VMware Cloud Community
vin01
Expert
Expert
Jump to solution

update vm information in csv only which changed in vcenter

I can able to get the complete information for every vms using the below script but its very tough to get csv exported for every week on large set of vms. so is it possible to append the existing csv with only changed vm information.

Something like this.

The script will be executed in scheduled task weekly once.

1.At first instance below script will execute on all the vms at get the information updated in .csv

2.When next week the scheduled task started it will check existing .csv and import all the information in a variable($importedvmsinfo)

3.Next script will Get vCenter Events and match if any change done for the vms which are in $importedvmsinfo variable.

4.then remove the row which is related to the vm in existing .csv and update the new information.

Ex: At step 1 when one vm is powered on state and which is exported to .csv then when executed the script in next week at this point if the vms is powered off then update the latest information on the .csv by deleting the complete row related to that vm and create new row with latest information. (Same applies if vm memory updated,changes in hard disk or vm deleted).

If nothing changed to the vm since 1 week then leave that row as it is in original csv file.

foreach($vm in (Get-View -ViewType VirtualMachine -Property Name,runtime.powerState,Guest.net,Config.Hardware.numCPU,Config.Hardware.MemoryMB,Runtime.Host,Guest.GuestFullName,

Config.GuestFullName,Parent,ResourcePool,Config.Hardware.Device,Config.version,guest.toolsversionstatus,

Config.Files.VMPathName)){

    $t = Get-View $vm.ResourcePool -Property Name,Parent

    while($t.getType().Name -eq "ResourcePool"){

       $t = Get-View $t.Parent -Property Name,Parent

    }

        if($t.GetType().Name -eq "ClusterComputeResource"){

        $cluster = $t.Name

        }

        else{

            $cluster = "Stand Alone Host"

        }

    while($t.getType().Name -ne "Datacenter"){

        $t = Get-View $t.Parent -Property Name,Parent

    }

    $datacenter = $t.Name

 

    $vm.Config.Hardware.Device | where {$_.GetType().Name -eq "VirtualDisk"} |

    Select @{N="vCenter";E={$script:vmhost = Get-View -Id $vm.Runtime.Host; $script:vmhost.Client.ServiceUrl.Split('/')[2]}},

    @{N="VM";E={$vm.Name}},

    @{N='powerState';E={$vm.runtime.powerState}},

    @{N='IP';E={[string]::Join(',',($vm.Guest.Net | %{$_.IpAddress | where{$_.Split('.').Count -eq 4} | %{$_}}))}},

    @{N='NumCPU';E={$vm.config.Hardware.NumCpu}},

    @{N='Memory GB';E={$vm.Config.Hardware.MemoryMB| %{[math]::Round($_/1kb,2)}}},

    @{N='VMHost';E={$script:esx = Get-View -Id $vm.Runtime.Host; $script:esx.name}},

    @{N='GuestOS';E={$vm.Guest.GuestFullName}},

    @{N='ConfiguredOS';E={$vm.Config.GuestFullName}},

    #@{N="Folder";E={$path}},

    @{N="Cluster";E={$cluster}},

    @{N="Datacenter";E={$datacenter}},

    @{N="Scsi";E={$_.UnitNumber}},

    @{N="Hard Disk";E={$_.DeviceInfo.Label}},

    @{N="Disk datastore";E={$_.Backing.Filename.Split(']')[0].TrimStart('[')}},

    @{N="Disk capacity GB";E={$_.CapacityInKB| %{[math]::Round($_/1MB,2)}}},

    @{N="Disk type";E={

            if($_.Backing.GetType().Name -match "flat"){

                "Flat"

            }

            else{

                $_.Backing.CompatibilityMode

            }}},

   @{N='DeviceName';E={

    if($_.Backing.GetType().Name -match 'raw'){

      $_.Backing.DeviceName

    }

    else{

      $script:lunnaa = (Get-View -Id $_.Backing.Datastore).Info.Vmfs.Extent[0].DiskName

      $script:lun = $script:esx.Config.StorageDevice.ScsiLun | where{$_.CanonicalName -eq $script:lunnaa}

      $script:lun.Descriptor | where{$_.Id -match 'vml.'} | Select -ExpandProperty Id

    }}},

   @{N='LUN NAA';E={

    if($_.Backing.GetType().Name -match 'raw'){

      $lunUuid = $_.Backing.LunUuid

      $script:lun = $script:esx.Config.StorageDevice.ScsiLun | where{$_.Uuid -eq $lunUuid}

      $script:lun.CanonicalName

    }

    else{

      $script:lunnaa

    }}},

   @{N='LUN ID';E={

      $dev = $script:esx.Config.StorageDevice.PlugStoreTopology.Device | where {$_.Lun -eq $script:lun.Key}

      $script:esx.Config.StorageDevice.PlugStoreTopology.Path | where {$_.Device -eq $dev.Key} |

      Select -First 1 -ExpandProperty LunNumber

    }},

   @{N='VMConfigFile';E={$VM.config.files.VMpathname}},

   @{N='VMDKPath';E={$_.Backing.FileName}},

   @{N="HW Version";E={$vm.Config.version}},

   @{N="Tools Status";E={$vm.guest.toolsversionstatus}},

   @{N="NIC Name";E={($vm.config.hardware.device | where {($_.DeviceInfo.Label -like "Network*")}).DeviceInfo.Label}},

   @{N="Mac"; E={($vm.Config.Hardware.Device | where{$_.DeviceInfo.Label -like "Network*"}).MacAddress}},

   @{N="Portgroup"; E={

     $nic = $vm.Config.Hardware.Device | where{$_.DeviceInfo.Label -like "Network*"}

     [string]::Join(',',(

       $nic | %{

       if($_.DeviceInfo.Summary -notmatch 'DVSwitch'){

         $_.DeviceInfo.Summary

       }

       else{

         Get-View -ViewType DistributedVirtualPortgroup -Property Name -Filter @{'Key'=$_.Backing.Port.PortgroupKey} |

         Select -ExpandProperty Name

       }}))}}

}

Regards Vineeth.K
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

I did some further tests, and it looks like the script was not capturing all types of power off events.
I also added sort on VM name and Hard Disk name.

Is that the order you mean?

function Get-VMInfo {

    param(

        [string]$Name

    )


    $vm = Get-View -ViewType VirtualMachine -Property Name, runtime.powerState, Guest.net, Config.Hardware.numCPU, Config.Hardware.MemoryMB, Runtime.Host, Guest.GuestFullName,

    Config.GuestFullName, Parent, ResourcePool, Config.Hardware.Device, Config.version, guest.toolsversionstatus,

    Config.Files.VMPathName -filter @{'Name' = "^$Name$" }

    if ($vm) {

        $t = Get-View $vm.ResourcePool -Property Name, Parent

        while ($t.getType().Name -eq "ResourcePool") {

            $t = Get-View $t.Parent -Property Name, Parent

        }

        if ($t.GetType().Name -eq "ClusterComputeResource") {

            $cluster = $t.Name

        } else {

            $cluster = "Stand Alone Host"

        }

        while ($t.getType().Name -ne "Datacenter") {

            $t = Get-View $t.Parent -Property Name, Parent

        }

        $datacenter = $t.Name

  

        $vm.Config.Hardware.Device | Where-Object { $_.GetType().Name -eq "VirtualDisk" } |

        Select-Object @{N = "vCenter"; E = { $script:vmhost = Get-View -Id $vm.Runtime.Host; $script:vmhost.Client.ServiceUrl.Split('/')[2] } },

        @{N = "VM"; E = { $vm.Name } },

        @{N = 'powerState'; E = { $vm.runtime.powerState } },

        @{N = 'IP'; E = { [string]::Join(',', ($vm.Guest.Net | ForEach-Object { $_.IpAddress | Where-Object { $_.Split('.').Count -eq 4 } | ForEach-Object { $_ } })) } },

@{N = 'NumCPU'; E = { $vm.config.Hardware.NumCpu } },

@{N = 'Memory GB'; E = { $vm.Config.Hardware.MemoryMB | ForEach-Object { [math]::Round($_/1kb, 2) } } },

@{N = 'VMHost'; E = { $script:esx = Get-View -Id $vm.Runtime.Host; $script:esx.name } },

@{N = 'GuestOS'; E = { $vm.Guest.GuestFullName } },

@{N = 'ConfiguredOS'; E = { $vm.Config.GuestFullName } },

#@{N="Folder";E={$path}}, 

@{N = "Cluster"; E = { $cluster } },

@{N = "Datacenter"; E = { $datacenter } },

@{N = "Scsi"; E = { $_.UnitNumber } },

@{N = "Hard Disk"; E = { $_.DeviceInfo.Label } },

@{N = "Disk datastore"; E = { $_.Backing.Filename.Split(']')[0].TrimStart('[') } },

@{N = "Disk capacity GB"; E = { $_.CapacityInKB | ForEach-Object { [math]::Round($_/1MB, 2) } } },

@{N = "Disk type"; E = {

        if ($_.Backing.GetType().Name -match "flat") {

            "Flat"

        } else {

            $_.Backing.CompatibilityMode

        } }

},

@{N = 'DeviceName'; E = {

        if ($_.Backing.GetType().Name -match 'raw') {

            ($_.Backing.DeviceName) -join '|'

        } else {

            $script:lunnaa = (Get-View -Id $_.Backing.Datastore).Info.Vmfs.Extent[0].DiskName

            $script:lun = $script:esx.Config.StorageDevice.ScsiLun | Where-Object { $_.CanonicalName -eq $script:lunnaa }

        $script:lun.Descriptor | Where-Object { $_.Id -match 'vml.' } | Select-Object -ExpandProperty Id

} }

},

@{N = 'LUN NAA'; E = {

        if ($_.Backing.GetType().Name -match 'raw') {

            $lunUuid = $_.Backing.LunUuid

            $script:lun = $script:esx.Config.StorageDevice.ScsiLun | Where-Object { $_.Uuid -eq $lunUuid }

        $script:lun.CanonicalName

    } else {

        $script:lunnaa

    } }

},

@{N = 'LUN ID'; E = {

        $dev = $script:esx.Config.StorageDevice.PlugStoreTopology.Device | Where-Object { $_.Lun -eq $script:lun.Key }

    $script:esx.Config.StorageDevice.PlugStoreTopology.Path | Where-Object { $_.Device -eq $dev.Key } |

    Select-Object -First 1 -ExpandProperty LunNumber

}

},

@{N = 'VMConfigFile'; E = { $VM.config.files.VMpathname } },

@{N = 'VMDKPath'; E = { $_.Backing.FileName } },

@{N = "HW Version"; E = { $vm.Config.version } },

@{N = "Tools Status"; E = { $vm.guest.toolsversionstatus } },

@{N = "NIC Name"; E = { ($vm.config.hardware.device | Where-Object { ($_.DeviceInfo.Label -like "Network*") }).DeviceInfo.Label -join '|' } },

@{N = "Mac"; E = { ($vm.Config.Hardware.Device | Where-Object { $_.DeviceInfo.Label -like "Network*" }).MacAddress -join '|' } },

@{N = "Portgroup"; E = {

        $nic = $vm.Config.Hardware.Device | Where-Object { $_.DeviceInfo.Label -like "Network*" }

    [string]::Join(',', (

            $nic | ForEach-Object {

                if ($_.DeviceInfo.Summary -notmatch 'DVSwitch') {

                    $_.DeviceInfo.Summary

                } else {

                    Get-View -ViewType DistributedVirtualPortgroup -Property Name -Filter @{'Key' = $_.Backing.Port.PortgroupKey } |

                        Select-Object -ExpandProperty Name

                } })) }

}

}

}


$fileName = '.\VMreport.csv'


$configFile = $fileName.Replace('.csv', '.cfg')

$eventTypes = 'VmReconfiguredEvent', 'VmRemovedEvent', 'VmDeployedEvent', 'VmCreatedEvent', 'VmPoweredOffEvent',

'VmPoweredOnEvent', 'DrsVmPoweredOnEvent'

$start = (Get-Date).AddDays(-1)


$hash = @{ }

$now = Get-Date


if (Test-Path -Path $fileName) {

    Import-Csv -Path .\VMreport.csv -UseCulture |

        ForEach-Object -Process {

            $hash.Add("$($_.VM)$($_.'Hard Disk')", $_)

        }

    $start = [DateTime](Get-Content -Path $configFile)

    Get-VIEvent -Start $start -MaxSamples ([int]::MaxValue) |

        Where-Object { $eventTypes -contains ($_.GetType()).FullName.Split('.')[2] -or $eventTypes -contains ($_.GetType()).BaseType.Name } |

        Group-Object -Property { $_.VM.Name } |

        ForEach-Object -Process {

            Write-Host "Looking at $($_.Name)"

            $_.Group | Sort-Object -Property CreatedTime |

            ForEach-Object -Process {

                $event = $_

                switch ($event.GetType().FullName.Split('.')[2]) {

                    'VmRemovedEvent' {

                        $rows = $hash.GetEnumerator() | Where-Object { $_.Key -match "^$($event.VM.Name)" }

                    $rows | ForEach-Object -Process {

                        $hash.Remove("$($_.Value.VM)$($_.Value.'Hard Disk')")

                    }

            }

            { 'VmDeployedEvent', 'VmCreatedEvent' -contains $_ } {

                Get-VMInfo -Name $event.VM.Name |

                    ForEach-Object -Process {

                        $hash.Add("$($_.VM)$($_.'Hard Disk')", $_)

                    }

            }

            'VmReconfiguredEvent' {

                Get-VMInfo -Name $event.VM.Name |

                    ForEach-Object -Process {

                        $hash.Item("$($_.VM)$($_.'Hard Disk')") = $_

                    }

            }

            { 'VmPoweredOnEvent', 'DrsVmPoweredOnEvent' -contains $_ } {

                $rows = $hash.GetEnumerator() | Where-Object { $_.Key -match "^$($event.VM.Name)" }

            $rows | ForEach-Object -Process {

                $_.Value.PowerState = 'poweredOn'

                $hash.Item($_.Key) = $_.Value

            }

    }

    'VmPoweredOffEvent' {

        $rows = $hash.GetEnumerator() | Where-Object { $_.Key -match "^$($event.VM.Name)" }

    $rows | ForEach-Object -Process {

        $_.Value.PowerState = 'poweredOff'

        $hash.Item($_.Key) = $_.Value

    }

}

}

}

}

} else {

    Get-View -ViewType Virtualmachine -Property Name -Filter @{'Config.Template' = 'False' } |

        ForEach-Object -Process {

            Write-Host "Looking at $($_.Name)"

            Get-VMInfo -Name $_.Name |

                ForEach-Object -Process {

                    $hash.Add("$($_.VM)$($_.'Hard Disk')", $_)

                }

        }

}


$hash.Values |

    Sort-Object -Property VM, 'Hard Disk' |

    Export-Csv -Path $fileName -NoTypeInformation -UseCulture

$now.ToString() | Out-File -FilePath $configFile


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

Reply
0 Kudos
18 Replies
LucD
Leadership
Leadership
Jump to solution

Perhaps I'm misunderstanding your intention here, but isn't this just the same as overwriting the same CSV on each run?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
vin01
Expert
Expert
Jump to solution

Yes LucD I’m looking to overwrite the existing csv by replacing the older information in the csv. If excel works better then csv then I’ll use excel itself.

If overwrite is not Possible then import the data from the existing csv and look for changes in the vms and create a new csv with latest date.

if nothing is changed for the vms from past 1 week then write the same imported data into new csv or just rename the existing csv with new date.

Regards Vineeth.K
Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Perhaps I didn't explain my question correctly, but if this is a fixed environment (same VMs all the time), you can just completely overwrite the CSV file each run.

If VMs are added and removed, and you want to keep the removed VMs in the CSV, then this would not work with just overwriting the CSV file.

Assume the first CSV contains

VM,CPU

vm1,2

vm2,4

Then next week the CPUs on VM2 are changed, so you just overwrite the file

VM,CPU

vm1,2

vm2,2

But if VMs are added and removed, and you want to keep removed VMs in the CSV, another approach is required.

VM,CPU

vm1,2
vm2,2

Now VM1 is removed and a new VM3 is introduced.
Should the CSV be

VM,CPU

vm2,2

vm3,4

or

VM,CPU

vm1,2

vm2,2

vm3,4


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
vin01
Expert
Expert
Jump to solution

Oh I got your question now.

In my original question the point I missed to explain is what if new vm is created or existing deleted.

I’m expecting any way as below.

VM,CPU,comment

vm1,2,vm is removed(or no data available)

vm2,2

vm3,4,new vm added on this date


If above doesn’t makes sense then below also works for me.

At first week execution sheet is like this

VM,CPU

vm1,2

vm2,4


second week execution create a new csv with latest date and write as below.Here it should get only the information for vm3 from vcenter as this is newly created and vc events shows this newly created event.

VM,CPU

vm1,2

vm2,2

vm3,4

Third week execution create a new csv with latest date and write as below.Here it should get only the information for vm1 from vcenter as there is change in cpu and vc events shows this.

VM,CPU

vm1,4

vm2,2

vm3,4


if the vc events doesn’t shows what has changed exactly but only the event says reconfigure is done then the script should get the complete information of that vm.

Basically what I’m looking is the script should avoid checking each and every vms information every week. Only changes should be updated. it will reduce the execution time when running on larger environment.

Hope my explanation understands the whole question.

Regards Vineeth.K
Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Ok, just to make sure I understand what you are saying.

The script shall be based on events from the last week.

The events that need to be looked at are 'a VM is created', 'a VM is removed' and 'a VM is reconfigured'.

Only for those VMs the CSV needs to be updated.


Which means:

- add VMs that were added over the last week

- remove VMs that are deleted over the last week

- change the entries for VMs that were reconfigured over the last week

Correct?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
vin01
Expert
Expert
Jump to solution

Yes you are right. I believe the expected output will contains the complete information of the vm right?

Like name,ip,powerstate etc.. which are there in the original script.

Regards Vineeth.K
Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

The script is a bit more complex than I considered originally.

And that is because you can have more than 1 entry per VM, since you make a row per VM/HardDisk.

The first time the script runs, when there is no CSV file, it will collect all data.

Subsequent runs will update the CSV depending on the events that were detected.

Note that you have to look at events since exactly the time the script ran the last time.

Otherwise the CSV might become incorrect.

function Get-VMInfo {

    param(

        [string]$Name

    )


    $vm = Get-View -ViewType VirtualMachine -Property Name, runtime.powerState, Guest.net, Config.Hardware.numCPU, Config.Hardware.MemoryMB, Runtime.Host, Guest.GuestFullName,

    Config.GuestFullName, Parent, ResourcePool, Config.Hardware.Device, Config.version, guest.toolsversionstatus,

    Config.Files.VMPathName -filter @{'Name' = "^$Name$" }

    if ($vm) {

        $t = Get-View $vm.ResourcePool -Property Name, Parent

        while ($t.getType().Name -eq "ResourcePool") {

            $t = Get-View $t.Parent -Property Name, Parent

        }

        if ($t.GetType().Name -eq "ClusterComputeResource") {

            $cluster = $t.Name

        } else {

            $cluster = "Stand Alone Host"

        }

        while ($t.getType().Name -ne "Datacenter") {

            $t = Get-View $t.Parent -Property Name, Parent

        }

        $datacenter = $t.Name

  

        $vm.Config.Hardware.Device | Where-Object { $_.GetType().Name -eq "VirtualDisk" } |

        Select-Object @{N = "vCenter"; E = { $script:vmhost = Get-View -Id $vm.Runtime.Host; $script:vmhost.Client.ServiceUrl.Split('/')[2] } },

        @{N = "VM"; E = { $vm.Name } },

        @{N = 'powerState'; E = { $vm.runtime.powerState } },

        @{N = 'IP'; E = { [string]::Join(',', ($vm.Guest.Net | ForEach-Object { $_.IpAddress | Where-Object { $_.Split('.').Count -eq 4 } | ForEach-Object { $_ } })) } },

@{N = 'NumCPU'; E = { $vm.config.Hardware.NumCpu } },

@{N = 'Memory GB'; E = { $vm.Config.Hardware.MemoryMB | ForEach-Object { [math]::Round($_/1kb, 2) } } },

@{N = 'VMHost'; E = { $script:esx = Get-View -Id $vm.Runtime.Host; $script:esx.name } },

@{N = 'GuestOS'; E = { $vm.Guest.GuestFullName } },

@{N = 'ConfiguredOS'; E = { $vm.Config.GuestFullName } },

#@{N="Folder";E={$path}}, 

@{N = "Cluster"; E = { $cluster } },

@{N = "Datacenter"; E = { $datacenter } },

@{N = "Scsi"; E = { $_.UnitNumber } },

@{N = "Hard Disk"; E = { $_.DeviceInfo.Label } },

@{N = "Disk datastore"; E = { $_.Backing.Filename.Split(']')[0].TrimStart('[') } },

@{N = "Disk capacity GB"; E = { $_.CapacityInKB | ForEach-Object { [math]::Round($_/1MB, 2) } } },

@{N = "Disk type"; E = {

        if ($_.Backing.GetType().Name -match "flat") {

            "Flat"

        } else {

            $_.Backing.CompatibilityMode

        } }

},

@{N = 'DeviceName'; E = {

        if ($_.Backing.GetType().Name -match 'raw') {

            ($_.Backing.DeviceName) -join '|'

        } else {

            $script:lunnaa = (Get-View -Id $_.Backing.Datastore).Info.Vmfs.Extent[0].DiskName

            $script:lun = $script:esx.Config.StorageDevice.ScsiLun | Where-Object { $_.CanonicalName -eq $script:lunnaa }

        $script:lun.Descriptor | Where-Object { $_.Id -match 'vml.' } | Select-Object -ExpandProperty Id

} }

},

@{N = 'LUN NAA'; E = {

        if ($_.Backing.GetType().Name -match 'raw') {

            $lunUuid = $_.Backing.LunUuid

            $script:lun = $script:esx.Config.StorageDevice.ScsiLun | Where-Object { $_.Uuid -eq $lunUuid }

        $script:lun.CanonicalName

    } else {

        $script:lunnaa

    } }

},

@{N = 'LUN ID'; E = {

        $dev = $script:esx.Config.StorageDevice.PlugStoreTopology.Device | Where-Object { $_.Lun -eq $script:lun.Key }

    $script:esx.Config.StorageDevice.PlugStoreTopology.Path | Where-Object { $_.Device -eq $dev.Key } |

    Select-Object -First 1 -ExpandProperty LunNumber

}

},

@{N = 'VMConfigFile'; E = { $VM.config.files.VMpathname } },

@{N = 'VMDKPath'; E = { $_.Backing.FileName } },

@{N = "HW Version"; E = { $vm.Config.version } },

@{N = "Tools Status"; E = { $vm.guest.toolsversionstatus } },

@{N = "NIC Name"; E = { ($vm.config.hardware.device | Where-Object { ($_.DeviceInfo.Label -like "Network*") }).DeviceInfo.Label } },

@{N = "Mac"; E = { ($vm.Config.Hardware.Device | Where-Object { $_.DeviceInfo.Label -like "Network*" }).MacAddress } },

@{N = "Portgroup"; E = {

        $nic = $vm.Config.Hardware.Device | Where-Object { $_.DeviceInfo.Label -like "Network*" }

    [string]::Join(',', (

            $nic | ForEach-Object {

                if ($_.DeviceInfo.Summary -notmatch 'DVSwitch') {

                    $_.DeviceInfo.Summary

                } else {

                    Get-View -ViewType DistributedVirtualPortgroup -Property Name -Filter @{'Key' = $_.Backing.Port.PortgroupKey } |

                        Select-Object -ExpandProperty Name

                } })) }

}

}

}


$fileName = '.\VMreport.csv'


$eventTypes = 'VmReconfiguredEvent', 'VmRemovedEvent', 'VmDeployedEvent'

$start = (Get-Date).AddDays(-1)


$hash = @{ }


if (Test-Path -Path $fileName) {

    Import-Csv -Path .\VMreport.csv -UseCulture |

        ForEach-Object -Process {

            $hash.Add("$($_.VM)$($_.'Hard Disk')", $_)

        }


    Get-VIEvent -Start $start -MaxSamples ([int]::MaxValue) |

        Where-Object { $eventTypes -contains ($_.GetType()).FullName.Split('.')[2] } |

        Group-Object -Property { $_.VM.Name } |

        ForEach-Object -Process {

            Write-Host "Looking at $($_.Name)"

            $_.Group | Sort-Object -Property CreatedTime |

            ForEach-Object -Process {

                $event = $_

                switch ($event.GetType().FullName.Split('.')[2]) {

                    'VmRemovedEvent' {

                        $hash.GetEnumerator() | Where-Object { $_.Key -match "^$($event.VM.Name)" } |

                        ForEach-Object -Process {

                            $hash.Remove("$($_.VM)$($_.'Hard Disk')")

                        }

                }

                'VmDeployedEvent' {

                    Get-VMInfo -Name $event.VM.Name |

                        ForEach-Object -Process {

                            $hash.Add("$($_.VM)$($_.'Hard Disk')", $_)

                        }

                }

                'VmReconfiguredEvent' {

                    Get-VMInfo -Name $event.VM.Name |

                        ForEach-Object -Process {

                            $hash.Item("$($_.VM)$($_.'Hard Disk')") = $_

                        }

                }

            }

        }

}

} else {

    Get-View -ViewType Virtualmachine -Property Name -Filter @{'Config.Template' = 'False' } |

        ForEach-Object -Process {

            Write-Host "Looking at $($_.Name)"

            Get-VMInfo -Name $_.Name |

                ForEach-Object -Process {

                    $hash.Add("$($_.VM)$($_.'Hard Disk')", $_)

                }

        }

}


$hash.Values | Export-Csv -Path $fileName -NoTypeInformation -UseCulture


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

vin01
Expert
Expert
Jump to solution

I just tested in my environment with small set of vms in a vcenter. At first execution the vms are in powered on state and I got the report in csv then I powered off the vms and done changes like adding harddisk and changing cpu & memory for few vms.

I have changed $start = (Get-Date).AddMinutes(-3) in the script and retried after 15 mins but the changes are not recorded in the csv.

Should I need to change any other parameter to do a quick check?

Regards Vineeth.K
Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

The changes are based on the events that are listed in $eventTypes, other changes, like power off, will not be captured.

And to be honest, that is not what I understood from your original question.

You have to capture all events that happened after the 1st run of the script.

So you $start datetime should in fact be the time your first run finished.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
vin01
Expert
Expert
Jump to solution

The changes are based on the events that are listed in $eventTypes, other changes, like power off, will not be captured.

And to be honest, that is not what I understood from your original question.

sorry it was miss from my side.

i thought VmReconfiguredEvent will contains poweroff,addition of hard disk and any change in network port.

Can we add events like poweroff addition of hard disk and any change in network portgroup.

You have to capture all events that happened after the 1st run of the script.

So you $start datetime should in fact be the time your first run finished.

Understood. will it fix if we do something like this

At the first run of the script if we capture date and time in the csv and in second run we can check the events which are generated between the current time and time which is in csv file. And while updating the vms change results we can also update the new time stamp in csv file.

Regards Vineeth.K
Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

The power off/power on will not be captured by a VmReconfiguredEvent.

These actions have other events: VmPoweredOff and VmPoweredOn.

Adding a harddisk or changing a vNIC config should result in a VmReconfiguredEvent, and thus should be captured.

A CSV is a rather flat structure and it can only have 1 type of row in there.

That would mean the datetime would appear in every row in the CSV, which is a lot of redundant data.

You could consider kind of config file next to the CSV.

There the script could write the datatime of the last run.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

vin01
Expert
Expert
Jump to solution

You could consider kind of config file next to the CSV.

There the script could write the datatime of the last run.

config file means a .txt file which contains date and time which will be in the same location where csv file exist? If that the thing expected if so I will then maintain it.

can you pls update the code With the creation of config txt file and the VmPoweredOff and VmPoweredOn Events.

Regards Vineeth.K
Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Ok, this version includes power on/power off events.

The file containing the date of the last run has the same name as the CSV file, except the type is .cfg.

function Get-VMInfo {

    param(

        [string]$Name

    )


    $vm = Get-View -ViewType VirtualMachine -Property Name, runtime.powerState, Guest.net, Config.Hardware.numCPU, Config.Hardware.MemoryMB, Runtime.Host, Guest.GuestFullName,

    Config.GuestFullName, Parent, ResourcePool, Config.Hardware.Device, Config.version, guest.toolsversionstatus,

    Config.Files.VMPathName -filter @{'Name' = "^$Name$" }

    if ($vm) {

        $t = Get-View $vm.ResourcePool -Property Name, Parent

        while ($t.getType().Name -eq "ResourcePool") {

            $t = Get-View $t.Parent -Property Name, Parent

        }

        if ($t.GetType().Name -eq "ClusterComputeResource") {

            $cluster = $t.Name

        } else {

            $cluster = "Stand Alone Host"

        }

        while ($t.getType().Name -ne "Datacenter") {

            $t = Get-View $t.Parent -Property Name, Parent

        }

        $datacenter = $t.Name

  

        $vm.Config.Hardware.Device | Where-Object { $_.GetType().Name -eq "VirtualDisk" } |

        Select-Object @{N = "vCenter"; E = { $script:vmhost = Get-View -Id $vm.Runtime.Host; $script:vmhost.Client.ServiceUrl.Split('/')[2] } },

        @{N = "VM"; E = { $vm.Name } },

        @{N = 'powerState'; E = { $vm.runtime.powerState } },

        @{N = 'IP'; E = { [string]::Join(',', ($vm.Guest.Net | ForEach-Object { $_.IpAddress | Where-Object { $_.Split('.').Count -eq 4 } | ForEach-Object { $_ } })) } },

@{N = 'NumCPU'; E = { $vm.config.Hardware.NumCpu } },

@{N = 'Memory GB'; E = { $vm.Config.Hardware.MemoryMB | ForEach-Object { [math]::Round($_/1kb, 2) } } },

@{N = 'VMHost'; E = { $script:esx = Get-View -Id $vm.Runtime.Host; $script:esx.name } },

@{N = 'GuestOS'; E = { $vm.Guest.GuestFullName } },

@{N = 'ConfiguredOS'; E = { $vm.Config.GuestFullName } },

#@{N="Folder";E={$path}}, 

@{N = "Cluster"; E = { $cluster } },

@{N = "Datacenter"; E = { $datacenter } },

@{N = "Scsi"; E = { $_.UnitNumber } },

@{N = "Hard Disk"; E = { $_.DeviceInfo.Label } },

@{N = "Disk datastore"; E = { $_.Backing.Filename.Split(']')[0].TrimStart('[') } },

@{N = "Disk capacity GB"; E = { $_.CapacityInKB | ForEach-Object { [math]::Round($_/1MB, 2) } } },

@{N = "Disk type"; E = {

        if ($_.Backing.GetType().Name -match "flat") {

            "Flat"

        } else {

            $_.Backing.CompatibilityMode

        } }

},

@{N = 'DeviceName'; E = {

        if ($_.Backing.GetType().Name -match 'raw') {

            ($_.Backing.DeviceName) -join '|'

        } else {

            $script:lunnaa = (Get-View -Id $_.Backing.Datastore).Info.Vmfs.Extent[0].DiskName

            $script:lun = $script:esx.Config.StorageDevice.ScsiLun | Where-Object { $_.CanonicalName -eq $script:lunnaa }

        $script:lun.Descriptor | Where-Object { $_.Id -match 'vml.' } | Select-Object -ExpandProperty Id

} }

},

@{N = 'LUN NAA'; E = {

        if ($_.Backing.GetType().Name -match 'raw') {

            $lunUuid = $_.Backing.LunUuid

            $script:lun = $script:esx.Config.StorageDevice.ScsiLun | Where-Object { $_.Uuid -eq $lunUuid }

        $script:lun.CanonicalName

    } else {

        $script:lunnaa

    } }

},

@{N = 'LUN ID'; E = {

        $dev = $script:esx.Config.StorageDevice.PlugStoreTopology.Device | Where-Object { $_.Lun -eq $script:lun.Key }

    $script:esx.Config.StorageDevice.PlugStoreTopology.Path | Where-Object { $_.Device -eq $dev.Key } |

    Select-Object -First 1 -ExpandProperty LunNumber

}

},

@{N = 'VMConfigFile'; E = { $VM.config.files.VMpathname } },

@{N = 'VMDKPath'; E = { $_.Backing.FileName } },

@{N = "HW Version"; E = { $vm.Config.version } },

@{N = "Tools Status"; E = { $vm.guest.toolsversionstatus } },

@{N = "NIC Name"; E = { ($vm.config.hardware.device | Where-Object { ($_.DeviceInfo.Label -like "Network*") }).DeviceInfo.Label -join '|' } },

@{N = "Mac"; E = { ($vm.Config.Hardware.Device | Where-Object { $_.DeviceInfo.Label -like "Network*" }).MacAddress -join '|' } },

@{N = "Portgroup"; E = {

        $nic = $vm.Config.Hardware.Device | Where-Object { $_.DeviceInfo.Label -like "Network*" }

    [string]::Join(',', (

            $nic | ForEach-Object {

                if ($_.DeviceInfo.Summary -notmatch 'DVSwitch') {

                    $_.DeviceInfo.Summary

                } else {

                    Get-View -ViewType DistributedVirtualPortgroup -Property Name -Filter @{'Key' = $_.Backing.Port.PortgroupKey } |

                        Select-Object -ExpandProperty Name

                } })) }

}

}

}


$fileName = '.\VMreport.csv'


$configFile = $fileName.Replace('.csv', '.cfg')

$eventTypes = 'VmReconfiguredEvent', 'VmRemovedEvent', 'VmDeployedEvent', 'VmPoweredOffEvent', 'VmPoweredOnEvent'

$start = (Get-Date).AddDays(-1)


$hash = @{ }

$now = Get-Date


if (Test-Path -Path $fileName) {

    Import-Csv -Path .\VMreport.csv -UseCulture |

        ForEach-Object -Process {

            $hash.Add("$($_.VM)$($_.'Hard Disk')", $_)

        }

    $start = [DateTime](Get-Content -Path $configFile)

    Get-VIEvent -Start $start -MaxSamples ([int]::MaxValue) |

        Where-Object { $eventTypes -contains ($_.GetType()).FullName.Split('.')[2] -or $eventTypes -contains ($_.GetType()).BaseType.Name } |

        Group-Object -Property { $_.VM.Name } |

        ForEach-Object -Process {

            Write-Host "Looking at $($_.Name)"

            $_.Group | Sort-Object -Property CreatedTime |

            ForEach-Object -Process {

                $event = $_

                switch ($event.GetType().FullName.Split('.')[2]) {

                    'VmRemovedEvent' {

                        $hash.GetEnumerator() | Where-Object { $_.Key -match "^$($event.VM.Name)" } |

                        ForEach-Object -Process {

                            $hash.Remove("$($_.VM)$($_.'Hard Disk')")

                        }

                }

                'VmDeployedEvent' {

                    Get-VMInfo -Name $event.VM.Name |

                        ForEach-Object -Process {

                            $hash.Add("$($_.VM)$($_.'Hard Disk')", $_)

                        }

                }

                'VmReconfiguredEvent' {

                    Get-VMInfo -Name $event.VM.Name |

                        ForEach-Object -Process {

                            $hash.Item("$($_.VM)$($_.'Hard Disk')") = $_

                        }

                }

                'VmPoweredOnEvent' {

                    $rows = $hash.GetEnumerator() | Where-Object { $_.Key -match "^$($event.VM.Name)" }

                $rows | ForEach-Object -Process {

                    $_.Value.PowerState = 'poweredOn'

                    $hash.Item($_.Key) = $_.Value

                }

        }

        'VmPoweredOffEvent' {

            $rows = $hash.GetEnumerator() | Where-Object { $_.Key -match "^$($event.VM.Name)" }

        $rows | ForEach-Object -Process {

            $_.Value.PowerState = 'poweredOff'

            $hash.Item($_.Key) = $_.Value

        }

}

}

}

}

} else {

    Get-View -ViewType Virtualmachine -Property Name -Filter @{'Config.Template' = 'False' } |

        ForEach-Object -Process {

            Write-Host "Looking at $($_.Name)"

            Get-VMInfo -Name $_.Name |

                ForEach-Object -Process {

                    $hash.Add("$($_.VM)$($_.'Hard Disk')", $_)

                }

        }

}


$hash.Values | Export-Csv -Path $fileName -NoTypeInformation -UseCulture

$now.ToString() | Out-File -FilePath $configFile


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

vin01
Expert
Expert
Jump to solution

I have tested the script in my environment by changing to $start = (Get-Date).AddMinutes(-1)

Test was done by executing once in hour by  powering off ,adding the disk and creating new vms

What I observed is re configuring task(like changing memory and cpu) and new harddisk creation is updating the .csv

when new harddisk is added to particular vm then the vm information is not in sequence in .csv. Can it be fixed to get in sequence?

Something like this:

Name Harddisk

VM01     harddisk1

VM02     harddisk1

VM01     harddisk2

Poweron/off and new vm creation tasks are not updating in the sheet.

Regards Vineeth.K
Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

What sequence do you mean?

Power off and on and new VM are included when I run the script in my test environment.

Do you see the corresponding events when you do a Get-VIEvent?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
vin01
Expert
Expert
Jump to solution

What I have did is before running the script logged into vc and  I have powered off all the vms and executed the script 1st time then a report is generated with all vms as below:

VM,CPU,Memory,powerstate,Harddisk

vm1,2,       4,    Poweredoff,       harddisk1

vm2,2,       2,    Poweredoff,       harddisk1

vm3,4,       8,    Poweredoff,       harddisk1

after 15 mins I logged into vcenter and added hard disk to VM1 and changed cpu and memory for vm1,vm2,vm3 and powered on all the vms.


waited for 1 hr and executed 2nd time. And here is the output

VM,CPU,Memory,powerstate,Harddisk

vm1,2,       4,    Poweredoff,       harddisk1

vm2,4,       6,    Poweredoff,       harddisk1

vm3,2,       4,    Poweredoff,       harddisk1

vm1,4,      12,   Poweredoff,       harddisk2


powerstate is not changed.

What sequence do you mean?

the VM1 output is not sequence in csv after 2nd

execution.

Do you see the corresponding events when you do a Get-VIEvent?

yes I can see the events in vc.


note: I have changed $start = (Get-Date).AddMinutes(-1) in the script.

Regards Vineeth.K
Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

I did some further tests, and it looks like the script was not capturing all types of power off events.
I also added sort on VM name and Hard Disk name.

Is that the order you mean?

function Get-VMInfo {

    param(

        [string]$Name

    )


    $vm = Get-View -ViewType VirtualMachine -Property Name, runtime.powerState, Guest.net, Config.Hardware.numCPU, Config.Hardware.MemoryMB, Runtime.Host, Guest.GuestFullName,

    Config.GuestFullName, Parent, ResourcePool, Config.Hardware.Device, Config.version, guest.toolsversionstatus,

    Config.Files.VMPathName -filter @{'Name' = "^$Name$" }

    if ($vm) {

        $t = Get-View $vm.ResourcePool -Property Name, Parent

        while ($t.getType().Name -eq "ResourcePool") {

            $t = Get-View $t.Parent -Property Name, Parent

        }

        if ($t.GetType().Name -eq "ClusterComputeResource") {

            $cluster = $t.Name

        } else {

            $cluster = "Stand Alone Host"

        }

        while ($t.getType().Name -ne "Datacenter") {

            $t = Get-View $t.Parent -Property Name, Parent

        }

        $datacenter = $t.Name

  

        $vm.Config.Hardware.Device | Where-Object { $_.GetType().Name -eq "VirtualDisk" } |

        Select-Object @{N = "vCenter"; E = { $script:vmhost = Get-View -Id $vm.Runtime.Host; $script:vmhost.Client.ServiceUrl.Split('/')[2] } },

        @{N = "VM"; E = { $vm.Name } },

        @{N = 'powerState'; E = { $vm.runtime.powerState } },

        @{N = 'IP'; E = { [string]::Join(',', ($vm.Guest.Net | ForEach-Object { $_.IpAddress | Where-Object { $_.Split('.').Count -eq 4 } | ForEach-Object { $_ } })) } },

@{N = 'NumCPU'; E = { $vm.config.Hardware.NumCpu } },

@{N = 'Memory GB'; E = { $vm.Config.Hardware.MemoryMB | ForEach-Object { [math]::Round($_/1kb, 2) } } },

@{N = 'VMHost'; E = { $script:esx = Get-View -Id $vm.Runtime.Host; $script:esx.name } },

@{N = 'GuestOS'; E = { $vm.Guest.GuestFullName } },

@{N = 'ConfiguredOS'; E = { $vm.Config.GuestFullName } },

#@{N="Folder";E={$path}}, 

@{N = "Cluster"; E = { $cluster } },

@{N = "Datacenter"; E = { $datacenter } },

@{N = "Scsi"; E = { $_.UnitNumber } },

@{N = "Hard Disk"; E = { $_.DeviceInfo.Label } },

@{N = "Disk datastore"; E = { $_.Backing.Filename.Split(']')[0].TrimStart('[') } },

@{N = "Disk capacity GB"; E = { $_.CapacityInKB | ForEach-Object { [math]::Round($_/1MB, 2) } } },

@{N = "Disk type"; E = {

        if ($_.Backing.GetType().Name -match "flat") {

            "Flat"

        } else {

            $_.Backing.CompatibilityMode

        } }

},

@{N = 'DeviceName'; E = {

        if ($_.Backing.GetType().Name -match 'raw') {

            ($_.Backing.DeviceName) -join '|'

        } else {

            $script:lunnaa = (Get-View -Id $_.Backing.Datastore).Info.Vmfs.Extent[0].DiskName

            $script:lun = $script:esx.Config.StorageDevice.ScsiLun | Where-Object { $_.CanonicalName -eq $script:lunnaa }

        $script:lun.Descriptor | Where-Object { $_.Id -match 'vml.' } | Select-Object -ExpandProperty Id

} }

},

@{N = 'LUN NAA'; E = {

        if ($_.Backing.GetType().Name -match 'raw') {

            $lunUuid = $_.Backing.LunUuid

            $script:lun = $script:esx.Config.StorageDevice.ScsiLun | Where-Object { $_.Uuid -eq $lunUuid }

        $script:lun.CanonicalName

    } else {

        $script:lunnaa

    } }

},

@{N = 'LUN ID'; E = {

        $dev = $script:esx.Config.StorageDevice.PlugStoreTopology.Device | Where-Object { $_.Lun -eq $script:lun.Key }

    $script:esx.Config.StorageDevice.PlugStoreTopology.Path | Where-Object { $_.Device -eq $dev.Key } |

    Select-Object -First 1 -ExpandProperty LunNumber

}

},

@{N = 'VMConfigFile'; E = { $VM.config.files.VMpathname } },

@{N = 'VMDKPath'; E = { $_.Backing.FileName } },

@{N = "HW Version"; E = { $vm.Config.version } },

@{N = "Tools Status"; E = { $vm.guest.toolsversionstatus } },

@{N = "NIC Name"; E = { ($vm.config.hardware.device | Where-Object { ($_.DeviceInfo.Label -like "Network*") }).DeviceInfo.Label -join '|' } },

@{N = "Mac"; E = { ($vm.Config.Hardware.Device | Where-Object { $_.DeviceInfo.Label -like "Network*" }).MacAddress -join '|' } },

@{N = "Portgroup"; E = {

        $nic = $vm.Config.Hardware.Device | Where-Object { $_.DeviceInfo.Label -like "Network*" }

    [string]::Join(',', (

            $nic | ForEach-Object {

                if ($_.DeviceInfo.Summary -notmatch 'DVSwitch') {

                    $_.DeviceInfo.Summary

                } else {

                    Get-View -ViewType DistributedVirtualPortgroup -Property Name -Filter @{'Key' = $_.Backing.Port.PortgroupKey } |

                        Select-Object -ExpandProperty Name

                } })) }

}

}

}


$fileName = '.\VMreport.csv'


$configFile = $fileName.Replace('.csv', '.cfg')

$eventTypes = 'VmReconfiguredEvent', 'VmRemovedEvent', 'VmDeployedEvent', 'VmCreatedEvent', 'VmPoweredOffEvent',

'VmPoweredOnEvent', 'DrsVmPoweredOnEvent'

$start = (Get-Date).AddDays(-1)


$hash = @{ }

$now = Get-Date


if (Test-Path -Path $fileName) {

    Import-Csv -Path .\VMreport.csv -UseCulture |

        ForEach-Object -Process {

            $hash.Add("$($_.VM)$($_.'Hard Disk')", $_)

        }

    $start = [DateTime](Get-Content -Path $configFile)

    Get-VIEvent -Start $start -MaxSamples ([int]::MaxValue) |

        Where-Object { $eventTypes -contains ($_.GetType()).FullName.Split('.')[2] -or $eventTypes -contains ($_.GetType()).BaseType.Name } |

        Group-Object -Property { $_.VM.Name } |

        ForEach-Object -Process {

            Write-Host "Looking at $($_.Name)"

            $_.Group | Sort-Object -Property CreatedTime |

            ForEach-Object -Process {

                $event = $_

                switch ($event.GetType().FullName.Split('.')[2]) {

                    'VmRemovedEvent' {

                        $rows = $hash.GetEnumerator() | Where-Object { $_.Key -match "^$($event.VM.Name)" }

                    $rows | ForEach-Object -Process {

                        $hash.Remove("$($_.Value.VM)$($_.Value.'Hard Disk')")

                    }

            }

            { 'VmDeployedEvent', 'VmCreatedEvent' -contains $_ } {

                Get-VMInfo -Name $event.VM.Name |

                    ForEach-Object -Process {

                        $hash.Add("$($_.VM)$($_.'Hard Disk')", $_)

                    }

            }

            'VmReconfiguredEvent' {

                Get-VMInfo -Name $event.VM.Name |

                    ForEach-Object -Process {

                        $hash.Item("$($_.VM)$($_.'Hard Disk')") = $_

                    }

            }

            { 'VmPoweredOnEvent', 'DrsVmPoweredOnEvent' -contains $_ } {

                $rows = $hash.GetEnumerator() | Where-Object { $_.Key -match "^$($event.VM.Name)" }

            $rows | ForEach-Object -Process {

                $_.Value.PowerState = 'poweredOn'

                $hash.Item($_.Key) = $_.Value

            }

    }

    'VmPoweredOffEvent' {

        $rows = $hash.GetEnumerator() | Where-Object { $_.Key -match "^$($event.VM.Name)" }

    $rows | ForEach-Object -Process {

        $_.Value.PowerState = 'poweredOff'

        $hash.Item($_.Key) = $_.Value

    }

}

}

}

}

} else {

    Get-View -ViewType Virtualmachine -Property Name -Filter @{'Config.Template' = 'False' } |

        ForEach-Object -Process {

            Write-Host "Looking at $($_.Name)"

            Get-VMInfo -Name $_.Name |

                ForEach-Object -Process {

                    $hash.Add("$($_.VM)$($_.'Hard Disk')", $_)

                }

        }

}


$hash.Values |

    Sort-Object -Property VM, 'Hard Disk' |

    Export-Csv -Path $fileName -NoTypeInformation -UseCulture

$now.ToString() | Out-File -FilePath $configFile


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
vin01
Expert
Expert
Jump to solution

Perfect I can get the report what I’m expected and also vms sorting with Harddisk Fixed the vms order in sequence. I will change back the (Get-Date).AddDays(-1) and schedule it on few vcenters and monitor for couple of days if any issues in output I’ll update you.

Thanks for your support Guru.

Regards Vineeth.K
Reply
0 Kudos