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
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
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
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.
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
Thanks Sir. I have followed the same process (Start-job) -Get-Job -Check completion status
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
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.
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
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.
When a job completes, you have the start and finish time in there.
You can do
Write-Host "This job ran for $(($job.PSEndTime - $job.PSBeginTime).TotalSeconds) seconds"
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Ok. Thanks.
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.