$VcHost = "10.20.30.40" $User = "administrator" $Password = "pass" $port = "443" $hostFile = "./Host.csv" $datastoreFile = "./Data_Store.csv" $lunFile = "./Lun.csv" $virtualDiskFile = "./Virtual_Disk.csv" $nasShareFile = "./Nas_Share.csv" $vmsFile = "./Virtual_Machine.csv" New-Item -Path $hostFile -type file -Force New-Item -Path $datastoreFile -type file -Force New-Item -Path $lunFile -type file -Force New-Item -Path $virtualDiskFile -type file -Force New-Item -Path $nasShareFile -type file -Force New-Item -Path $vmsFile -type file -Force # Ensure that dates are always returned in English [System.Threading.Thread]::CurrentThread.CurrentCulture="en-US" Function loadVMware { try { if (-not(Get-PSSnapin | ? { $_.name -eq 'VMware.VimAutomation.Core' })) { #$logg.Info("Trying to add PS Snapin for VMware") Add-PSSnapin VMware.VimAutomation.Core -ErrorAction Stop } } catch { #$logg.Error("FAILED to load PS Snapin for VMware") Throw "Unable to load VMware Poweshell Snapin" } } # Get the credentials for the VirtualCenter set in the Datasource Function getConnectionInfo { $connectionInfo = @{}; try { #$logg.Info("Get credentials for the VMware Host") $connectionInfo["host"] = Get-WfaRestParameter "host" $connectionInfo["port"] = Get-WfaRestParameter "port" $connectionInfo["credentials"] = Get-WfaCredentials } catch { #$logg.Error("Error getting data source credentials") throw "Error getting data source credentials. Please see the log file for more details" } #$logg.Info("Host credentials found successfully") return $connectionInfo } # This function recieve an array of VMView objects and add the relevant information # to the Virtual_Machine.csv file Function addVmEntriesToVmFile { param( [parameter(Mandatory=$true, HelpMessage="Virtual Machine views to add to vm CSV file")] [array]$VirtualMachineViews ) #$logg.Info("In function addVmEntriesToVmFile") foreach($vmView in $VirtualMachineViews) { # Skipping vm if object has no UUID, if UUID already seen (Extreme but possible) or vm is orphaned or corrupted or unavailable if(!$vmView.Config.InstanceUuid -or $vmInstanceUuidsHash[$vmView.Config.InstanceUuid] -or $vmView.Runtime.ConnectionState -ne "connected") { continue } [string]$vmName = $vmView.Name [string]$vmDatastoreId = "\N" [string]$vmInstanceUuid = "\N" [string]$vmPathName = $vmView.Summary.Config.VmPathName [string]$vmHostId = $vmview.Summary.Runtime.Host.GetHashCode() # vmPathName is in the format of [datastoreName] /vmDir/dir/file.vmx $vmDatastore = $dcDatastores | ?{$vmPathName.StartsWith("[" + $_.Name + "]")} if($vmDatastore) { $vmDatastoreId = $vmDatastore.Id.GetHashCode() } $vmInstanceUuidsHash[$vmView.Config.InstanceUuid] = $true $vmInstanceUuid = $vmView.Config.InstanceUuid # the wfa uuid is the uuid hashed in order to recieve an integer (uuid has alphabetic chars) [int]$hashedVmInstanceUuid = $vmInstanceUuid.GetHashCode() [int]$vmNumCpu = $vmView.Summary.Config.NumCpu [int]$vmMemoryMb = $vmView.Summary.Config.MemorySizeMB $vmPowerState = $vmview.Summary.Runtime.PowerState $vmGuestState = "\N" [string]$vmGuestOs = $vmView.Guest.GuestFullName [int]$vmIsTemplate = [int]$vmView.Config.Template $vmGuestOs = $vmGuestOs -replace "\n"," " # guest depended information $vmDnsName = "\N" $ipAddress = "\N" $vmBootTime = "\N" if($vmView.Summary.Runtime.BootTime) { # getting the boottime and save it in a format to fits mysql [DateTime]$bootDate = $vmView.Summary.Runtime.BootTime $vmBootTime = Get-Date -Date $bootDate -Format "yyyy-MM-dd HH:mm:ss" } # there is situations where the PowerState is poweredOff but the guestState is running. # Overriding it - whenever the PowerState is poweredOff GuestState will be null if($vmPowerState -eq "PoweredOn") { $wfaVmPowerState = "On" $vmGuestState = $vmView.Guest.GuestState if($vmView.Guest.GuestState -eq "running") { $vmDnsName = $vmView.Guest.HostName if($vmView.Guest.IpAddress) { $ipAddress = $vmView.Guest.IpAddress } elseif($vmView.Guest.Net) { $firstNic = $vmView.Guest.Net[0] if($firstNic.IpConfig.IpAddress.Length -gt 0) { $ipAddress = $firstNic.IpConfig.ipaddress[0].IpAddress } elseif($firstNic.IpAddress.Length -gt 0) { $ipAddress = $firstNic.ipAddress[0] } } } } elseif($vmPowerState -eq "PoweredOff"){ $wfaVmPowerState = "Off" } else{ $wfaVmPowerState = "Suspended" } Add-Content $vmsFile ([Byte[]][Char[]] "$hashedVmInstanceUuid`t$vmDatastoreId`t$vmHostId`t$vmInstanceUuid`t$vmName`t$vmDnsName`t$ipAddress`t$wfaVmPowerState`t$vmBootTime`t$vmGuestState`t$vmGuestOs`t$vmNumCpu`t$vmMemoryMb`t$vmIsTemplate`n") -Encoding Byte [array]$vmVdisks = $vmView.Config.Hardware.Device | ?{$_ -is [VMware.Vim.VirtualDisk]} if($vmVdisks) { addDiskEntriesToDiskFile -Disks $vmVdisks -hashedVmInstanceUuid $hashedVmInstanceUuid -vmHostId $vmHostId } } } Function addDiskEntriesToDiskFile { param( [parameter(Mandatory=$true, HelpMessage="Target vm disks to add to the Virtual_Disk CSV file")] [array]$Disks, [parameter(Mandatory=$true, HelpMessage="Hashed UUID of the target vm handled")] [int]$hashedVmInstanceUuid, [parameter(Mandatory=$true, HelpMessage="The integer part of the vm's running esx/i host")] [int]$VMHostId ) $Disks | %{ $hd = $_ if(!$hd.Backing.Datastore) { # skipping if hd is not accessible continue } $datastoreId = $hd.Backing.Datastore.GetHashCode() # csv null $lunId = "\N" if($hd.Backing.GetType().Name.Contains("RawDiskMapping")) { # imatating sdk scsi lun id value $lunId = ($VMHostId.ToString() + "/" + $hd.Backing.LunUuid).GetHashCode() } [string]$diskFileName = $hd.Backing.FileName $capacityMB = $hd.CapacityInKB / 1KB # getting all files associated with the curred disk with file from the LayoutEx.Disk array $vmLayoutExDisk = $vmView.LayoutEx.Disk | ?{$_.Key -eq $hd.Key} # recording only files associated with the disk [array]$arrLayoutExDiskFileKeys = $vmLayoutExDisk.Chain | ?{$_ -is [VMware.Vim.VirtualMachineFileLayoutExDiskUnit]} # calculating actual size of disk by measuring all disk file actual size and calculate sum $sizeOnDatastoreBytes = ($arrLayoutExDiskFileKeys | %{ $_.FileKey} | %{ $intFileKey = $_ # matching the file from the LayoutEx.File tree with matching key file and that represent # a file that is a diskExtent - part of the disk $vmView.LayoutEx.File | ?{($_.Key -eq $intFileKey) -and ($_.Type -eq "diskExtent")} } | Measure-Object -Sum Size).Sum $sizeOnDatastoreMB = [Math]::Round($sizeOnDatastoreBytes / 1MB, 1) Add-Content $virtualDiskFile ([Byte[]][Char[]] "\N`t$datastoreId`t$hashedVmInstanceUuid`t$lunId`t$diskFileName`t$capacityMB`t$sizeOnDatastoreMB`n") -Encoding Byte } } loadVMware #$connectionInfo = getConnectionInfo #$logg.Info("Trying to connect to VMware Host") try { #Connect-VIServer $connectionInfo["host"] -Port $connectionInfo["port"] -Credential $connectionInfo["credentials"] -Protocol https Connect-VIServer -Server $VcHost -Port $Port -User $User -Password $Password -Protocol https #$logg.Info("Connected Successfully") } catch { #$logg.Error("Failed to Connect to VmWare Host") throw $_.Message } # Getting the connected virtualcenter server ip. the GetHostAddresses will return an ip in either case of the supplied host (ip/hostname) #$virtualCenterIp = ([System.Net.Dns]::GetHostAddresses($connectionInfo["host"]) | Select -First 1).IPAddressToString $virtualCenterIp = $VcHost $datacenters = Get-Datacenter if(!$datacenters) { Disconnect-VIServer -Confirm:$false exit 0 } $globalVmhosts = @() # hash tables $extentsToVmfsDsHash = @{} $vmInstanceUuidsHash = @{} foreach($datacenter in $datacenters) { #$logg.Info("For Datacenter $datacenter") $datacenterName = $datacenter.Name $dcDatastores = @() # putting datastore collection ahead of hosts collection as # host collection includes luns connection which uses the exntestovmfsdshash populated in # the datastore section Foreach($datastore in ($datacenter | Get-Datastore | ?{$_.Accessible})){ # gathering dc datastores for later us in vm section $dcDatastores += $datastore $datastoreName = $datastore.Name $capacityMB = $datastore.CapacityMB $freeSpaceMB = $datastore.FreeSpaceMB $datastoreId = $datastore.Id.GetHashCode() $type = $datastore.Type if($type -eq "VMFS") { $datastore.ExtensionData.Info.Vmfs.Extent | %{ $extentsToVmfsDsHash[$_.DiskName] = $datastoreId } } Add-Content $datastoreFile ([byte[]][Char[]] "$datastoreId`t$datastoreName`t$capacityMB`t$freespaceMB`t$type`t$datacenterName`n") -Encoding Byte if($type -eq "NFS") { $nfsHost = $datastore.RemoteHost $nfsPath = $datastore.RemotePath $datastore.ExtensionData.Host | %{ $mountingHostId = $_.Key.GetHashCode() Add-Content $nasShareFile ([byte[]][Char[]] "\N`t$mountingHostId`t$datastoreId`t$nfsHost`t$nfsPath`t$capacityMB`n") -Encoding Byte } } } # collecting only operational hosts $vmhosts = $datacenter | Get-VMHost -State Connected # collecting all hosts objects for later use. doing that to reduce queries from vc $globalVmhosts += $vmhosts $vmhosts| %{ $name = $_.Name # Getting integer part of the VMHost's moid $hostId = $_.Id.GetHashCode() # ESX and ESXi are different in the way the API stores the management ip address # in their objects as concept changed in ESXi. if($_.ExtensionData.Summary.Config.Product.Name.Contains("ESXi")) { $ip = ($_.NetworkInfo.VirtualNic | ?{$_.ManagementTrafficEnabled -eq $true} | Select -First 1).IP } else { $ip = ($_.NetworkInfo.ConsoleNic | Select -First 1).ip } $os_version = $_.ExtensionData.Summary.Config.Product.FullName # the csv null $cluster = "\N" # see if the parent of the host is a cluster if($_.Parent.GetType().Name -eq "ClusterImpl") { $cluster = $_.Parent.Name } Add-Content $hostFile ([byte[]][char[]] "$hostId`t$name`t$ip`t$os_version`t$virtualCenterIp`t$cluster`t$datacenterName`n") -Encoding Byte } #working with views so we can have size on disk for thin provisioned disks $dcVmViews = Get-View -ViewType VirtualMachine -SearchRoot $datacenter.ExtensionData.MoRef # if dcVmview is null ie no virtual machines exists on the datacenter, the foreach loop is still entered once. avoiding that if($dcVmViews) { # addVmEntriesToVmFile function also adds the virtual disks entries addVmEntriesToVmFile -VirtualMachineViews $dcVmViews } } $scsiLunUuidToHbaTypeHash = @{} $scsiLuns = @() # The Get-VMHostHBA and the Get-ScsiLuns are the top time consumers of the datasource # as these return results really slow if($globalVmhosts) { $hbas = Get-VMHostHba -VMHost $globalVmhosts -ErrorAction SilentlyContinue $hbas | %{ $hba = $_ # collecting only SCSI luns which their primary opertional state is 'ok' $hbaDiskLuns = Get-ScsiLun -Hba $hba -LunType disk -ErrorAction SilentlyContinue | ?{($_.ExtensionData.OperationalState[0] -eq "ok") -or ($_.ExtensionData.OperationalState[0] -eq "quiesced")} # setting type for each lun in hash # if the current hbas is not seeing any online disk scsi luns and $hbaDiskLuns is $null - it loops single time even though # it is null casuing null entry in $scsiLuns collection -> adding validation before if($hbaDiskLuns){ $hbaDiskLuns | %{ $scsiLunUuidToHbaTypeHash[$_.ExtensionData.Uuid] = $hba.Type } # adding to all disk luns array for later use $scsiLuns += $hbaDiskLuns } } } if($scsiLuns) { $seenScsiLunsHash = @{} foreach ($scsi_lun in $scsiLuns) { $lunUuid = $scsi_lun.ExtensionData.Uuid # index 2 because this is a full moref in format of HostSystem-host-xxxx and not moref value $hostId = $scsi_lun.VMHostId.GetHashCode() $lunCanonicalName = $scsi_lun.CanonicalName # same lun will have the same uuid on its different instances from the different esx # so creating a unique id based on the host and the lun uuids $lunIdentifier = ($hostId.ToString() + "/" + $lunUuid).GetHashCode() #eliminate duplications of luns casued by multiple LUN paths on the vmhost if(!$seenScsiLunsHash[$lunIdentifier]) { $seenScsiLunsHash[$lunIdentifier] = $true; $lunDatastoreId = $extentsToVmfsDsHash[$lunCanonicalName] if(!$lunDatastoreId) { # csv null $lunDatastoreId = "\N" } $lunTransport = "\N" #Try to get the arrRuntime for each lun. If not present try to get the Lun Path try { # run time in the format vmhbaXX:CX:TX:LXXX $arrRuntime = $scsi_lun.RuntimeName.Split(":") $vmhostHbaName = $arrRuntime[0] $targetId = [int]$arrRuntime[2].SubString(1) # the LUN number $lunNumber = [int]$arrRuntime[3].subString(1) $sizeMB = $scsi_lun.CapacityMB #$logg.Info("Successfully obtained LUN RuntimeName for lun : $lunCanonicalName") } catch { # Get the first ACTIVE LUN path in format vmhbaXX:CX:TX:LXXX #$logg.Info("Failed to get LUN data from RuntimeName for Lun $lunCanonicalName. Using cmdlet Get-ScsiLunPath") $paths = Get-ScsiLunPath -ScsiLun $scsi_lun try { foreach ($path in $paths) { if($path.State -eq "Active") { $arrRuntime= $path.Name.Split(":") break } } $vmhostHbaName = $arrRuntime[0] $targetId = [int]$arrRuntime[2].subString(1) $lunNumber = [int]$arrRuntime[3].subString(1) $sizeMB = $scsi_lun.CapacityMB #$logg.Info("Successfully obtained LUN data for lun : $lunCanonicalName") } catch { #Some Luns(perhaps from unknown non NetApp vendors) do not return ACTIVE paths in format of vmhbaXX:CX:TX:LXXX. Such Luns will be ignored. #$logg.Error("FAILED to get Lun Info for the path ACTIVE path $path. Ignoring this lun path: $path") continue } } switch($scsiLunUuidToHbaTypeHash[$scsi_lun.ExtensionData.Uuid]) { "Block" {$lunTransport = "Local"; break} "ParallelSCSI" {$lunTransport = "Local"; break} "FibreChannel" {$lunTransport = "FibreChannel"; break} "iSCSI" {$lunTransport = "iSCSI"; break} } Add-Content $lunFile ([Byte[]][Char[]] "$lunIdentifier`t$hostId`t$lunDatastoreId`t$vmhostHbaName`t$lunNumber`t$targetId`t$sizeMB`t$lunCanonicalName`t$lunTransport`n") -Encoding Byte } } } #$logg.Info("Disconneting from VMware Host") Disconnect-VIServer -Confirm:$false #$logg.Info("Disconneted Successfully") #The CSV files may not be of UTF8 encoded. Convert the encoding of the all CSV files to UTF8 #$logg.Info("Changing File Encoding to UTF8 without BOM") [System.Io.File]::ReadAllText($hostFile) | Set-Content -Path $hostFile -Encoding ascii [System.Io.File]::ReadAllText($datastoreFile) | Set-Content -Path $datastoreFile -Encoding ascii [System.Io.File]::ReadAllText($lunFile) | Set-Content -Path $lunFile -Encoding ascii [System.Io.File]::ReadAllText($virtualDiskFile) | Set-Content -Path $virtualDiskFile -Encoding ascii [System.Io.File]::ReadAllText($nasShareFile) | Set-Content -Path $nasShareFile -Encoding ascii [System.Io.File]::ReadAllText($vmsFile) | Set-Content -Path $vmsFile -Encoding ascii function checkFileStatus($filePath) { $fileInfo = New-Object System.IO.FileInfo $filePath try { $fileStream = $fileInfo.Open( [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::Read ) $fileStream.Dispose() return $true } catch { return $false } } while(!(checkFileStatus $hostFile)) { #$logg.Warn("File $hostFile is busy. Sleeping 5 seconds") Sleep -s 5 } while(!(checkFileStatus $datastoreFile)) { #$logg.Warn("File $datastoreFile is busy. Sleeping 5 seconds") Sleep -s 5 } while(!(checkFileStatus $lunFile)) { #$logg.Warn("File $lunFile is busy. Sleeping 5 seconds") Sleep -s 5 } while(!(checkFileStatus $virtualDiskFile)) { #$logg.Warn("File $virtualDiskFile is busy. Sleeping 5 seconds") Sleep -s 5 } while(!(checkFileStatus $nasShareFile)) { #$logg.Warn("File $nasShareFile is busy. Sleeping 5 seconds") Sleep -s 5 } while(!(checkFileStatus $vmsFile)) { #$logg.Warn("File $vmsFile is busy. Sleeping 5 seconds") Sleep -s 5 } #$logg.Info("========= Acquistion script completed =======")