Skip part of script on error


Skip part of script on error



I've been banging my head against the wall for awhile on this one. No amount of Googling has yielded me any successful results thus far. Wondering if someone can give me a hand?


# Load the PowerShell module for Active Directory
Import-Module ActiveDirectory

# For each computer in AD that is a member of the target group
Get-ADGroupMember -Identity "CN=RenameComputer,CN=Users,DC=int,DC=example,DC=com" | ForEach {

# Define the new name as the old one, minus one letter at the end
$NewComputerName = $_.Name -replace ".$"

# Perform the rename operation
Rename-Computer -ComputerName $_.Name -NewName $NewComputerName -Force -PassThru -Restart -WhatIf

# Remove the computer from the target group
# THIS SHOULD NOT OCCUR IF THE RENAME ABOVE FAILED!!!
Remove-ADGroupMember -Identity "CN=RenameComputer,CN=Users,DC=int,DC=example,DC=com" -Members $NewComputerName -WhatIf

}



TL;DR: This script finds a bunch of computers in a designated group, renames them, and then removes them from the group. I need help telling the script to NOT remove them from the group in the event that the rename fails (machine is offline, etc.) or throws some error.



Thoughts?



Thanks in advance!




1 Answer
1



There are a few ways you can check the result of a cmdlet in Powershell.



Run the cmdlet as an if statement condition


if



You can use the returned object of a cmdlet as an if statement condition, as long as it returns an object on success (which is why we need -PassThru for Rename-Computer, as normally it doesn't return an object). So to use your code as an example (and removing the -WhatIf):


if


-PassThru


Rename-Computer


-WhatIf


# If Rename-Computer succeeds...
if( Rename-Computer -ComputerName $_.Name `
-NewName $NewComputerName -Force -PassThru -Restart ) {

# then remove the Computer from group
Remove-ADGroupMember -Identity "CN=RenameComputer,CN=Users,DC=int,DC=example,DC=com" `
-Members $NewComputerName

}



This works because objects are truthy in Powershell. Most defined objects and any number other than 0 (zero) are considered $True, but values like $null, "" (an empty string), and 0 (zero) are evaluated as $False. So in the case of your code, if it succeeds an object will be returned ($True), if an error occurs there will be no object returned ($False).


0


$True


$null


""


0


$False


$True



Explicitly check cmdlet result



Alternatively, you could run Rename-Computer first, then check its success:


Rename-Computer


Rename-Computer -ComputerName $_.Name -NewName $NewComputerName -Force

# Check success of last cmdlet
if( $? ) {
Remove-ADGroupMember -Identity "CN=RenameComputer,CN=Users,DC=int,DC=example,DC=com" `
-Members $NewComputerName
}



$? evaluates the success of the last cmdlet run (don't confuse this with $LASTEXITCODE, which is for checking the last run program result, not cmdlet result). If the cmdlet succeeded, it returns $True, if not, $False.


$?


$LASTEXITCODE


$True


$False



Catch Errors in a Try/Catch Block


Try/Catch



A third way to do it would be to use a try/catch block, though you have to make sure that any error is terminating (will stop the script if uncaught) to catch an exception. You can use the -ErrorAction Stop paremeter for this:


try/catch


-ErrorAction Stop


try {

Rename-Computer -ComputerName $_.Name -NewName $NewComputerName -Force `
-PassThru -Restart -ErrorAction Stop

Remove-ADGroupMember -Identity "CN=RenameComputer,CN=Users,DC=int,DC=example,DC=com" `
-Members $NewComputerName

} catch {
# Do something on failure
Write-Warning "An error occurred: $($_.Exception.Message)"
}



If Rename-Computer throws an error within the try block, which it will on failure, the execution jumps to the catch block and will skip over Remove-ADGroupMember. If using a try/catch is your preferred method of error handling, you may consider setting $ErrorActionPreference = 'Stop' in your scripts, which sets the default -ErrorAction behavior to Stop, and you don't have to specify it on every cmdlet.


Rename-Computer


try


catch


Remove-ADGroupMember


try/catch


$ErrorActionPreference = 'Stop'


-ErrorAction


Stop



Additional Information



Here are some official docs about try/catch/finally and if statements in Powershell. I did not cover finally above but this is an optional part of try/catch statements:


try/catch/finally


if


finally


try/catch



About If: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_if?view=powershell-6



About Try/Catch/Finally: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-6



Here is also some advanced information about exceptions, errors, and handling them in Powershell: https://kevinmarquette.github.io/2017-04-10-Powershell-exceptions-everything-you-ever-wanted-to-know/





Thank you sincerely for your in-depth reply. I suspected there were multiple ways to accomplish this! Out of curiosity, from a development standpoint, what would be considered the best practice and why?
– Mitch Brown
2 days ago





It mostly comes down to personal preference. My preference is to use the inline if statement and check my cmdlet results in there. There are cases where you want to error out immediately using a try/catch and handle the error instead of checking a result in an if statement and continue, but in this case you don't need to worry about race conditions, as they are called.
– Bender the Greatest
yesterday


if


if


race conditions





I added some additional info on if, try/catch/finally, and Powershell error handling in general.
– Bender the Greatest
yesterday


if


try/catch/finally






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Comments

Popular posts from this blog

paramiko-expect timeout is happening after executing the command

Opening a url is failing in Swift

Export result set on Dbeaver to CSV