PowerShell Transactions QuickStart

The second CTP of PowerShell V2 (CTP2) introduces full engine support for transactions – groups of actions that can be finalized or undone in an all-or-nothing way. Wikipedia gives a great overview of transactions here: http://en.wikipedia.org/wiki/Database_transactions.


 


We put a ton of thought into how to expose this normally developer-centric concept in a way suitable for system administration and system administrators. We would LOVE to hear your feedback on what concepts or behaviours make sense, and especially which ones do not.


 


We didn’t get a chance to fully document these cmdlets in the CTP2, though, so here’s a quick start and primer to help you explore the feature.


 


Using transactions


PowerShell surfaces its support for transactions through the following cmdlets:



PS C:\Temp> gcm *transaction*


 


CommandType     Name


———–     —-


Cmdlet          Complete-PSTransaction


Completes / Commits a transaction


 


Cmdlet          Start-PSTransaction


Begins a transaction


 


Cmdlet          Undo-PSTransaction


Rolls back a transaction


 


Cmdlet          Use-PSTransaction


Places the current PowerShell transaction in Transaction.Current, for direct .NET Scripting against transacted objects.


 


To start a transaction, call the Start-PSTransaction cmdlet. To use a cmdlet that supports transactions, call it with 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 only surfaces the transaction to the cmdlet when you supply this parameter.


PowerShell’s registry provider supports transactions on Vista. In addition, a utility class called System.Management.Automation.TransactedString supports transactions on all platforms.


Once you have completed the transactional work, call the Complete-PSTransaction cmdlet to make it final, or the Undo-PSTransaction cmdlet to discard the changes.


 


Here is an example session that illustrates these concepts:



PS C:\Users\leeholm> cd hkcu:\temp


PS HKCU:\temp> dir


PS HKCU:\temp> Start-PsTransaction


 


## Create a key. We didn’t specify the –UseTransaction parameter, so it is not being done in a transaction.


PS HKCU:\temp> New-Item WillStayBehind


 


 


   Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\temp


 


SKC  VC Name                           Property


  – —-                           ——–


  0   0 WillStayBehind                 {}


 


 


## Create a key in the transaction


PS HKCU:\temp> New-Item WillGetRolledBack -UseTransaction


 


 


   Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\temp


 


SKC  VC Name                           Property


  – —-                           ——–


  0   0 WillGetRolledBack              {}


 


 


## Get-ChildItem from outside of the transaction. You don’t see any of your transacted changes.


PS HKCU:\temp> Get-ChildItem


 


 


   Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\temp


 


SKC  VC Name                           Property


  – —-                           ——–


  0   0 WillStayBehind                 {}


 


 


## Get-ChildItem from inside of the transaction. Your transacted changes are now visible.


PS HKCU:\temp> Get-ChildItem -UseTransaction


 


 


   Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\temp


 


SKC  VC Name                           Property


  – —-                           ——–


  0   0 WillStayBehind                 {}


  0   0 WillGetRolledBack              {}


 


 


## Now, some transacted .NET scripting against the TransactedString object


PS HKCU:\temp> $transactedString = New-Object System.Management.Automation.TransactedString


PS HKCU:\temp> $transactedString.Append(“Hello “)


 


## Append some text in the transaction.


PS HKCU:\temp> Use-PsTransaction -UseTransaction { $transactedString.Append(“World”) }


 


## From outside the transaction, the changes are not visible


PS HKCU:\temp> $transactedString.ToString()


Hello


 


## But from within the transaction, they are


PS HKCU:\temp> Use-PsTransaction -UseTransaction { $transactedString.ToString() }


Hello World


 


## Roll back the transaction


PS HKCU:\temp> Undo-PsTransaction


 


## Look at the registry, and only our non-transacted changes are there.


PS HKCU:\temp> Get-ChildItem


 


 


   Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\temp


 


SKC  VC Name                           Property


  – —-                           ——–


  0   0 WillStayBehind                 {}


 


 


PS HKCU:\temp> Get-ChildItem -UseTransaction


Get-ChildItem : Cannot use transaction. No transaction has been started.


At line:1 char:14


+ Get-ChildItem <<<<  -UseTransaction


PS HKCU:\temp> $transactedString.ToString()


Hello


PS HKCU:\temp>


 


Developing for transactions – Cmdlet and Provider declarations


Cmdlets declare their support for transactions in a similar way that they declare their support for ShouldProcess:


[Cmdlet(“Get”, “Process”, SupportsTransactions=True)]


 


Providers declare their support for transactions through the ProviderCapabilities flag:


[CmdletProvider(“Registry”, ProviderCapabilities.Transactions)]


 


Developing for transactions – Participating in the PowerShell transaction


The PowerShell engine exposes transactions in a way that lets developers implement a transacted provider or cmdlet in the same way that they implement other transacted code.


The recommended pattern for developing isolated transacted code in traditional applications is this: http://msdn2.microsoft.com/en-us/library/ms229973.aspx


using(TransactionScope scope = new TransactionScope())


{


   … // Perform transactional work here


 


   // No errors – commit transaction
   scope.Complete();


}


 


This is called implicit transaction management, and the code in the code block represents the entirety of the transacted operation. The TransactionScope object handles management of the ambient transaction, committing and rolling it back as necessary.


Since cmdlet and provider developers should not deal directly with transaction scopes (but still be able to easily port transacted code,) PowerShell gives a similar experience:

using(CurrentPsTransaction)
{
   … // Perform transactional work here
}

 


The CurrentPsTransaction property returns an IDisposable object. This IDisposable object, through the System.Transactions explicit programming model (http://msdn2.microsoft.com/en-us/library/ms172146.aspx), sets the current ambient transaction to be the current PowerShell Transaction. Its dispose method restores the previous ambient transaction. Attempting to use this current transaction while no transactions are active generates an error. The other transaction cmdlets control the nesting and lifetime of these transactions.


This CurrentPsTransaction experience differs from the C# TransactionScope experience in two ways:


1)     The object returned by the CurrentPsTransaction property does not actually represent a transaction. It does not support a Complete() method, Rollback() method, or anything else. The user is in charge of these decisions, not the cmdlet or provider developer.


2)     The transaction persists beyond the end of the code block, as multiple cmdlets can participate in it. Although the transaction persists outside of the code block, it is not active / ambient outside of the code block. A cmdlet or provider author can easily write the following code to tightly control which operations are transacted:

using(CurrentPsTransaction)
{
   … // Perform transactional work here
}
 
… // Perform non-transacted work here
 
using(CurrentPsTransaction)
{
   … // Perform more transactional work here
}
 

 


Using the same pattern PowerShell has established for the ShouldProcess functionality, cmdlet and provider authors should check for an existing transaction if they can operate without one:

if(TransactionAvailable())

{

    using(CurrentPsTransaction)
    {
       … // Perform transactional work here
    }

}

 


If the cmdlet or provider cannot operate without a transaction, they can simply use the CurrentPsTransaction property. If no transaction is available, PowerShell automatically generates an error.


 



Lee Holmes [MSFT]


Windows PowerShell Development