Blog Stats
  • Posts - 24
  • Articles - 0
  • Comments - 18
  • Trackbacks - 0

 

Creating large SharePoint structures from Powershell and xml

In a previous post I outlined how we can create a site structure in SharePoint from some xml.

Let us expand on that example a bit, and show how we can build powerful functions that support real life SharePoint structures.

Many (most) of the Powershell SharePoint commands have arguments with default or optional values. For example, look at New-SPWebApplication:

Get-Help New-SPWebApplication

New-SPWebApplication -ApplicationPool <String> -Name <String> [-AdditionalClaimProvider <SPClaimProviderPipeBind[]>] [-AllowAnonymousAccess <SwitchParameter>] [-ApplicationPoolAccount <SPProcessAccountPipeBind>] [-AssignmentCollection <SPAssignmentCollection>] [-AuthenticationMethod <String>] [-AuthenticationProvider <SPAuthenticationProviderPipeBind[]>] [-Confirm [<SwitchParameter>]] [-DatabaseCredentials <PSCredential>] [-DatabaseName <String>] [-DatabaseServer <String>] [-HostHeader <String>] [-Path <String>] [-Port <UInt32>] [-SecureSocketsLayer <SwitchParameter>] [-ServiceApplicationProxyGroup <SPServiceApplicationProxyGroupPipeBind>] [-SignInRedirectProvider <SPTrustedIdentityTokenIssuerPipeBind>] [-SignInRedirectURL <String>] [-Url <String>] [-WhatIf [<SwitchParameter>]] [<CommonParameters>]

In my previous post I only used the arguments that had interest for me. We need a more general solution.

Creating an xml-parser that looks for these optional parameters, and then add them only if they exists, sounds like something I'd put a junior consultant to do. Or perhaps I should bite the bullet and do it the lazy (i.e. clever) way…

How can we expand the xml of our previous post to take advantage of these?

Easy; use HashTables and splats. Powershell treats hashtable arguments rather cleverly; it matches the keys against the allowed arguments.

Read the xml, and put all key- value pairs into a hashtable. Pass this hashtable to the get-spweb as a splat argument (i.e. using '@').

Let us do a spike first; create a hashtable, and pass it to get-spweb:

$splat = @{ Identity='http://server’; Confirm=$true }

Get-SPWeb @splat

Well, that worked. Next, let us write a tiny function to transmogrify xml to a hashtable.

function SimpleTransmogrificator($xml) # I would totally fire someone who uses names like this
{
  $splat = @{} # the splat - i.e. hashtable
   foreach ($attribute in $xml.Attributes) # from attributes to key-values
   {
      $splat.Add($attribute.Name, $attribute.Value)
   }
   return $splat
}

[xml]$structure = "<xml><test a='A' b='B' /></xml>"

SimpleTransmogrificator $structure.xml.test

Neat? I call Remove-SPWeb with a splat argument, and Identity and Confirm are used automatically.

I still like my previous approach; separating the data and the program - i.e. have the structure in an xml-file, and a powershell program that uses this file.

Although my spike test works, a bit of actual use will show us that this doesn't quite work for switch parameters. And we may want to add default arguments. For example, we may want to add a prefilled splat with Confirm:$false or TemplateId=STS#1. And we'll let any matching attributes in the xml-file overwrite these defaults.

function Transmogrificator($xml, $defaults)
{
   if ($defaults -ne $null) { # default arguments
      $splat = $defaults.Clone() # Clone it because we do not want to change the original
   } else {
      $splat = @{}
   }
   foreach ($attribute in $xml.Attributes)
   {
      if ($attribute -ne $null)
      {
         $val = $attribute.Value
         # Treat switch params special
         if ([System.String]::Compare("true", $attribute.Value, $true) -eq 0)
         {
            $val = $true
         }
         elseif ([System.String]::Compare("false", $attribute.Value, $true) -eq 0)
         {
            $val = $false
         }
         # add (or change an existing) value - it may already have a value through the defaults argument
         if ($splat.Contains($attribute.Name))
         {
             $splat[$attribute.Name] = $val
         }
         else
         {
            $splat.Add($attribute.Name, $val)
         }
      }
   }
   $splat
}

Wonderful.

Now we just need to traverse our xml-structure.

Pseudo:

Add solutions

  Activate farm features

  Foreach webapp

      If (alreadyexists) Delete existing

      New-Webapp

      Foreach contentDB

         New-spcdb

      Foreach siteColl

         New siteColl

         Resursively create webs

Some details, like feature activation, removed.

Well, that's the general idea. In my script I create the sitecoll in three steps, first I create the sitecoll, then I activate some features, and only then do I apply the web template. Why? Because web templates, done correctly, are site collection scoped. As a consequence, they need to be activated before they can be used.

When I traverse the xml-structure, I also take advantage of parent elements. For example, if the parent has a url of “http://server/site1,” if the child specifies “subsite1” as url, I’ll add this to “http://server/site1”. Otherwise there’s not much point in having a nice structure!

I also do the same with content databases by creating a defaults document based on setting from the web application they’ll be created under.

The second part, which shows how you can create the xml structure, can be found here.


Feedback

No comments posted yet.


Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 

 

 

Copyright © Norgean