What is the best way to analyze powershell cmdlets in production ? Suppose you write a script which does the following -
- Write lof of registry
For the direct registry manipulation you can use PowerShell's transactional support to either commit the changes upon overall success of the script or undo the transaction to rollback the registry changes e.g.:
try {
$ErrorActionPreference = 'Stop' # convert all errors to terminating errors
Start-Transaction
Set-ItemProperty Acme -Name Count -Value 99 -UseTransaction
Remove-ItemProperty Acme -Name InstallDir -UseTransaction
New-Item Acme2 -UseTransaction
New-ItemProperty Acme2 -Name Count -Value 2 -UseTransaction
New-ItemProperty Acme2 -Name InstallDir -Value 'C:\Program Files\Acme2' -UseTx
... do other stuff ...
Complete-Transaction
}
catch {
Undo-Transaction
... Log failure ...
}
I wrote a Write-Log
function that I posted on poshcode ( http://poshcode.org/3270 ) that I use for production level PowerShell programs. Alternatively you could use Start-Transcript
which logs almost everything displayed on the console to a file. There are a couple gotchas about Start-Transcript
-
Out-Host
such as ping.exe localhost | Out-Host
.$host
.The pattern I usually use for error handling is to wrap everything in a try/catch. The exception object will be available as $_
in the catch block. It will contain everything about the error that happened, the message, the line and column number etc...
I also set the $ErrorActionPreference
to Stop
so that all cmdlets throw a terminating error so the script won't continue. It looks like this:
$ErrorActionPreference = "Stop"
try {
# Write lof of registry values
New-Item -Path HKCU:\Software\MyTest -ItemType Directory
New-ItemProperty -Path HKCU:\Software\MyTest -Name MyTestValue -Value Test
# Register COM Dlls
regsrv32 my.dll
if ($LASTEXITCODE -ne 0) { throw "Failed to register my.dll" }
# Make IIS AppPools
IIS:\>New-WebAppPool NewAppPool
# Start Windows Services
Start-Service -Name MyService
} catch {
Write-Log ("Script failed. The error was: '{0}'." -f $_)
}
Rolling back is not easy... The only thing that might be easy to roll back are the registry operations because the registry supports transactions (assuming Vista or beyond). You can just create a transaction (like a database) and roll it back if an error occurs. The rest of the operations you mentioned will need specific code to roll them back. You could add the rollback code to the catch block like this:
} catch {
# Undo the registry transaction.
# Unregister the DLL.
# Delete the App pool if it exists.
# Stop the windows service.
}