VMware Cloud Community
vin01
Expert
Expert
Jump to solution

Improve the execution Speed while doing invoke-vm

I am trying to execute the below script on 180vms but its taking long time to get the output ( 180 vm nearly 14hrs) and some times powershell session is hanging.

Is it possible to improve the execution time? Can some improvement done for the code in $script variable or anywhere in the script if possible?

$script = @'

if([IntPtr]::Size -eq 8){

    $text = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Network Associates\ePolicy Orchestrator\Agent\" -ErrorAction SilentlyContinue |

        %{"$($_.AgentGUID)"}

}

elseif([IntPtr]::Size -eq 4){

    $text = Get-ItemProperty -Path "HKLM:\SOFTWARE\Network Associates\ePolicy Orchestrator\Agent\" -ErrorAction SilentlyContinue |

        %{"$($_.AgentGUID)"}

}

if(-not $text){

    $text = Get-Service -Name "McAfee Framework Service" -ErrorAction SilentlyContinue | %{"$($_.Name) is $($_.Status)"}

    if(-not $text){

        $text = Get-Service -Name wuauserv | Select -First 1 |

            %{"$($_.Name) is $($_.Status)"}

    }

}

$cbout = Get-Service -Name "CarbonBlack" -ErrorAction SilentlyContinue | Select -first 1 | %{"$($_.Name) is $($_.Status)"}

if(-not $cbout){

    $cbout = Get-Service -Name wuauserv | Select -first 1 | %{"$($_.Name) is $($_.Status)"}

}

$hotfix = "KB4012598", "KB4012598", "KB4012598", "KB4012598", "KB4012212", "KB4012215", "KB4012212", "KB4012215", "KB4012213", "KB4012216", "KB4012214", "KB4012217", "KB4012213", "KB4012216", "KB4012606", "KB4013198", "KB4013429", "KB4013429"

$hotfixinfo = "No hotfix found!"

$fixes = Get-WmiObject -Class "win32_quickfixengineering" |

    where{$hotfix -contains $_.HotFixID} |

    Select -ExpandProperty HotFixID

if($fixes){

    $hotfixinfo = "$($fixes -join ',') installed"

}

$outputarray=@()

$outputarray+=$hotfixinfo

$outputarray+=$cbout

$outputarray+=$text

$outputarray -join ';'

'@

$obj = foreach($vm in Get-ResourcePool "RP1" |Get-VM |  Where-Object {$_.PowerState -eq "Poweredon" -and $_.ExtensionData.Config.GuestFullName -like "*Microsoft*"}) {

  $out1 = ""

  $out2 = ""

  $out3 = ""

  $out4 = ""

  $found = $false

  try{ 

    $out1 = Invoke-VMScript -VM $vm.Name -GuestUser "administrator" -GuestPassword "" -ScriptText $script -ScriptType Powershell -ErrorAction Stop | Select -ExpandProperty ScriptOutput

    $found = $true

  } 

  catch [VMware.VimAutomation.ViCore.Types.V1.ErrorHandling.InvalidGuestLogin]{ 

    $out1 = "Invalid logon" 

  } 

  catch{ 

    $out1 = "any other output" 

  } 

  if(!$found) {

    try{ 

      $out2 = Invoke-VMScript -VM $vm.Name -GuestUser "administrator" -GuestPassword "" -ScriptText $script -ScriptType Powershell -ErrorAction Stop | Select -ExpandProperty ScriptOutput

      $found = $true

    } 

    catch [VMware.VimAutomation.ViCore.Types.V1.ErrorHandling.InvalidGuestLogin]{ 

      $out2 = "Invalid logon" 

    } 

    catch{ 

      $out2 = "any other output" 

    } 

  }

  if(!$found){

    try{ 

      $out3 = Invoke-VMScript -VM $vm.Name -GuestUser "administrator" -GuestPassword "" -ScriptText $script -ScriptType Powershell -ErrorAction Stop | Select -ExpandProperty ScriptOutput

      $found = $true

    } 

    catch [VMware.VimAutomation.ViCore.Types.V1.ErrorHandling.InvalidGuestLogin]{ 

      $out3 = "Invalid logon" 

    } 

    catch{ 

      $out3 = "any other output" 

    }

  }

  if(!$found){

    try{ 

      $out4 = Invoke-VMScript -VM $vm.Name -GuestUser "admin" -GuestPassword "" -ScriptText $script -ScriptType Powershell -ErrorAction Stop | Select -ExpandProperty ScriptOutput

      $found = $true

    } 

    catch [VMware.VimAutomation.ViCore.Types.V1.ErrorHandling.InvalidGuestLogin]{ 

      $out4 = "Invalid logon" 

    } 

    catch{ 

      $out4 = "any other output" 

    }

  }

New-Object PSObject -Property (

    [ordered]@{

        Name = $vm.Name

        OS   = $vm.Guest.OSFullName

        IP   = ($vm.Guest.IPAddress | Where {($_.Split(".")).length -eq 4}) -join (",")

        RP   = $vm.ResourcePool.Name

        Out1 = $out1

        Out2 = $out2

        Out3 = $out3

        Out4 = $out4

  })

}

$obj

#|Export-Csv -Path C:\Users\admin\Documents\cbstatusonser29_latest.csv -NoTypeInformation -NoClobber

Regards Vineeth.K
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

The easiest way to improve the run time would be to run the script for a number of VMs in parallel.

This could be done by running the instances in the background, through the Start-Job cmdlet.


You would need to pass the SessionId to the background scripts to do a Connect-VIServer to the session you already opened.

Then in a loop you would retrieve the output for each background job with the Receive-Job cmdlet.
You could control how many background jobs you run in parallel (depends on the capacity of the station on which you run the scripts).


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

View solution in original post

Reply
0 Kudos
10 Replies
LucD
Leadership
Leadership
Jump to solution

The easiest way to improve the run time would be to run the script for a number of VMs in parallel.

This could be done by running the instances in the background, through the Start-Job cmdlet.


You would need to pass the SessionId to the background scripts to do a Connect-VIServer to the session you already opened.

Then in a loop you would retrieve the output for each background job with the Receive-Job cmdlet.
You could control how many background jobs you run in parallel (depends on the capacity of the station on which you run the scripts).


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

Reply
0 Kudos
vin01
Expert
Expert
Jump to solution

The easiest way to improve the run time would be to run the script for a number of VMs in parallel.

OK I split the vms based on the resource pools some thing like (Get-resourcepool 'rp1' |Get-vm)

start-job like this?

$job1=Start-Job -FilePath 'C:\My Data\Working\getclusterasajob.ps1'

$job1result=Receive-Job -Job $job1

I passed the connect-viserver inside the getclusterasajob.ps1 file. Its displaying the output as below. Please correct if I am wrong.

pastedImage_3.png

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

No, that looks ok for the Start-Job.

Now you have to wait for the job to finish (Get-Job and check the status).

When the jobs are finished you get the output with Receive-Job


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

Reply
0 Kudos
vin01
Expert
Expert
Jump to solution

Thanks Sir. I have followed the same process (Start-job) -Get-Job -Check completion status

Regards Vineeth.K
Reply
0 Kudos
vin01
Expert
Expert
Jump to solution

Hi LucD,

When I executed the Start-job on 1800 VMs it was done successful after 32hrs but on other start-job executed on 680 VM's the job is still running from 48hrs. Is there any way to find whether the code was struck or any errors.

Is it possible add start and end time to the script (Some thing like -Measure-Command)?

Job info:

Get-Job -Id 1 |fl

pastedImage_0.png

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

Unfortunately background jobs do not provide a time limit feature.

I normally use a timer for each job, and when the elapsed time exceeds a specific threshold the script stops the running jobs.

This is an example.

It launches 3 background jobs that will run for 60 seconds.

Then the script waits for 15 seconds, after which it checks the timers of each job.

If the job had been running for longer than 10 seconds, the script stops the job.

$script = {

  sleep 60

  }

 

   $maxSecondsToRun = 10

   $timerTab = @{}

 

   1..3 | %{

     $job = Start-Job -Name TestJob -ScriptBlock $script

     $t = New-Object Diagnostics.StopWatch

     $timerTab.Add($job.Id,$t)

     $timerTab.Item($job.Id).Start()

  }

 

  sleep 15

 

   $timerTab.GetEnumerator() | %{

   if($_.Value.Elapsed.TotalSeconds -gt $maxSecondsToRun){

    Stop-Job -Id $_.Key -Confirm:$false

   }

  }

 


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

vin01
Expert
Expert
Jump to solution

Cool. Thanks.

But this may not Solve the problem. If the Job Stops after defined time then we will not get the output with the above script.

If I execute the main script normally (not as job) can it possible to include the completion time as a status bar.

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

When a job completes, you have the start and finish time in there.

You can do

$job = Get-Job -Name TestJob

Write-Host "This job ran for $(($job.PSEndTime - $job.PSBeginTime).TotalSeconds) seconds"


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

vin01
Expert
Expert
Jump to solution

Ok. Thanks.

Regards Vineeth.K
Reply
0 Kudos
satheeshsatz
Enthusiast
Enthusiast
Jump to solution

are you able to complete this? if so, what all are the changes you have done in the original script and what is the output.

Regards, Satheesh
Reply
0 Kudos