VMware Cloud Community
Golden275
Contributor
Contributor
Jump to solution

how to catch error when Invoke-VMScript RunAsync

Hello team

i'm trying to validate local admin credential for all vms 

#1 Able to submit validation for all vms without waiting for other to complete  by command : 'Invoke-VMScript RunAsync'

#2 but try...catch doesn't work for some reason, the 'Catch part never get evaluated, even though tasks.state show some vms failed to login

Question :

the interesting thing is foreach loop work fine for try...catch, and try..catch doesnot work when use 'foreach-object -process' 

if there is a way to catch error when Invoke-VMScript RunAsync ?

 

#################

'  $result : output shows all good. But the real result are item 1 - 5 : 'error for login' and item6 'login successfully' if compare with below "tasks.state output " 

Golden275_4-1664758945882.png

 

tasks.state output :

Golden275_3-1664758815649.png

 

###script####

$VMName=Get-VM | Where-Object {$_.PowerState -eq "PoweredOn" -and $_.ExtensionData.Config.GuestFullName -like "*Microsoft*" }

$tasks = @()

$result = @()

$tasks += $VMName | ForEach-Object -process {

try{
(Invoke-VMScript -vm $_ -GuestUser ".\XXX" -GuestPassword "XXX" -ScriptText " " -ErrorAction Stop -ScriptType Powershell -RunAsync -confirm:$false)

$Loginstate= "Login Sucessfully"
write-host "$($_.name) successful guest login"
}
catch [VMware.VimAutomation.ViCore.Types.V1.ErrorHandling.InvalidGuestLogin]{

$Loginstate= "Invalid guest logon"
Write-Host "$($_.name) invalid guest login"
Write-Output $_

}

catch{

$Loginstate= "something went wrong !"
Write-Host "$($_.name) other error"
Write-Output $_

}

###



$result += $_ | Select Name,

@{N='Output';E={$Loginstate}},

@{N='IP';E={(Get-VMGuest -VM $_ | Select -ExpandProperty IPAddress) -join '|'}}
}

}

0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

The Try-Catch construct will intercept a terminating exception.
With -ErrorAction Stop you specify that every error will be a terminating exception.
So far so good, your logic is correct.

But with the RunAsync option you tell the Invoke-VMScript cmdlet to start the script and then return to continue execution of the rest of the code.
In your case the Try-Catch will only handle any errors that are caused by the start of the Invoke-VMScript cmdlet.
Not any errors that the actual script started with Invoke-VMScript triggers.

Leave out the RunAsync and you should see the difference.


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

View solution in original post

0 Kudos
5 Replies
LucD
Leadership
Leadership
Jump to solution

The Try-Catch construct will intercept a terminating exception.
With -ErrorAction Stop you specify that every error will be a terminating exception.
So far so good, your logic is correct.

But with the RunAsync option you tell the Invoke-VMScript cmdlet to start the script and then return to continue execution of the rest of the code.
In your case the Try-Catch will only handle any errors that are caused by the start of the Invoke-VMScript cmdlet.
Not any errors that the actual script started with Invoke-VMScript triggers.

Leave out the RunAsync and you should see the difference.


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

0 Kudos
Golden275
Contributor
Contributor
Jump to solution

Thank you @LucD 

You're right, get ride of -RunAsync, and initiated by a cmdlet do not run in parallel, run it in a loop and try-catch handles the error properly.

Because there are more than hundred machines need to check so I would like to run in parallel.

 

For Invoke-VMScript, to refresh the task status, pass the task object to the Get-Task cmdlet will not work rather have to call tasks.state directly(client side task) 

Question:

I'm looking to see if there is a way to get the vm name along with tasks.state, coz the regular tasks.task output doesn't has vm properties.

 

Golden275_0-1664801929830.png

 

Golden275_1-1664802149450.png

 

 

0 Kudos
LucD
Leadership
Leadership
Jump to solution

When you use Start-Job for parallelism ou don't need that RunAsync switch.
Just make sure that limit the maximum number of background jobs you run with Start-Job.

Client-side tasks are not visible through Get-Task I'm afraid.
You could use the Get-Job cmdlet to check on the status of the background jobs.


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

0 Kudos
Golden275
Contributor
Contributor
Jump to solution

thank you @LucD 

I will try your recommendation 'start-job' and report back 


Right, client-side task doesn't avail via get-task so I modified my code, and as a workaround I'm able to bind state with VM name by adding a new temp hash table

Golden275_0-1664808855925.png


####Script to validate local admin cred###

$VMName=Get-VM | Where-Object {$_.PowerState -eq "PoweredOn" -and $_.ExtensionData.Config.GuestFullName -like "*Microsoft*" }

$tasks = @()
$vnametemp = @()
$task2s = @{}

$VMName | ForEach-Object -process {

    try{
        $tasks += (Invoke-VMScript  -vm  $_ -GuestUser ".\XXXX" -GuestPassword "XXXX" -ScriptText " " -ErrorAction Stop -ScriptType Powershell  -RunAsync -confirm:$false)

        $vnametemp += $_.name

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

        $Loginstate= "Invalid guest logon"
        Write-Host "$($_.name) invalid guest login"
        Write-Output $_

    }

    catch{

        $Loginstate= "something went wrong !"
        Write-Host "$($_.name) other error"
        Write-Output $_

    }

    For($i = 0; $i -lt $tasks.id.Count; $i++){
   
        $tname   = $vnametemp[$i]
        $task2s[$tasks[$i].id] = $vnametemp[$i]
    }
}

while ($tasks.State -contains 'running') {

Start-Sleep 1

}


$newset =@()

Foreach($ids in $tasks){
    $id = $ids.id
    if ($task2s.ContainsKey($id)){
    $newset += $ids| select  @{N='State';E={$_.state}},
           
                                @{N='VM';E={$task2s[$id]}},
           
                                @{N='Start-Time';E={$_.starttime}},
           
                                @{N='Error';E={$_.terminatingerror}}
    }
}

$newset | ft -AutoSize
0 Kudos
Golden275
Contributor
Contributor
Jump to solution

@LucD 

 

I will close this for now and create a new one if start-job running into any issue

 

thanks

0 Kudos