VMware Cloud Community
vandreytrindade
Enthusiast
Enthusiast

Export VMware information to an Excel file using PowerCLI

Hi,

I created a script on PowerCLI to extract information from VMware and fill a existing Excel file.

I'll show it here for two reasons:

  1. Trying to get help on optimizing the code to make it better (I'm a beginner on PowerShell)
  2. Sharing script to people who is beginning on PowerCLI like me to exchange ideas =]

Steps to make it work:

  1. Verify the path of the ConfigVirtual.xlsx file in the script (Row 76)
  2. Copy the existent worksheets to create new ones to match the exaclty number of physical servers you have on your environment (Source Excel file attached on the post)
  3. Rename the worksheets and the top right cell with the name of each of your ESXi servers (I've changed them to Server name, Server 2 name, etc...)
  4. Run the script on PowerCLI logged on every server with a user with root privileges on them (I've used a logon script)
    • $VMcred = Get-Credential

      Connect-VIServer -Server server1, server2, server3, server4 -Credential $VMcred

Copy the text below to notepad, save it with .ps1 extension and run it =]

#Function to retrieve physical servers info

function HostInfo {

  #$WorkBook01.Sheets.Item($ESXiServer.Name).Activate()

  $TotalGB = $Null

  $Datastores = Get-VMHost $ESXiServer.Name | Get-Datastore | Where {($_.Name -notlike "VMD*") -and ($_.Name -notlike "BKP*")}

  foreach ($Datastore in $Datastores) {

    $TotalGB += $Datastore.CapacityGB

    }

  ($WorkBook01.Sheets.Item($ESXiServer.Name)).Cells.Item(7,2) = $ESXiServer.NumCpu

  ($WorkBook01.Sheets.Item($ESXiServer.Name)).Cells.Item(7,3) = ($ESXiServer | select @{N="RAM";E={[math]::Round($_.MemoryTotalGB,0)}}).RAM

  ($WorkBook01.Sheets.Item($ESXiServer.Name)).Cells.Item(7,4) = [math]::Round($TotalGB,0)

  ($WorkBook01.Sheets.Item($ESXiServer.Name)).Cells.Item(7,6) = "VMware "+($ESXiServer).Version+" build "+($ESXiServer).Build

  ($WorkBook01.Sheets.Item($ESXiServer.Name)).Cells.Item(7,7) = if ($ESXiServer.PowerState -eq 1){"On"}else{"Off"}

  }

#Function to retrieve physical servers resources

function Resources {

  $ActiveServers = Get-VM -Server $ESXiServer.Name | Where {$_.PowerState -eq "PoweredOn"}

  $UsedCPU = $Null

  $UsedRAM = $Null

  $TotalHD = $Null

  foreach ($CPU in $ActiveServers.NumCpu){

    $UsedCPU += $CPU

    }

  foreach ($RAM in $ActiveServers.MemoryGB){

    $UsedRAM += $RAM

    }

  $ServerRAM = ($ESXiServer | select @{N="RAM";E={[math]::Round($_.MemoryTotalGB,0)}}).RAM

  $TotalCPU = $ESXiServer.NumCpu - $UsedCPU

  $Datastores = Get-VMHost $ESXiServer.Name | Get-Datastore | Where {($_.Name -notlike "VMD*") -and ($_.Name -notlike "BKP*")}

  foreach ($Datastore in $Datastores) {

    $TotalFreeGB += $Datastore.FreeSpaceGB

    }

  ($WorkBook01.Sheets.Item($ESXiServer.Name)).Cells.Item(8,2) = $TotalCPU

  ($WorkBook01.Sheets.Item($ESXiServer.Name)).Cells.Item(8,3) = [math]::Round(($ServerRAM - $UsedRAM),0)

  ($WorkBook01.Sheets.Item($ESXiServer.Name)).Cells.Item(8,4) = [math]::Round($TotalFreeGB,0)

  }

#Function to retrieve VMs info

function ServerInfo {

  foreach ($Server in $Servers) {

    #$WorkBook01.Sheets.Item($ESXiServer).Activate()

    $TotalHD = $Null

    $HardDisks = (Get-HardDisk $Server).CapacityGB

    foreach ($HardDisk in $HardDisks) {

      $TotalHD += $HardDisk

      }

    ($WorkBook01.Sheets.Item($ESXiServer)).Cells.Item($Row,1) = $Server.Name

    ($WorkBook01.Sheets.Item($ESXiServer)).Cells.Item($Row,2) = $Server.NumCpu

    ($WorkBook01.Sheets.Item($ESXiServer)).Cells.Item($Row,3) = [math]::Round($Server.MemoryGB)

    ($WorkBook01.Sheets.Item($ESXiServer)).Cells.Item($Row,4) = [math]::Round($TotalHD,0)

    ($WorkBook01.Sheets.Item($ESXiServer)).Cells.Item($Row,5) = (Get-Datastore -VM $Server.name).name

    ($WorkBook01.Sheets.Item($ESXiServer)).Cells.Item($Row,6) = $Server.Guest.OSFullName

    ($WorkBook01.Sheets.Item($ESXiServer)).Cells.Item($Row,7) = if ($Server.PowerState -eq 1){"On"}else{"Off"}

    ($WorkBook01.Sheets.Item($ESXiServer)).Cells.Item($Row,8) = $Server.Version.ToString()

    ($WorkBook01.Sheets.Item($ESXiServer)).Cells.Item($Row,9) = ($Server.Guest.ExtensionData).ToolsStatus.ToString()

    ($WorkBook01.Sheets.Item($ESXiServer)).Cells.Item($Row,10) = $Server.Guest.ToolsVersion

    #($WorkBook01.Sheets.Item($ESXiServer)).Columns.Item("A:I").EntireColumn.AutoFit() | out-null

    ($WorkBook01.Sheets.Item($ESXiServer)).Columns.Item("A:I").HorizontalAlignment = -4108

    $Row = $Row + 1

    }

  }

#Script beginning

$ESXiServers = Get-VMHost | Sort Name

$Excel01 = New-Object -ComObject Excel.Application

$Excel01.Visible = $False

$Workbook01 = $Excel01.Workbooks.Open("C:\VMware\ConfigVirtual.xlsx")

for ($i = 1; $i -le ($ESXiServers.Count); $i++) {

  $WorkSheet = $WorkBook01.Sheets.Item($i)

  #$WorkSheet.Activate()

  While ($Worksheet.Cells.Item(9,1).Value() -NotLike $Null){

    [void]$Worksheet.Cells.Item(9,1).EntireRow.Delete()

    }

  }

foreach ($ESXiServer in $ESXiServers) {

  HostInfo

  Resources

  }

foreach ($ESXiServer in $ESXiServers.Name) {

  $Row = 9

  $Servers = (Get-VM -Server $ESXiServer | Where {$_.Name -notlike "Unknown*"})

  ServerInfo

  }

$WorkBook01.Save()

#$WorkBook01.Close($True)

$Excel01.Quit()

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel01) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Workbook01) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Worksheet) | Out-Null

$Excel01 = $Null

PS: Using measure-command I found out that on my environment of 15 ESXi servers it took 13:04 to run the script.

Let me know if there is an error on the script... It was originally written in portuguese and then translated to post it here.

Hope someone will find this helpful =]

Att, Vandrey Trindade
Tags (1)
5 Replies
LucD
Leadership
Leadership

A small suggestion, you could use a function to create the Excel file instead of doing it cell by cell.

There is for example my Export-Xlsx, the sequel, and ordered data post.

And yesterday Doug published a module called ImportExcel, based on EPPlus, which has import and export functions.


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

vandreytrindade
Enthusiast
Enthusiast

Thanks LucD!

I'll take a look at those links =]

Att, Vandrey Trindade
0 Kudos
vandreytrindade
Enthusiast
Enthusiast

LucD,

I was reading your links (added your site on my RSS) and I was thinking:

Your suggestion is to optimize my code (get better performance) or more to clean (organize) the script?

I don't know if the way I'm doing is the faster or better way...

Att, Vandrey Trindade
0 Kudos
LucD
Leadership
Leadership

In fact both in my opinion.

Both the functions should be faster than maneuvering trough the cells.

But you should verify that of course.

By using either of the functions, you could collect the data in an array and pipe it to the function.

Would make the script simpler in my opinion.


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

0 Kudos
vandreytrindade
Enthusiast
Enthusiast

Hi!

After a long time I decided to change the script to stop using Excel COM objects and start using ImportExcel module.

Since my company use only ESXi free hosts, I had to create a way to get all information from those servers daily.

Some parts of this worksheet are added manually because the free version doesn't allow to extract that information. (Ex.: Physical server info used on comments)

I know that it won't be something that a lot of people will need, but I used it to learn more about PowerShell, and it's always fun to do something different, right? Smiley Happy

PS: I still don't know how to make the script faster... LucD gave me some tips but I just don't know a way how to do it... I'll have to study more to know how to make the script better.

PS2: The script was written using brazilian portuguese and I had to translate it to post here, sorry if there are some typos in there...

Here it goes a sample.

Screenshot of how the worksheet will be like:

Print1.png

Comment with the physical host information:

Print2.png

Comment showing notes for every VM:

Print3.png

Comment showing VMware Tools status:

Print4.png

$HVVMWARE = [System.Collections.ArrayList]@(

    "Server Name"

    "Server Name 2"

    "Server Name 3"

    "Server Name 4"

)

$File = "C:\PowerShell\VMware\Files\ConfigVirtual.xlsx"

$InactiveServers = @()

#Function to retrieve physical servers info

function HostInfo {

    $TotalGB = $Null

    $Datastores = Get-VMHost $ESXiServer.Name | Get-Datastore

    foreach ($Datastore in $Datastores) {

        $TotalGB += $Datastore.CapacityGB

    }

    $WorkSheet.Cells.Item(7,2).Value = $ESXiServer.NumCpu

    $WorkSheet.Cells.Item(7,3).Value = ($ESXiServer | Select-Object @{N = "RAM"; E = { [math]::Round($_.MemoryTotalGB, 0) } }).RAM

    $WorkSheet.Cells.Item(7,4).Value = [math]::Round($TotalGB, 0)

    $WorkSheet.Cells.Item(7,6).Value = "VMware " + ($ESXiServer).Version + " build " + ($ESXiServer).Build

    $WorkSheet.Cells.Item(7,7).Value = if ($ESXiServer.PowerState -eq 1) { "On" } else { "Off" }

}

#Function to retrieve physical servers resources

function Resources {

    $ActiveServers = Get-VM -Server $ESXiServer.Name | Where-Object { $_.PowerState -eq "PoweredOn" }

    $UsedCPU = $Null

    $UsedRAM = $Null

    $TotalFreeGB = $Null

    foreach ($CPU in $ActiveServers.NumCpu) {

        $UsedCPU += $CPU

    }

    foreach ($RAM in $ActiveServers.MemoryGB) {

        $UsedRAM += $RAM

    }

    $ServerRAM = ($ESXiServer | Select-Object @{N = "RAM"; E = { [math]::Round($_.MemoryTotalGB, 0) } }).RAM

    $TotalCPU = $ESXiServer.NumCpu - $UsedCPU

    $Datastores = Get-VMHost $ESXiServer.Name | Get-Datastore

    foreach ($Datastore in $Datastores) {

        $TotalFreeGB += $Datastore.FreeSpaceGB

    }

    $WorkSheet.Cells.Item(8,2).Value = $TotalCPU

    $WorkSheet.Cells.Item(8,3).Value = [math]::Round(($ServerRAM - $UsedRAM), 0)

    $WorkSheet.Cells.Item(8,4).Value = [math]::Round($TotalFreeGB, 0)

}

#Function to retrieve VMs info

function ServerInfo {

    $i = 1

    foreach ($Server in $Servers) {

        Write-Progress -Id 0 -Activity "Host: $($Server.VMHost.Name)"

        Write-Progress -Id 1 -ParentId 0 -Activity "Verifying VMs..." -CurrentOperation $Server -Status "Status: $([math]::round(($i / $Servers.count)*100))%" -PercentComplete (($i / $Servers.count) * 100) | Out-Default

        $i++

        $TotalHD = $Null

        $HardDisks = (Get-HardDisk $Server).CapacityGB

        foreach ($HardDisk in $HardDisks) {

            $TotalHD += $HardDisk

        }

        if ($Server.Notes) {$Notes = $Server.Notes} else {$Notes = "Server without notes, please fix it on VMware."}

        $WorkSheet = $WorkBook01.Item($ESXiServer)

        $WorkSheet.Cells["A$Line"].Value = $Server.Name

        $WorkSheet.Cells["A$Line"].AddComment($Notes,"VMware") | Out-Null

        $Font = $WorkSheet.Cells["A$Line"].Comment

        $Font.Font.Bold = $true

        $Font.Font.Size = 9

        $Font.Font.FontName = "Segoe UI"

        $WorkSheet.Cells["A$Line"].Comment.AutoFit = $true

        $WorkBook01.Item($ESXiServer).Cells.Item($Line,2).Value = $Server.NumCpu

        $WorkBook01.Item($ESXiServer).Cells.Item($Line,3).Value = [math]::Round($Server.MemoryGB)

        $WorkBook01.Item($ESXiServer).Cells.Item($Line,4).Value = [math]::Round($TotalHD, 0)

        $WorkBook01.Item($ESXiServer).Cells.Item($Line,5).Value = if ((Get-Datastore -VM $Server.name).count -gt 1) {

            $Datastores = Get-Datastore -VM $Server.name | Select-Object -ExpandProperty Name

            $Datastores -join ", "

        }

        else {

            (Get-Datastore -VM $Server.name).Name

        }

        $WorkBook01.Item($ESXiServer).Cells.Item($Line,6).Value = $Server.Guest.OSFullName

        $WorkBook01.Item($ESXiServer).Cells.Item($Line,7).Value = if ($Server.PowerState -eq 1) { "On" } else { "Off" }

        $WorkBook01.Item($ESXiServer).Cells.Item($Line,8).Value = $Server.HardwareVersion.ToString()

        $WorkBook01.Item($ESXiServer).Cells.Item($Line,9).Value = if (((($Server.Guest.ExtensionData).ToolsStatus.ToString()) -eq "toolsNotRunning") -or ((($Server.Guest.ExtensionData).ToolsStatus.ToString()) -eq "toolsOld")) {

            1

        }

        elseif ((($Server.Guest.ExtensionData).ToolsStatus.ToString()) -eq "toolsOk") {

            2 }

        else {

            0

        }

        if (($Server.Guest.ExtensionData).ToolsStatus.ToString() -ne "toolsOk") {

            $WorkSheet.Cells["I$Line"].AddComment(($Server.Guest.ExtensionData).ToolsStatus.ToString(),"VMware") | Out-Null

            $Font = $WorkSheet.Cells["I$Line"].Comment

            $Font.Font.Bold = $true

            $Font.Font.Size = 9

            $Font.Font.FontName = "Segoe UI"

            $WorkSheet.Cells["I$Line"].Comment.AutoFit = $true

        }

        $WorkBook01.Item($ESXiServer).Cells.Item($Line,10).Value = if (Get-VM $Server | Get-Snapshot) {

            "Yes"

        }

        else {

            "No"

        }

        $Line = $Line + 1

    }

}

$ESXiServers = Get-VMHost | Sort-Object Name

$Excel01 = Open-ExcelPackage -Path "C:\PowerShell\VMware\Files\ConfigVirtual-Original.xlsx"

$WorkBook01 = $Excel01.Workbook.Worksheets

$InactiveServers = Compare-Object -ReferenceObject $HVVMWARE -DifferenceObject $ESXiServers.Name | Select-Object -ExpandProperty InputObject

foreach ($InactiveServer in $InactiveServers){

    $WorkBook01.Item($InactiveServer).Cells.Item(7,7).Value = "Off"

}

$i = 1

foreach ($ESXiServer in $ESXiServers) {

    Write-Progress -Activity "Verifying servers..." -CurrentOperation $ESXiServer -Status "Status: $([math]::round(($i / $ESXiServers.count)*100))%" -PercentComplete (($i / $ESXiServers.count) * 100) | Out-Default

    $i++

    $WorkSheet = $WorkBook01.Item($ESXiServer.Name)

    HostInfo

    Resources

}

Write-Progress -Activity "Verifying servers..." -Completed | Out-Default

foreach ($ESXiServer in $ESXiServers.Name) {

    $Line = 9

    $Servers = (Get-VM -Server $ESXiServer | Where-Object { $_.Name -notlike "Unknown*" })

    ServerInfo

}

Write-Progress -Id 1 -ParentId 0 -Activity "Verifying VMs..." -Completed | Out-Default

Write-Progress -Id 0 -Completed -Activity "Host: $($Server.VMHost.Name)" | Out-Default

Close-ExcelPackage $Excel01 -SaveAs $File

$Excel01 = $Null

Att, Vandrey Trindade