Introduction to Transactions

The information in this article was written against the second Community Technology Preview (CTP2) of Windows PowerShell 2.0. This information is subject to change in future releases of Windows PowerShell 2.0.

An Introduction to Windows PowerShell Transactions

Transactions have long been a staple of the database world. With a transaction, you carry out a number of operations (e.g., changes to the database) without actually changing the database in any way. Why would you want to do that? Well, you’d want to do that if your end goal was all-or-nothing: you either want every single operation carried out (without errors) or you don’t want any of the operations carried out.

Take this scenario, for example. Suppose your company has decided to give everyone a 10 percent raise. (Hey, we just said it was a scenario; we didn’t say it was a realistic scenario.) Your accounting department might want to update the payroll database as a transaction. Why? Because that way they could ensure that each and every employee gets the 10 percent raise. If something goes wrong partway through the operation the accountants can simply roll back the transaction. Suppose you didn’t grant the raise as a transaction, and suppose that, midway through the process, something did go wrong. At that point you have a problem: now you have to figure out who got the raise and who didn’t. With a transaction, you can carry out the operation and verify that no errors occurred. If everything went fine then you can complete the transaction; if something went wrong, you can roll back the transaction, keeping the database in the same shape it was before you started, and giving you the opportunity to try again.

So why should you, a Windows PowerShell scripter, care about transactions? That’s easy: in the Windows PowerShell 2.0 of Windows PowerShell 2.0, the PowerShell team has introduced transactions to the system administration scripting world.

Now, before you get too excited we should note that PowerShell’s support for transactions is somewhat limited at the moment. The ability to carry out transactions must be built into the PowerShell provider, and as far as we know only the registry provider currently supports the use of transactions. At any rate, if you run the command Get-PSProvider only the registry provider claims to support the use of transactions:

Name                 Capabilities                            Drives
----                 ------------                            ------
Alias                ShouldProcess                           {Alias}
Environment          ShouldProcess                           {Env}
FileSystem           Filter, ShouldProcess                   {C, D, F, E}
Function             ShouldProcess                           {Function}
Registry             ShouldProcess, Transactions             {HKLM, HKCU}
Variable             ShouldProcess                           {Variable}
Certificate          ShouldProcess                           {cert}

No doubt this will change in the future, and more and more providers will offer the ability to use transactions. Until that time, however, let’s be thankful for what we have. And what we have is a registry provider that allows us to experiment with transactions and transaction processing.

Using Transactions with the Windows PowerShell Registry Provider

To better illustrate the basic idea behind transactions, let’s use a transaction to conditionally add a new value to the registry. Let’s assume we have a registry key named HKCU\Test, and that this key contains nothing more than the (Default) value:

You’re right; that is rather boring, isn’t it? So let’s liven things up a bit. In order to process commands as a transaction, we need to first create a new transaction; that’s what the Start-PSTransaction cmdlet is for:

Start-PSTransaction

Note. As far as we know, you can carry out only a single transaction at a time. However, you can carry out any number of operations within this one transaction.

When you start a transaction that transaction runs in the background, waiting for you to make explicit use of it. For example, suppose we use this command to add a new registry value named NontransactedValue to HKCU\Test:

New-ItemProperty -path HKCU:\Test -name NontransactedValue -value "This is a test."

Will this operation become part of our transaction? No, it will not. The command will run just fine, and the new registry value will be created. But the new registry value will be created immediately, without being stored as part of the transaction. And there will be no way to automatically roll back the creation of this new registry value.

So what went wrong here? Nothing; like we said, unless you specifically indicate that a command is supposed to be part of your transaction then that command won’t be part of your transaction; instead, it will just run like any old PowerShell command. If you want a command to be part of a transaction then you need to add the –useTransaction parameter, like so:

New-ItemProperty -path HKCU:\Test -name TransactedValue -value "This is a test." -useTransaction

This time around we’re adding a value named TransactedValue to HKCU:\Test (and, just for the heck of it, giving TransactedValue the value “This is a test.”). However, because we added the –useTransaction parameter, this command will not be carried out immediately; instead, it will be added to our transaction.

So what does happen when we run this command? To all outward appearances, nothing. For example, a quick peek at the registry shows that no new registry values have been added to HKCU\Test:

Likewise, running the Get-ItemProperty cmdlet against HKCU\Test returns nothing:

PS HKCU:\Test> Get-ItemProperty HKCU:\Test
PS HKCU:\Test>

Why aren’t we getting anything back? Because, at the moment at least, there isn’t anything to get back. We haven’t actually created our registry value, we’ve just added it to our transaction. Try running this command:

Get-ItemProperty HKCU:\Test -useTransaction

By adding the –useTransaction parameter Get-ItemProperty will now show us what HKCU\Test will look like if we choose to complete the transaction:

TransactedValue
---------------
This is a test.

See how that works? In a sense, we’re simply building a new version of HKCU\Test in memory. If we decide we like this new version we can complete the transaction, which will replace the original version of the registry key with the new one. If we decide we don’t like the new version then we can simply discard it.

Speaking of which, rolling back a transaction is as easy as calling the Undo-PSTransaction cmdlet. Do that, and your transaction will be deleted, and the registry key HKCU\Test will remain unchanged. If you’d prefer to complete the transaction, then call the Complete-PSTransaction cmdlet instead. When you do that you won’t see any dramatic changes onscreen, either:

PS HKCU:\Test> Complete-PSTransaction
PS HKCU:\Test>

But now take a look at the registry

Cool, huh?

If you want a slightly-fancier example, try running these two commands:

PS HKCU:\Test> Start-PSTransaction
PS HKCU:\Test> 1 .. 100 | ForEach-Object {New-ItemProperty -path HKCU:\Test -name $_ -useTransaction}

These commands will start a new transaction and create 100 new registry values as part of that transaction. You can use Get-ItemProperty (don’t forget the –useTransaction parameter) to view the transaction, and use Undo-PSTransaction and Complete-PSTransaction to rollback and complete the transaction, respectively.

Give it a try; after all, what do you have to lose?

Hey, that’s right: as long as you use a transaction then you’ve got nothing to lose, do you?