Wednesday, October 5, 2011

Editing a web.config using PowerShell–adding a node method 1

This is an extension of a recent bunch of posts about XML and handling XML in PowerShell.

One way of adding a node and content can be accomplished by building the node out of XML objects.  Some might consider the the true ‘object’ way of working through this problem.

The bit of XML that I want to add to my web.config is:

<location path="FederationMetadata">
  <system.web>
    <authorization>
      <allow users="*" />
    </authorization>
  </system.web>
</location>

This is a walkthrough of building that as pure XML objects using PowerShell.  And then inserting it into a very specific location in the larger XML document.

First, I have to load in the web.config and make it an XML document. 

$file = get-item -path $path

$xml = [xml](get-content $file)

Now, Create the ‘location’ node as an XML document.

$federationLocation = $xml.CreateElement("location")

Set the attribute of ‘path’

$federationLocation.SetAttribute("path", "FederationMetadata") 

Create a nested node ‘system.web’

$fedSystemWebNode = $xml.CreateElement("system.web") 

Create a nested node ‘authorization’

$fedAuthNode = $xml.CreateElement("authorization")

Create the ‘allow’ element

$fedAllowNode = $xml.CreateElement("allow")

Add the ‘users’ attribute

$fedAllowNode.SetAttribute("users", "*") 

Now, for the really fun part.  And the important part to pay attention to.

Reassemble the nodes in the correct order

Begin by appending the inner mode node to its parent

$fedAuthNode.AppendChild($fedAllowNode)

Repeat with the next node and its parent

$fedSystemWebNode.AppendChild($fedAuthNode)

Repeat the process again

$federationLocation.AppendChild($fedSystemWebNode) 

Now, I want to insert the XML document Node into a very specific place in the web.config XML configuration file.  That location is before the XML path ‘configuration/system.web’.

$xml.configuration.InsertBefore($federationLocation, ($xml.configuration.Item("system.web")))

That is it.  lots of lines. But performed in a true object style.

Oh, and don’t forget to save back to the file system.

$xml.Save($file.FullName)

Next time, the quick and dirty string method.

Monday, October 3, 2011

Editing a web.config using PowerShell–changing a value

It has been some time since I have been working with XML using PowerShell.  I needed to refresh my brain and work through some issues.

In bunches of searching I ran across only one article that really tries to explain what is going on: http://www.codeproject.com/KB/powershell/powershell_xml.aspx

And coupling this understanding with Tobias Weltner’s chapter on XML starts to put the pieces together:  http://powershell.com/cs/blogs/ebook/archive/2009/03/30/chapter-14-xml.aspx

And, there are clues here: http://devcentral.f5.com/weblogs/Joe/archive/2009/01/27/powershell-abcs---x-is-for-xml.aspx and here: http://blogs.technet.com/b/heyscriptingguy/archive/2010/07/29/how-to-add-data-to-an-xml-file-using-windows-powershell.aspx

I finally sat down with a senior developer, who has always been very helpful, and he finally helped me wrap my brain around manipulating XML using PowerShell.  A bit of shared screen time and he could quickly recognize what PowerShell was doing to the XML document in handling each section.

This has prompted me to attempt to capture this understanding before I get pulled to a new project and I forget it all.

This is part 1 of a series of handling XML in PowerShell.  I decided to tackle something really simple.  Editing a value. 

The ‘partial’ XML kind of way.  I call this partial because I am only using XML handling to find the collection, not to modify it.  But this is a quick trick that I have used quite a bit to modify settings and configuration files.

This is an Azure related example, and in Azure there are no hard paths, so we keep things relative.

# Discover the path of the web.config file

$path = (( Get-Website | where {$_.Name -match ($role.Id)} ).PhysicalPath + "\Mine\Site\web.config")
$file = get-item -path $path

# Get the content of the config file and cast it to XML to easily find and modify settings.

$xml = [xml](get-content $file)

I want to edit the following line in the file:  <routingPolicy alternateFoo="off" />  I want to change the value to “on”

$routePolicy = $xml.GetElementsByTagName("routingPolicy")

This returns an object, $routePolicy.  And looking at that object I see no methods, but it does have a count property.  I actually got a collection back (or an array), not some XML element.

One way to modify this is to go along with this being an array and simply step through it taking advantage of the  dot tag type of XML browsing.

foreach ($e in $routePolicy) {
    if ($e.alternateFoo -eq "off" ) {
    $e.alternateFoo = "on"
    }
    $e
}

That is what I consider the really simple way.  It is quick and dirty and gets the job done.

Now, what I just did is not very “object oriented” which is the power of PowerShell.  So, how do I get in there and change this setting.  What is it really.

The way this setting is defined is that alternateFoo is an attribute of  <routingPolicy />.  It is not a node or an element within it.

Here is where things get really funky.  The following will modify that attribute without having to do the loop.  The important part is that each step of the way we are selecting each node as an individual object and therefore its own XML document within the larger XML document.  The difference here is that I have to know the path.  “routingPolicy” is a node under “system.web”, which is itself a node of the larger “configuration”. 

$xml.configuration.Item("system.web").Item("routingPolicy").SetAttribute("alternateFoo", "on")

Easier to code, but more difficult to understand what is really happening.  It is this stuff that is really not well written out there.

Oh, don’t forget to save the file back to the file system.

$xml.Save($file.FullName)

Thursday, September 8, 2011

The Cloud–a place for my stuff

I keep trying to come up with new ways to describe clouds and what it means to run things in the cloud and to do things in the cloud.

To quote a really old George Carlin skit; a place for my stuff.  “That is the whole meaning of life, to find a place for my stuff”.

In the cloud world there are private places and public places.  A private cloud is a cloud that you are responsible for.  A public cloud is a cloud that someone else is responsible for.

The “location” of your cloud infers the physical location and who owns and manages that physical location.  If it is private, then you own or manage that physical location.  If it is public then someone else does.

In the end.  A cloud is not a new thing, but a new word describing an old thing.  It describes infrastructure.  The new thing is how you interact with that infrastructure – this is where underwear gets all knotted up.

When moving to clouds – someone loses control.  And it is that change in control that things someone other than me is messing with my stuff.

Personally, I love my SkyDrive.  Cloud based storage that I can interact with from anywhere.  A place for my document and picture (files) stuff.

I also like Gmail and Hotmail.  Most folks don’t think of these as cloud either, but they are.  Because they are off, somewhere else.  And, they are a place for my email stuff.

I like Azure.  It is my cloud based infrastructure.  I don’t have to worry about controlling it, because Azure does that.  I just tell it to run my stuff and it does.  A place for my workload stuff.

So, for me.  I have a place for my stuff.  I know where that physical place is.  Coming form the IT Pro background I have a need to know that physical place to link my stuff with a location, even if that location is a service.

But, I just feel good that I have a place for my stuff.  And that I can get to my stuff.  From any of my other stuff.  And I don’t have to manage all aspects of my stuff.

Tuesday, September 6, 2011

How to ask a question in a technical forum

A friend of mine recently ran across this and forwarded it to me.

Personally, as a TechNet forum moderator, and frequent forum contributor, and as a person who handles a few forums for my employer; I find the information in this KB both lighthearted and highly useful at the same time.

http://support.microsoft.com/kb/555375

The title of the KB article:  “How to ask a question”

Please, check it out.

Thank you Daniel Petri!

Thursday, September 1, 2011

PowerShell to select a certificate and encrypt a password

Here is a quick little script to encrypt a password with PowerShell.  Yes, it requires the user to select a certificate and enter the password but that could be easy to change.. 

I have found this very handy when encrypting passwords for use in Azure Role settings, such as Azure Connect.  Secure String does not work as in Azure my local machine keys are not available, however I can use a Service Certificate to encrypt on my end and therefore decrypt on the Azure end.

I have one assumption – that the certificate is in the LocalMachine Personal certificate store.

$password = Read-Host -Prompt "Enter the password to encrypt"

$certs = dir cert:\LocalMachine\My
[System.Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
$collection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$certs | ForEach-Object { $collection.Add($_) } | Out-Null
$cert = [System.Security.Cryptography.x509Certificates.X509Certificate2UI]::SelectFromCollection($collection, "", "Select a certificate", 0)
$thumbprint = $cert[0].thumbprint
$pass = [Text.Encoding]::UTF8.GetBytes($password)
$content = new-object Security.Cryptography.Pkcs.ContentInfo -argumentList (,$pass)
$env = new-object Security.Cryptography.Pkcs.EnvelopedCms $content
$env.Encrypt((new-object System.Security.Cryptography.Pkcs.CmsRecipient(gi cert:\LocalMachine\My\$thumbprint)))
write-host "Writing encrypted password, cut/paste the text below the line to CSCFG file"
[Convert]::ToBase64String($env.Encode()) | Out-File .\encrypted_password.txt
Invoke-Item ".\encrypted_password.txt"