Transactions describe a system’s ability to support tentative or multistep changes. When you make changes within the context of a transaction, the system provides four main guarantees:
To observers not participating in the transaction, the commands inside the transaction have not impacted the system.
Once you decide to finalize (commit) a transaction, either all of the changes take effect or none of them do.
Errors caused during a transaction that would cause an inconsistent system state are dealt with in order to bring the system back to a consistent state.
Once the system has informed you of the transaction’s successful completion, you can be certain that the changes are permanent.
As a real-world example of a transaction, consider a money transfer between two bank accounts. This might happen in two stages: subtract the money from the first account, and then add the money to the second account. In this situation, you have the exact same goals for robustness and correctness:
While the money transfer is taking place (but has not yet completed), the balance of both bank accounts appears unchanged.
At some point in the process, it’s possible that we’ve subtracted the money from the first account but haven’t added it yet to the second account. When we process the money transfer, it’s critical that the system never show this intermediate state. Either all of the changes take effect or none of them do.
If an error occurs during the money transfer, the system takes corrective action to ensure that it is not left in an intermediate state. Perhaps it accounts for a lack of funds by adding an overdraft charge or by abandoning the money transfer altogether. It should not, for example, take the funds from one account without depositing them into the second account.
Once the money transfer completes, you don’t have to worry about a system error undoing all or part of it.
Although transactions are normally a developer topic, PowerShell exposes transactions as an end-user concept, opening a great deal of potential for consistent system management.
To start a transaction, call the Start-Transaction
cmdlet. To use a cmdlet
that supports transactions, specify the -UseTransaction
parameter. Being explicit about this parameter is crucial, as many cmdlets
that support transactions can work equally well without one. Because of
that, PowerShell lets the cmdlet participate in the transaction only when
you supply this parameter.
In Windows Vista and later, PowerShell’s registry provider supports transactions as a first-class concept. You can see this in action in Safely Combine Related Registry Modifications.
PS > Set-Location HKCU: PS > Start-Transaction PS > mkdir TempKey -UseTransaction Hive: HKEY_CURRENT_USER SKC VC Name Property --- -- ---- -------- 0 0 TempKey {} PS > New-Item TempKeyTempKey2 -UseTransaction Hive: HKEY_CURRENT_USERTempKey SKC VC Name Property --- -- ---- -------- 0 0 TempKey2 {} PS > Get-ChildItem TempKey Get-ChildItem : Cannot find path 'HKEY_CURRENT_USERTempKey' because it does not exist. PS > Complete-Transaction PS > Get-ChildItem TempKey Hive: HKEY_CURRENT_USERTempKey SKC VC Name Property --- -- ---- -------- 0 0 TempKey2 {}
Once you have
completed the transactional work, call either the Complete-Transaction
cmdlet to make it
final or the Undo-Transaction
cmdlet to discard the
changes. While you may now be tempted to experiment with transactions on
other providers (for example, the filesystem), be aware that only the
registry provider currently supports them.
You want to experiment with PowerShell’s transactions support but don’t want to use the Registry Provider as your playground.
Use PowerShell’s
System.Management.Automation.TransactedString
object
along with the Use-Transaction
cmdlet to experiment
with a string, rather than registry keys:
PS > Start-Transaction Suggestion [1,Transactions]: Once a transaction is started, only commands that get called with the -UseTransaction flag become part of that transaction. PS > PS > $transactedString = New-Object Microsoft.PowerShell.Commands.Management. TransactedString PS > $transactedString.Append("Hello ") PS > PS > Use-Transaction -UseTransaction { $transactedString.Append("World") } Suggestion [2,Transactions]: The Use-Transaction cmdlet is intended for scripting of transaction-enabled .NET objects. Its ScriptBlock should contain nothing else. PS > PS > $transactedString.ToString() Hello PS > PS > Complete-Transaction PS > PS > $transactedString.ToString() Hello World PS >
PowerShell’s transaction support builds on
four core cmdlets: Start-Transaction
,
Use-Transaction
,
Complete-Transaction
, and
Undo-Transaction
.
The Start-Transaction
begins a transaction, creating a context where changes are visible to
commands within the transaction, but not outside of it. For the most
part, after starting a transaction, you’ll apply commands to that
transaction by adding the -UseTransaction
parameter
to a cmdlet that supports it. For example, when a PowerShell provider
supports transactions, all of PowerShell’s core cmdlets
(Get-ChildItem
, Remove-Item
, etc.)
let you specify the -UseTransaction
parameter for
actions against that
provider.
The Use-Transaction
cmdlet
is slightly different. Although it still requires the
-UseTransaction
parameter to apply its script block
to the current transaction, its sole purpose is to let you script
against .NET objects that support transactions themselves. Since they
have no way to supply a -UseTransaction
parameter,
PowerShell offers this generic cmdlet for any type of transactional .NET
scripting.
Other transaction-enabled cmdlets should
not be called within the Use-Transaction
script
block. You still need to provide the -UseTransaction
parameter to the
cmdlet being called, and there’s a chance that they might cause
instability with your PowerShell-wide transactions.
To give users an opportunity to play with something a little
less risky than the Windows Registry, PowerShell includes the
Microsoft.PowerShell.Commands.Management.TransactedString
class. This class acts like you’d expect any transacted command to act
and lets you become familiar with how the rest of PowerShell’s
transaction cmdlets work together. Since this is a .NET object, it must
be called from within the script block of the
Use-Transaction
cmdlet.
Finally, when you are finished performing
tasks for the current transaction, call either the
Complete-Transaction
or the
Undo-Transaction
cmdlet. As compared to the solution,
here’s an example session where the Undo-Transaction
cmdlet lets you discard changes made during the transaction:
PS > Start-Transaction Suggestion [1,Transactions]: Once a transaction is started, only commands that get called with the -UseTransaction flag become part of that transaction. PS > PS > $transactedString = New-Object Microsoft.PowerShell.Commands.Management.Tra nsactedString PS > $transactedString.Append("Hello ") PS > PS > Use-Transaction -UseTransaction { $transactedString.Append("World") } Suggestion [2,Transactions]: The Use-Transaction cmdlet is intended for scripting of transaction-enabled .NET objects. Its ScriptBlock should contain nothing else. PS > PS > $transactedString.ToString() Hello PS > PS > Undo-Transaction PS > PS > $transactedString.ToString() Hello
For more information about transactions in the Windows Registry, see Safely Combine Related Registry Modifications.
Use the -RollbackPreference
parameter of the Start-Transaction
cmdlet to control
what type of error will cause PowerShell to automatically undo your
transaction:
HKCU: >Start-Transaction HKCU: >New-Item Foo -UseTransaction Hive: HKEY_CURRENT_USER SKC VC Name Property --- -- ---- -------- 0 0 Foo {} HKCU: >Copy IDoNotExist Foo -UseTransaction Copy-Item : Cannot find path 'HKCU:IDoNotExist' because it does not exist. HKCU: >Complete-Transaction Complete-Transaction : Cannot commit transaction. The transaction has been rolled back or has timed out. HKCU: >Start-Transaction -RollbackPreference TerminatingError Hive: HKEY_CURRENT_USER SKC VC Name Property --- -- ---- -------- 0 0 Foo {} HKCU: >Copy IDoNotExist Foo -UseTransaction Copy-Item : Cannot find path 'HKCU:IDoNotExist' because it does not exist. HKCU: >Complete-Transaction HKCU: >Get-Item Foo Hive: HKEY_CURRENT_USER SKC VC Name Property --- -- ---- -------- 0 0 Foo {}
Errors in scripts are an extremely frequent cause of system inconsistency. If a script incorrectly assumes the existence of a registry key or other system state, this type of error tends to waterfall through the entire script. As the script continues, some of the operations succeed while others fail. When the script completes, you’re in the difficult situation of not knowing exactly what portions of the script worked correctly.
Sometimes running the script again will magically make the problems go away. Unfortunately, it’s just as common to face a painstaking manual cleanup effort.
Addressing these consistency issues is one of the primary goals of system transactions.
When PowerShell creates a new transaction, it undoes (rolls back) your transaction for any error it encounters that is operating in the context of that transaction. When PowerShell rolls back your transaction, the system impact is clear: no part of your transaction was made permanent, so your system is still entirely consistent.
Some situations are simply too volatile to
depend on this rigid interpretation of consistency, though, so
PowerShell offers the -RollbackPreference
parameter
on the Start-Transaction
to let you configure how it
should respond to errors:
Error
PowerShell rolls back your transaction when any error occurs.
TerminatingError
PowerShell rolls back your transaction only when a terminating error occurs.
Never
PowerShell never automatically rolls back your transaction in response to errors.
For more information about PowerShell’s error handling and error levels, see Chapter 15.
3.147.85.181