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/
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.
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