PowerShell Part 2 – using the SharePoint OM

This is part 2 in my series on PowerShell and in this article we’re going to take a look at how to exploit the SharePoint Object Model through PowerShell. Having access to the out of the box cmdlets, like New-SPWebApplication etc, is really helpful but there’s a limit to what you can do with them. Creating a Farm, getting a Web Application and Site Collection up and running is fairly straight-forward with the standard cmdlets. However, when you start diving deeper into a site there’s not much out of the box stuff available. This is where, if I put my developer hat on, I jump in and start looking at writing custom scripts. Once you start writing your own scripts there’s no limit really as to how far you can take them. I’ve worked on projects where the PowerShell scripts are 4 lines to deploy solutions and then other projects where PowerShell has its own project within Visual Studio and can generate the entire site collection including content! If you work in development teams on a project and/or have to deploy out the same things to multiple environments (dev, UAT, Stating, Production) then PowerShell could well become your best friend.

So how do you get started?

Well the first thing – can you do it with the out of the box cmdlets? Why spend time writing a custom script to create a new Web Application, when there’s a script that does it for you! So let’s assume then that we have a scenario where there is no out of the box cmdlet. The next step is to identify what the script needs to do. You can (and should) apply the same principles to scripting that you do for coding – identify methods, process flows, identify parameters etc. Just because you don’t compile the scripts doesn’t mean to should take any less time to plan them! If you’re new to PowerShell you might find it easier to write the code out in C# first. Once you have that code ready  it doesn’t take too much to convert it to PowerShell.

In this post we’re going to look at writing a PowerShell function that we can use to add items to a custom list. Now in a lot of scenarios you may use an Event Receiver to achieve the same result – if you have in the past then that’s great because you’ll have the C# code you can use as a basis for the PowerShell. A question I get asked a lot is why would I script something rather than code it? Adding items to a list is a good example because a lot of the people would use an Event Receiver. There isn’t a right or a wrong way – it’s just different ways to do the same thing. Sometimes you won’t have a choice – client won’t grant you access to their server so you can’t run scripts, clients can’t provide data until after final code release etc. When there is a choice, then it’s down to preference. I like distingushing between code (e.g. C# logic), configuration (e.g. authentication method) and content (e.g. pages. lists) and being able to manage them independently – why should a code release be needed to update items in a list? Even if you disagree with that view, PowerShell is still worth looking at!

Now on with the script!

Part 1- Creating the list

First of all I want to script the creation of my custom list. For this example I’ve got a simple list definition in a WSP, CompanyContactList, which let’s me fill in some information like Company, Contact Name, Telephone, Fax and Postcode (You can download the WSP at the bottom of the page). My PowerShell script needs to check if my list exists first and if it doesn’t, create it for me based on a custom List Template that I’ve deployed to my site. To help get started it’s helpful sometimes to think how you would go about this in C#. Let’s take a look at what that might look :

var spSite = new SPSite("http://example");
var web = site.RootWeb;
var companyList = web.Lists.TryGetList("Company Contacts");

if (companyList == null)
{
var companyListTemplate = web.ListTemplates["CompanyContactsList"];
if (companyListTemplate != null)
{
web.Lists.Add("Company Contacts", "Contains main Company Contact information for site", companyListTemplate);
}
}

Hopefully this code should be familiar and you should get the idea of what this code is trying to do – first just get the RootWeb (that’s where I want this list to be created) and then using the TryGetList method I can check to see if a list with the name ‘Company Contacts’ already exists. If it doesn’t then it’s a case of getting the List Template and passing through the information to SPWeb.Lists.Add(). So let’s see how this would look in :

$site = New-Object Microsoft.SharePoint.SPSite("http://example")
$web = $site.RootWeb
$companyList = $web.Lists.TryGetList("Company Contacts")

Here we have the first 3 lines of our script. Really there have only been two changes:

  • Replacing all of the variable names to include the $
  • Replacing ‘new’ with ‘New-Object’ with the full namespace to SPSite

Let’s look at the next bit of the script, checking the companyList object:

if (companyList -eq $null)
{
$companyListTemplate = web.ListTemplates["CompanyContactsList"]
if ($companyListTemplate -neq $null)
{
#}
}

Again there hasn’t been that much to change from the C#:

  • Replacing ‘==’ with ‘-eq’
  • Replacing ‘null’ with ‘$null’
  • Replacing variables names to include $

And our final line just requires us to update the variable names. Technically speaking we now have valid and working PowerShell script to create out list. However, just as you would review you C# code to see if there’s any improvements, you should also review your scripts. For example, there may be things in PowerShell that you can exploit – like where, select etc which might improve your scripts. Another thing I find that’s useful is to include some logging information so you have some indication as to what’s going on. Logging doesn’t just have to be for diagnostics as it can be helpful for providing some reporting (There’ll be more of this in a future post!). The easiest way to do this is to provide some Write-Host to output a message.

So factoring all of this in, gives us the final script:

$web= Get-SPWeb "http://example"
$companyList = $web.Lists.TryGetList("Company Contacts")

if ($companyList -eq $null)
{
$companyListTemplate = $web.ListTemplates["CompanyContactsList"]
if ($companyListTemplate)
{
$web.Lists.Add("Company Contacts", "Contains main Company Contact information for site", $companyListTemplate);
Write-Host "Creating List"

}
else
{
Write-Host "List Template does not exist"

}
}
else
{
Write-Host "List already exists"
}

The final tweaks includes -

  • PowerShell gives us Get-SPWeb, so I’ve swapped that out from our original code
  • Rather than ‘$companyListTemplate -neq $null’ I can just check to see if ‘$companyListTemplate’ is set
  • Finally, adding various Write-Host to provide some information about what parts of the script have run

Part 2 – Populating the List

So now the list has been created I want to populate it with some data. This time however, rather than starting with the C# we’re going to jump right in to the script. Eventually I want this to be a function that I can call as required and it needs to be able to check if a value already exists before adding (I don’t want a list full of duplicates!). However, in this first iteration of the script I’m just going to add a new item to the list with the data so that we’ve got something to work with.

So here’s the first iteration of the script:

$web = Get-SPWeb "http://example"
$companyList = $web.Lists.TryGetList("Company Contacts")

if ($companyList)
{
$item = $companyList.Items.Add()
$item["Title"] = "Example 1"
$item["Contact_Name"] = "Example 1 - Name"
$item["Contact_Tel"] = "Example 1 - Tel"
$item["Contact_Fax"] = "Example 1 - Fax"
$item["Contact_Postcode"] = "Example 1 - Postcode"
$item.Update()
}

This should look a bit more familiar now – the first few lines focus on getting the list instance from the web and once we have our list we can call out to Items.Add() to create a new list item which can be be populated with data. If you ignore the $ this part of the script is then the same the C#. (By the way, Items.Add() in C# or PowerShell isn’t necessarily the best approach to use when adding items. When lists grow in size this becomes less performant – I’m using it here as a simple example).

If I’m only ever adding a single item then this script is fine, but it’s not really that reusable. So the first enhancement that I want to do it to make it a function. How you go about this is the same as if you were writing this as a method in C# – what do you want to turn into parameters? Clearly each of our fields (Title, Contact_Tel etc) should be parameters, but we also need to have access to the list as well. Here is where there’s a couple of options, for example do we:

  • Pass in the List object?
  • Pass in the Web object?
  • Pass in Web Url?

For this example I’m going to get the List object and pass that in as a parameter to my function, which means everything below the line ‘if ($companyList)’ is going to be added to my function. Now the parameters have been identied I can modify the script so that it looks like this:

$web = Get-SPWeb "http://example"
$companyList = $web.Lists.TryGetList("Company Contacts")

function Add-CompanyContact([Microsoft.SharePoint.SPList]$list, [string]$company, [string]$name, [string]$tel, [string]$fax, [string]$postcode)
{
if ($list)
{
$item = $list.Items.Add()
$item["Title"] = $company
$item["Contact_Name"] = $name
$item["Contact_Tel"] = $tel
$item["Contact_Fax"] = $fax
$item["Contact_Postcode"] = $postcode
$item.Update()
}
}

We now have a reusable function that we can call and pass in information as we need it – something like this:

Add-CompanyContact -list $companyList -company "Title 2" -name "Name 2" -tel "Tel 2" -fax "Fax 2" -postcode "Postcode 2" 

The final enhancement that I want to make is to do a check before we add a new item and to do this I’m going to write an SPQuery to see if an item already exists in the list with the same Title. If no item is found then the item will be added. Let’s take a look at the script and then see what it is doing -

$query = New-Object Microsoft.SharePoint.SPQuery
$query.Query = [string]::Format("{0}", $company)

$results = $list.GetItems($query)

if ($results.Count -eq 0)
{
# Same code as before
}

First of all we are building up our SPQuery object – first by instantiating it and then by building the Query. Since the Query is dynamic, based on the $company parameter I’m using [string]::Format to build it. Then it is just the case of passing the $query object in the GetItems method and we have the results. I’m not really interested in the results, except to get the count. It’s at this point (if the count is 0) that the original code can be used.
Let’s see the final script with some logging and examples of it being called:

cls
$web = Get-SPWeb "http://teamsite.tsb.local"
$companyList = $web.Lists.TryGetList("Company Contacts-PS")

function Add-CompanyContact([Microsoft.SharePoint.SPList]$list, [string]$company, [string]$name, [string]$tel, [string]$fax, [string]$postcode)
{
if ($list)
{
$query = New-Object Microsoft.SharePoint.SPQuery
$query.Query = [string]::Format("{0}", $company)
$results = $list.GetItems($query)

if ($results.Count -eq 0)
{
$item = $list.Items.Add()
$item["Title"] = $company
$item["Contact_Name"] = $name
$item["Contact_Tel"] = $tel
$item["Contact_Fax"] = $fax
$item["Contact_Postcode"] = $postcode
$item.Update()
Write-Host "Added Item '$company' to list"
}
else
{
Write-Host "Item '$company' already exists list"
}
}
}

Add-CompanyContact -list $companyList -company "Title 3" -name "Name 3" -tel "Tel 3" -fax "Fax 3" -postcode "Postcode 3"
Add-CompanyContact -list $companyList -company "Title 4" -name "Name 4" -tel "Tel 4" -fax "Fax 4" -postcode "Postcode 4"
Add-CompanyContact -list $companyList -company "Title 3" -name "Name 3" -tel "Tel 3" -fax "Fax 3" -postcode "Postcode 3"

When this is run I get the following output:

Added Item 'Title 3' to list
Added Item 'Title 4' to list
Item 'Title 3' already exists list

Which means I can instantly see that 2 items were added, but the 3 was a duplicate.

What’s next?

Now we have a working script we could create a ps1 file which contains our function and data and any time we need to create this list and populate it we can just run this ps1 file. Could you use this script in Production? Almost. Before I use it I would go through and provide some extra logging information – with automation logging really is your best friend to see what has happened. I would probably also want to go through and provide some more validation and error checking – for example how does the ‘Add-CompanyContact’ function behaviour with a blank ‘company’ parameter? The script we created in the first part could also be converted into a function – pass in a list name and template and it will create it for you if it doesn’t exist.

Summary

On the one hand the scripts we’ve seen here are fairly simply but they do illustrate some of the potential that scripting has to offer. If you are a developer then I would really encourage you to take a closer look. The next time you have to make a change in SharePoint think about whether you can script it.

Download – if you want to use the same list definition as in this article, I’ve included the WSP which you can download from here as a zip. (Like all code this site it comes with the disclaimer that if you install it, it’s your responsibility if it causes any problems on your environment!)

 

Leave a comment

  • (will not be published)

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>