Search code examples
powershellpowercli

PowerCLI performance


I have written a PowerCLI script to retrieve relevant information plus usage stats for the previous month for our VM's and output it a csv but it's painfully slow. Can anyone suggest any improvements that could help speed up the report.

I'm fairly new to powershell/PowerCLI so any help would be gratefully received

$todayMidnight = (Get-Date -Hour 0 -Minute 0 -Second 0).AddMinutes(-1)
$EndOfLastMonth = (Get-Date -Year (get-date).Year -Month (get-date).Month -Day 1 -Hour 0 -Minute 0 -Second 0).AddMinutes(-1)
$FirstofLastMonth = $EndOfLastMonth.AddMonths(-1)
$workingDays = "Monday","Tuesday","Wednesday","Thursday","Friday"
$dayStart = New-Object DateTime(1,1,1,8,0,0) 
$dayEnd = New-Object DateTime(1,1,1,18,0,0)

$CSVFile = Read-Host "Enter Filename for csv"

$Report = @()

Connect-VIServer "VCServer" | Out-Null

$ServerList = Get-VM |  Where-Object {$_.VMHost.Name -ne "192.168.106.161" -and $_.PowerState -eq "PoweredOn" } | Sort-Object Name

$Counter = 1

foreach ($Server in $ServerList) {

    $VMInfo = {} | select Name, OS, VMHost, IPAddress, NumCPU, TotalMemMB, AvgMemPcnt, MaxMemPcnt, AvgCPUMhz, AvgCPUPcnt, MaxCPUPcnt
    $VMInfo.name = $Server

    $MaxCPUPcnt = get-vm $Server | Get-Stat -Stat cpu.usage.average -IntervalSecs 1 |
        Where-Object {  $workingDays -contains $_.Timestamp.DayOfWeek -and $_.Timestamp.TimeOfDay -gt $dayStart.TimeOfDay -and $_.Timestamp.TimeOfDay -lt $dayEnd.TimeOfDay} |
        Measure-Object value -Max | select Maximum

    $MaxMemPcnt = get-vm $Server | Get-Stat -Stat mem.usage.average -IntervalSecs 1 |
    Where-Object {  $workingDays -contains $_.Timestamp.DayOfWeek -and $_.Timestamp.TimeOfDay -gt $dayStart.TimeOfDay -and $_.Timestamp.TimeOfDay -lt $dayEnd.TimeOfDay} |
    Measure-Object value -Max | select Maximum

    $AvgCPUMhz = get-vm $Server | Get-Stat -Stat cpu.usagemhz.average -IntervalMins 5 |
        Where-Object {  $workingDays -contains $_.Timestamp.DayOfWeek -and $_.Timestamp.TimeOfDay -gt $dayStart.TimeOfDay -and $_.Timestamp.TimeOfDay -lt $dayEnd.TimeOfDay} |
        Measure-Object value -Average | select Average

    $AvgCPUPcnt = get-vm $Server | Get-Stat -Stat cpu.usage.average -IntervalMins 5 |
        Where-Object {  $workingDays -contains $_.Timestamp.DayOfWeek -and $_.Timestamp.TimeOfDay -gt $dayStart.TimeOfDay -and $_.Timestamp.TimeOfDay -lt $dayEnd.TimeOfDay} |
        Measure-Object value -Average | select Average

    $AvgMemPcnt = get-vm $Server | Get-Stat -Stat mem.usage.average -IntervalMins 5 |
        Where-Object {  $workingDays -contains $_.Timestamp.DayOfWeek -and $_.Timestamp.TimeOfDay -gt $dayStart.TimeOfDay -and $_.Timestamp.TimeOfDay -lt $dayEnd.TimeOfDay} |
        Measure-Object value -Average | select Average

    $VMinfo.MaxCPUPcnt =  [math]::round($MaxCPUPcnt.Maximum,2)
    $VMinfo.MaxMemPcnt =  [math]::round($MaxMemPcnt.Maximum,2)
    $VMinfo.AvgCPUMhz =  [math]::round($AvgCPUMhz.Average,2)
    $VMInfo.AvgCPUPcnt = [math]::round($AvgCPUPcnt.Average,2)
    $VMInfo.AvgMemPcnt = [math]::round($AvgMemPcnt.Average,2)

    $TotalMemMB = get-vm $Server | select MemoryMB
    $VMInfo.TotalMemMB = $TotalMemMB.MemoryMb

    $VMInfo.VMHost = (get-vm gbvc0007 | Get-VMHost).name

    $VMInfo.OS = (Get-VM $Server | Get-View).summary.config.GuestFullName

    $VMInfo.IPAddress =  (Get-VM $Server | Get-VIew).summary.guest.ipaddress

    $VMInfo.NumCPU = (Get-VM $Server | Get-VIew).summary.config.NumCPU

    $Report += $VMInfo

    $Counter++
}

clear
$Report | ft -AutoSize

$Report | Export-Csv -Path $CSVFile

Disconnect-VIServer -Server * -Confirm:$false

Solution

  • Well, first of all, you would want to do:

    $vm = get-vm $Server

    and work with that afterwards, so you would not need to query the server one extra time for each of the operations. Also, I'm not aware of how the get-stat works, but if you could pull all the stats you need for a VM to a variable in just one call to the server, you could later easily filter out everything you need, saving a lot of time potentially, something like this:

    $vm = get-vm $Server
    $vm | Get-Stat -Stat mem.usage.average,cpu.usage.average,cpu.usagemhz.average
    

    And you could easily get something out of the variable you already have like:
    $vm.MemoryMB or probably something like $vm.GuestID

    you should take a look here - http://www.virtualizationadmin.com/articles-tutorials/general-virtualization-articles/use-powercli-quick-stats-part2.html

    As for the general approach, i would suggest you use something like PoshRSJob to parallel out the execution of your foreach loop, that would improve the time it takes for your script to run drasticly, the only caveat is that you would need to authenticate in each RSJob, which might be an issue.