My adventures into SharePoint and PowerShell
On the back of some requests on Twitter about me using PowerShell for SharePoint, I thought I would put this post together to talk about where I started and the things I did today in the space of a couple of hours. To start with, I’ll admit that I haven’t done more than look at powershell and briefly read over some doco in the SDK about it, so this article is more of the people who haven’t touched it – and seriously you don’t know what you’re missing if that is you!
So to get started I downloaded the PowerShel 2.0 CTP and PowerGUI(to edit scripts with InteliSense, top tool). I had a read over the article at SPDevWiki(and I will probably go and contribute more to that now that I have done it myself). So the first thing I learned was about the powershell profile (this is from the SPDevWiki page). Basically this is somewhere that you can put in functions that you want to be available in all the PowerShell scripts you run on that machine. The page there recommends putting some neat little functions into this before you get too far along – I didn’t to begin with (I just put them into my script file) but I will be doing it shortly.
I had also better explain why I had been looking into PowerShell before I get into the details though – basically I got handed a deployment document for a site that a client wanted built. It was pages of screenshots of the manual steps involved to create a default team site and apply a few customisations to it. It took a while to actually do this so I thought to myself “there has to be a better way”, then I thought of scripting it with PowerShell – I knew I could essentially access the entire SP object model in the script, which meant I knew I could achieve everything they were doing in the script.
So the first thing I did was create a new site collection in an existing web application. This was pretty straight forward, here is the code:
$webapp = [Microsoft.SharePoint.Administration.SPWebApplication]::Lookup("http://server")
$webapp.Sites.Add("http://server/sites/mynewsite", "Site title", "Site description", 1033, "STS#0", "DOMAIN\SiteOwner", "Site Owner Name", "Site.Owner@domain.com")
$site = get-spsite $url
So the $webapp is basically the SPWebApplication object, and from that you can get all the properties and functions you can through C# code, so we can add the site collection the same was as we would through code. The next thing I needed to do was to update the properties of one of the lists in the site. So I used the get-splist method that was in that profile section from SPDevWiki to get the SPList object, then just updated settings as I needed to
$mylist = get-splist $url "Announcements" $mylist.Description = "My new description" $mylist.OnQuickLaunch = $true $mylist.Update()
So again, this is pretty much the exact same as if you were writing the code in C#, so naturally at this point I’m getting pretty excited at what I can do with this whole PowerShell thing! Next I needed to update the properties of a document library, so I needed the SPDocumentLibrary object instead of the SPList, so I made a copy of the get-splist function and called it get-spdoclib, then used it to update the document library.
function get-spdoclib ([String]$webUrl=$(throw 'Parameter -webUrl is missing!'),String]$listName=$(throw 'Parameter -listName is missing!'))
{
$site = New-Object -TypeName "Microsoft.SharePoint.SPSite" -ArgumentList "$webUrl";
$web = $site.OpenWeb();
return [Microsoft.SharePoint.SPDocumentLibrary]$web.Lists[$listName]
}
$lstDocuments.Title = "Custom Document Library"
$lstDocuments.Description = "I changed this with PowerShell!"
$lstDocuments.EnableModeration = $true
$lstDocuments.EnableVersioning = $true
$lstDocuments.EnableMinorVersions = $true
$lstDocuments.Update()
This worked quite nicely and let me set the versioning and approval settings of the document library. So the things I learnt here were that I could still cast objects (The return line of the get-spdoclib function uses a type cast to get the SPList that is returned to come out as the SPDocumentLibrary) and that if you need to refer to things like true and false, you do it with a $ at the front. My next target was the quick launch navigation, I needed to move an item to the top of the “Lists” section, and delete an item off the bottom, so again I took what I knew I could do with C# and applied it here:
$web = get-spweb "http://vds000023/sites/mynewsite" $quicklaunch = $web.Navigation.QuickLaunch $quicklaunchlists = $quicklaunch[1] $quicklaunchlists.Children[2].MoveToFirst($quicklaunchlists.Children) $quicklaunch.Delete($quicklaunch[4])
I wasn’t overly happy with the way I put this part together because I just refer to the items in the quick launch by their indexes in the array (so $quicklaunch[1] is the heading “Lists” and then the Children[2] property from it is the 3rd item in the list of items under that heading). But again it’s just the same methods to move the navigation around and then to delete the item. My next thing was that I wanted to move one of the web parts on the home page, add a new web part and delete a web part – here was what I came up with:
$file = $web.GetFile("default.aspx")
$wpmanager = $file.GetLimitedWebPartManager([System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
$wpmanager.DeleteWebPart($wpmanager.WebParts[3]) $calendarwp = [Microsoft.SharePoint.WebPartPages.ListViewWebPart]$wpmanager.WebParts[1]
$wpmanager.MoveWebPart($calendarwp, "Right", 20)
$wpmanager.SaveChanges($calendarwp)$documentswp = New-Object "Microsoft.SharePoint.WebPartPages.ListViewWebPart"
$documentswp.ListName = $lstDocuments.ID.ToString("B").ToUpper()
$documentswp.WebId = $web.ID
$documentswp.ZoneID = "Right"
$wpmanager.AddWebPart($documentswp, "Left", 20)
So the delete operation was first (and again I’m using the indexes to refer to the webparts – I don’t like it but it works), then I moves a web part to the right zone, and then I created a new list view web part (the $lstDocuments variable in there was from a previous call I had to get-spdoclib to update the document library settings). So here I figured out that you can refer to enumerations such as the PersonalizationScope in PowerShell by giving the typename followed by :: then the value – and this had intelisense for the values as well which was handy.
The last thing I wanted to do was around permissions – I needed to create a group, create a custom permission level, and then assign that permission level to the group for the site. Here’s the code:
$newroledef = New-Object "Microsoft.SharePoint.SPRoleDefinition" -ArgumentList $web.RoleDefinitions["Contribute"] $newroledef.Name = "Approve" $newroledef.BasePermissions = $newroledef.BasePermissions.ToString() + ",ApproveItems" $web.RoleDefinitions.Add($newroledef) $web.Update() $customroledef = $web.RoleDefinitions["My Permission Level"] $customroleassignment = New-Object "Microsoft.SharePoint.SPRoleAssignment" -ArgumentList $web.SiteGroups["Group name"] $customroleassignment.RoleDefinitionBindings.Add($customroledef) $web.RoleAssignments.Add($customroleassignment)
So the first thing I did here was to make a copy of the Contirbute permission and add the approve items right. In PowerShell when you have a flagged enum like the BasePermissions property, you can actually refer to the list of values like “Value1,Value2,Value3″ and it will work the rest out for you. In this case I am just ToString()’ing the value and adding the ApproveItems flag – I like this, very easy to do. Next I added the group, again straight forward ($site was already around from where I had used the get-spsite method from SPWevWiki again) and the applying the permission to the group was again, the same as it would be in C# code. The thing I learnt here was around creating new objects. You use the “New-Object” variable and the put the arguments for the constructor you want to call after the ArguementList option (just as a comma seperated list).
So in the end when I string that all together I got myself the desired result, so I was pretty impressed after that. So the theory of what will happen with this script for the client is that the fill script will become a base for a new extranet site, so whenever they need to create a new one they can take a copy of the script, tweak it a little and roll out the new site in a lot less time that it takes to manually prepare it. The are some alternatives that could have been taken (such as site definitions, or using features to apply the changes) but given that there is likely to be a large number of these sites, and they will all be slightly different (but still essentially just customised OOTB sites) going to all that effort didn’t seem like an appropriate choice. The script on the other hand didn’t take long to put together, will create the individual sites, and won’t clog the farm up with endless site definitions and/or features for each extranet.
I will definitely be looking to make more use of PowerShell from here on in, and I hope that if you have learned something from this (somewhat large) post that you will too!




Glad you found the @SPDevWIki helpful…lots of other great PowerShell links on there too. Be great to get more going in this area!
Just make sure you dispose your SPWeb & SPSite objects.
Powershell leaks SP objects too :-)