free html hit counter
Posted on: Tuesday, 13 February 2007 by Rajiv Popat

A couple of months ago I had to make a difficult choice. A choice between Vista and Powershell (I still need time getting used to this name:)). Vista had officially RTM'ed and Powershell for Vista wasn't available. It was a difficult choice - wait on XP/2003 and continue to use Powershell or Move to Vista and wait for Powershell to become Vista Ready. I decided to move to Vista.

Recently, after about a couple of months, exactly as promised by Jeffrey Snover, the Moand team has Vista'ed the Powershell Installer and that means I can have both Vista and Powershell now. To add to that, I can totally continue to Skin Powershell and make it look beautiful, just like my Vista. And that's very important! Right? :)

I've been playing a lot with Powershell and now I think it's time for me, to put it to some real use, in project deployments on QA / Production.

One of the problems I always had while writing anything about PowerShell is that there are just too many good things to talk about in there and almost anything that could have been written about, has been written by someone else. That's one of the reasons (excuses? :)) why I haven't been writing a lot about it. For now, I'm just going to start by talking a little bit about XML and how you can read XML files in Powershell.

Let's just assume that we have the following simple XML file which lets me run various Uninstall and Install SQL scripts on the database depending on the Application version that I am pushing to the server.


<DBScripts>
<version name="1.0.0">
<script UnInstallScript="uninstall1-1.sql" InstallScript="install1-1.sql"/>
<script UnInstallScript="uninstall1-2.sql" InstallScript="install1-2.sql"/>
</version>
<version name="2.0.0">
<script UnInstallScript="uninstall2-1.sql" InstallScript="install2-1.sql"/>
<script UnInstallScript="uninstall2-2.sql" InstallScript="install2-2.sql"/>
</version>
</DBScripts>
 

Typically, creating a database project, a command file and then a custom Console application in C# that reads this XML file and fires corresponding SQL Scripts might work, but it takes some time. Let's use Powershell to see how easy it is to read this XML.

Let's start with simplest approach first. XML support is pretty neatly baked into Powershell. So assuming this file was called Mockfile.xml, I can just create a XmlDocument object using the following command at my Powershell window:


PS E:\> [xml] $MyXmlDocument = Get-Content MockFile.xml
 

That's it. The XMLDocument is now available in this instance variable. Let's take a look at what the variable $MyXmlDocuent contains:


PS E:\> Get-Variable MyXmlDocument

NameValue Value
---- -----
MyXmlDocument #document

PS E:\> $MyXmlDocument

DBScripts
---------
DBScripts
 

We're all set to read from the document now. Let's try to get to various nodes on the XML File. Because, everything returned by Powershell is an object, to get the value of the Node all I Need to do is access the attribute within the object. For Example, to get all values of version nodes inside the the DBScripts Node and store it inside a separate variable I could just do:


PS E:\> $MyVersions = $MyXmlDocument.DBScripts
PS E:\> $MyVersions

version
-------
{1.0.0, 2.0.0}
 

Notice how I access the DBScripts Node and assign all version nodes inside the root node to a different variable. To see value value of $MyVersions variable that I just created I just type it in and Powershell confirms that it now has nodes pertaining to both versions in this newly created variable.

Now let's assume I need to reach the version node with the Name 2.0.0. and access all the script files in there. To do this let's quickly take a look at the version node collection which was returned by the above command:


PS E:\> $MyVersions.version

name script
---- ------
1.0.0 {script, script}
2.0.0 {script, script}

Now to directly access the version node where the name is 2.0.0, let's use PowerShell filtering and let's pipe the output of version nodes to the the Where-Object commandlet:


PS E:\> $SecondVersion = $MyVersions.version | where-object {$_.name -match '2.0.0'}
PS E:\> $SecondVersion

name script
---- ------
2.0.0 {script, script}

PS E:\> $SecondVersion.script

UnInstallScript InstallScript
-------------- -------------
uninstall2-1.sql install2-1.sql
uninstall2-2.sql install2-2.sql

Once I've piped the output of all my version attribute to the where-object commandlet and assigned the final output to another variable I inspect the attribute the variable contains by typing $SecondVersion and then peek into it's script collection by doing $SecondVersion.script. If this was a deployment script, the above command would have given me all the scripts I need to run to install the version 2.0.0 of my application. I could easily convert this to a Powershell script where the version number comes in a parameter. The script can go ahead and figure out which SQL script it needs to run and then can run them. However, the focus of this Post is to concentrate of reading XML files, so let's try to do everything we just did using just two lines of code:


PS E:\> [xml] $MyXmlDocument = Get-Content MockFile.xml
PS E:\> ($MyXmlDocument.DBScripts.version | where-object {$_.name -match '2.0.0'}).script

UnInstallScript InstallScript
-------------- -------------
uninstall2-1.sql install2-1.sql
uninstall2-2.sql install2-2.sql

Sweet! Don't you think? :) Wait, there's more! You could actually do this using a single command at the command prompt if you wanted to:


PS E:\> (([xml] (Get-Content MockFile.xml)).DBScripts.version | Where-Object {$_.name -match '2.0.0'}).script

UnInstallScript Installscript
-------------- -------------
uninstall2-1.sql install2-1.sql
uninstall2-2.sql install2-2.sql

And then of course you could iterate through this list by using the ForEach-Object commandlet if you wanted to:


PS E:\> (([xml] (Get-Content MockFile.xml)).DBScripts.version | Where-Object {$_.name -match '2.0.0'}).script | ForEach-
Object { "Here I Could Run : " + $_.InstallScript }

Here I Could Run : install2-1.sql
Here I Could Run : install2-2.sql

PS E:\>
 

Or you could just access a particular element by it's zero based index:


PS E:\> (([xml] (Get-Content MockFile.xml)).DBScripts.version | Where-Object {$_.name -match '2.0.0'}).script[1]

UnInstallScript InstallScript
-------------- -------------
uninstall2-2.sql install2-2.sql

PS E:\>

And did I mention that two code snippets above are just one line commands? You could of-course turn this into a script depending on what you are trying to do. But what if you were not very comfortable with this approach and wanted to use familiar XPath to access XML nodes? Don't run away! You can do that too. This MSDN article shows you how.

Here's wishing you Happy-XML with Powershell!