PowerShell Transactions QuickStart

PowerShell Transactions QuickStart

  • Comments 8

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

Leave a Comment
  • Please add 4 and 6 and type the answer here:
  • Post
  • "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"

    Real world examples!  TONS of them :)  Powershell users (like myself) could benefit from seeing where transactions are the best solution in their everyday life (read: not _only_ SQL).  Feed me.

  • Wow, I am loving PSH.  Getting better by the day.

  • BTW - Why did you implement transaction cmdlet for Registry earlier than one for filesystem?

  • We implemented this for the Registry mainly because it is a smaller, more self-contained provider.

    It's best to introduce a complex concept on a relatively simple data store, rather than a complex concept on a complex data store.

    Lee

  • Hi, Thanks Lee.

    I was worried because transaction command disappeared when Vista reached RTM.

    I'm looking forward to your next introduction which is complex concept on a complex data store!

  • I see that New-PSDrive takes a useTransaction parameter.  How does this compare/contrast with the useTransaction parameter for, say, New-Item?

  • Not seeing the utility of this at first glance.

  • This blog posting is out-of-date, with many details having changed in the RTM version of Powershelll.   For better information about transactions in Powershell, see www.pavleck.net/.../ch30.html.

Page 1 of 1 (8 items)