homeblogtips using puppet powershell module

Tips for using the Puppet PowerShell module

*Editor's Note: This post was originally posted on Glenn Sarti's blog, http://glennsarti.github.io. It appears here in a slightly modified form, with Glenn's permission.*

Puppet has great support for Windows, including a module on the Puppet Forge to run PowerShell-based commands. There are some instructions in the README to help people use the module, but there a few traps for people who are just starting out. So here are a few tips to help you.

PowerShell exit codes

The Puppet PowerShell module makes use of the exit codes produced by PowerShell, so it's important to understand how these codes are produced within a PowerShell process.

The exit command can be used to exit a PowerShell script and optionally return an exit code.

Unlike a lot of scripting languages, PowerShell has the concept of terminating and non-terminating errors (more information). A terminating error will stop the script, whereas a non-terminating error will not. This can cause some headaches for people who aren't expecting it!

Take this very simple batch file:

It produces

This is what you would expect to happen. But what if I don't explicitly set an exit code?

produces

So far, so good. So what happens if I raise an error (division by zero)?

produces

But the exit code is still zero?!?!? What if the script syntax was broken, like a missing bracket?

produces

Now the exit code is no longer zero.

What would happen if I changed the error from non-terminating to terminating? This behavior is normally changed by specifying the ErrorAction parameter on Cmdlets or setting the global $ErrorActionPreference variable to Stop

produces

So the non-terminating error (without the ErrorAction parameter) produced a zero exit code, while the terminating error (ErrorAction Stop) produced a non-zero exit code.

What does this all mean?

If you depend on PowerShell returning a meaningful exit code, you should use the exit command for all code branches. This ensures PowerShell returns the exit codes you expect.

rather,


More information on -ErrorAction

More information on $ErrorActionPreference

Using onlyif and unless attributes

We’ll first look at version 1.0.6 of the Puppet PowerShell module. We’ll then look at the newly released version 2.0 of the Puppet Powershell module and what has changed.

The Puppet exec resource has onlyif and unless attributes, which can be used to limit when the command is invoked — e.g., "create this file only if it does not exist," or "start this windows service unless it's already running."

The onlyif parameter is defined as

If this parameter is set, then this exec will only run if the command has an exit code of 0.

Whereas the unless attribute is defined as

If this parameter is set, then this exec will run unless the command has an exit code of 0.

Using what we know about PowerShell exit codes, a simple test manifest can be used to see what would happen for different scenarios:

produces

So what did these tests show?

  • The explicitly set exit code tests onlyif check exit 0, onlyif check exit 1, unless check exit 0 and unless check exit 1 behaved exactly as the documentation stated.
  • The no exit code set tests onlyif check noexit and unless check noexit behaved exactly as the documentation stated; i.e., default exit code of 0.
  • The terminating error tests (onlyif check term error and unless check term error) behaved exactly as the documentation stated, remembering that PowerShell returns exit code 1 for terminating errors.
  • The non-terminating error tests onlyif check non-term error and unless check non-term error are a bit strange. They behaved as if Powershell had returned an exit code of 1, but our PowerShell tests in the previous section showed that non-terminating errors return zero.

So as one last test, what would happen if a non-terminating error was thrown, but there was another command afterwards? After all, non-terminating errors do not terminate the script. Let's add Write-Host "Hello" to the end of the onlyif and unless, and see what happens:

produces

What happened there?!?! So now the non-terminating errors are behaving as if PowerShell had returned exit code 0.

It appears that the exit code is being determined by the last command executed. This can cause a lot of headaches when debugging onlyif and unless clauses.

What does all this mean?

  • Always use explicit exit codes.
  • Watch out for terminating errors, as the exec resource will treat them as non-zero exit codes.

Example:

Create an exec resource that;

Starts the FOO service unless, it doesn't exist or it exists and is already running

In the example above, if the service doesn't exist, it will throw a non-terminating error and then process the next command. Remembering the previous weird test case, it will then execute the exec resource, attempting to start a service that does not exist. However, this is not what we wanted.

Now that there is an explicit Exit 0, the previous non-terminating error is ignored, and the exec resource behaves as we wanted.

Update - Puppet PowerShell module, version 2.0

The previous section addresses version 1.0.6 of the Puppet PowerShell module. On 24 May, a new version was released which has made some huge performance improvements, and also fixed some bugs with error handling.

Performance Improvements

To give you some idea of the improvement, we can use the test manifest from the previous example and time how long it took to run. Of course, we can use PowerShell to test this:

produces

So the old module took 32 seconds, while the new module took 22 seconds — but that isn't the whole story. The baseline measures how long Puppet takes to just start up, so it's not even executing PowerShell then. Taking the baseline time away, the old module took 20 seconds (32 - 12) and the new module took 10 seconds (22 - 12).

So the new module is two times faster than the old one! Also, there's less disk IO and there's no temporary file on disk, so that security concern is gone.

Obviously the performance improvement you see will change depending on your scenario, but in essence, the more PowerShell exec resources are in your manifests, the greater the improvement.

Error handling

Error handling has changed slightly in the new module, so let’s see what happens when we run the previous test manifest. I've also included the non-terminating error with extra command tests, too.

PowerShell module version 1.0.6

PowerShell module version 2.0.0

So what's changed from version 1.0.6 to version 2.0?

  • The onlyif check non-term error test now runs the exec resource. This is the same behavior as onlyif check non-term error then command, and now treats all non-terminating errors the same — i.e., exit code zero.
  • The unless check non-term error no longer runs the exec resource. This is because the exit code is zero for non-terminating errors.

So when using the PowerShell module, remember:

  • Always use explicit exit codes.
  • Watch out for terminating errors, as the exec resource will treat them as non-zero exit codes.
  • Try to use the latest version of the PowerShell module, as it is faster and has improved error handling.

Glenn Sarti is a senior software engineer at Puppet.

Learn more

Puppet sites use proprietary and third-party cookies. By using our sites, you agree to our cookie policy.