1

What is the best way to analyze powershell cmdlets in production ? Suppose you write a script which does the following -

  1. Write lof of registry values
  2. Register COM Dlls
  3. Make IIS AppPools
  4. Start Windows Services

&...something goes wrong in between, then what are the best practices to inform user such that root issue can be traced and debugged ?

Suppose, user credentials fail for AppPool creation for some reason and I want to stop processing at that time plus I want to rollback what I had done earlier.

Is verbose mode + Logging an elegant way to collect each details at every step ?

1
  • any final solution with full source code sample about it ? Commented Jun 14, 2012 at 8:41

2 Answers 2

2

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 -

  1. It won't log external program output unless you force it through the host output API by piping it to Out-Host such as ping.exe localhost | Out-Host.
  2. It's not supported on all hosts. For example PowerGUI doesn't support it so you have to add a host check using $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.
}
Sign up to request clarification or add additional context in comments.

4 Comments

One thing - I have 35 different nested modules. And, all modules are loaded via Import-Module call and then specific function related to a specific operation is called in sequence. So, should I still put try / catch inside each function in the module or should I wrap all the sequences of call in one tr / catch ... ?
@AngshumanAgarwal The pattern I usually follow is to use a try/catch in the module functions. If something goes wrong in them I just throw the message from it with an exception message. It will be caught in the caller's catch block and you can re-throw so by the time it ends up in the main you'll have a pseudo stacktrace to evaluate.
Was using the Write-Log function. Don't you think that this function should be modified like this -[Parameter()] [Switch] $NoConsoleOut = $true,
@AngshumanAgarwal One of my goals with it was to log and display information at the same time so the user has a status of what is going on while the program is running.
2

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.