SharePoint Automation Gary Lapointe – Founding Partner, Aptillon, Inc.

20Aug/113

Resetting SharePoint 2010 Themes – Part 2, the Reset-SPTheme cmdlet

Yesterday I threw up a quick post showing how to reset a SharePoint 2010 theme using a reasonably simple Windows PowerShell script. In that post I promised that I’d convert the script to a cmdlet and make it part of my downloadable extensions. Well, as promised I’ve updated my extensions so that they now include a Reset-SPTheme cmdlet. I added on minor enhancement over the previously shown script in that I allow you to pass in either an SPSite or an SPWeb object and by default it will not force all child webs to inherit from the relevant SPWeb object. This way, if you have a child Site with it’s own theme it won’t wipe out that theme. If you have multiple Sites with a custom theme setting within a Site Collection then you’ll want to provide the -Site parameter and pass in an SPSite reference – this will result in all Sites with custom themes within the Site Collection to be reset. If you only wish to reset a single Site then use the -Web parameter and pass in a SPWeb reference.

Here’s the full help for the Reset-SPTheme cmdlet:

NAME
    Reset-SPTheme

SYNOPSIS
    Resets a theme by applying all user specified theme configuration settings to the original source files. This is particularly helpful when the original source files have changed to a Feature upgrade.

SYNTAX
    Reset-SPTheme [-Web] <SPWebPipeBind> [-SetSubWebsToInherit <SwitchParameter>] [-AssignmentCollection <SPAssignmentCollection>] [<CommonParameters>]

    Reset-SPTheme [-Site] <SPSitePipeBind> [-SetSubWebsToInherit <SwitchParameter>] [-AssignmentCollection <SPAssignmentCollection>] [<CommonParameters>]

DESCRIPTION
    Resets a theme by applying all user specified theme configuration settings to the original source files. This is particularly helpful when the original source files have changed to a Feature upgrade.

    Copyright 2011 Falchion Consulting, LLC
    > For more information on this cmdlet and others:
    >
http://blog.falchionconsulting.com/
    > Use of this cmdlet is at your own risk.
    > Gary Lapointe assumes no liability.

PARAMETERS
    -Web <SPWebPipeBind>
        Specifies the URL or GUID of the Web containing the theme to reset.

        The type must be a valid GUID, in the form 12345678-90ab-cdef-1234-567890bcdefgh; a valid name of Microsoft SharePoint Foundation 2010 Web site (for example, MySPSite1); or an instance of a valid SPWeb object.

        Required?                    true
        Position?                    1
        Default value
        Accept pipeline input?       true (ByValue, ByPropertyName)
        Accept wildcard characters?  false

    -Site <SPSitePipeBind>
        The site containing the theme to reset.

        The type must be a valid GUID, in the form 12345678-90ab-cdef-1234-567890bcdefgh; a valid URL, in the form http://server_name; or an instance of a valid SPSite object.

        Required?                    true
        Position?                    1
        Default value
        Accept pipeline input?       true (ByValue, ByPropertyName)
        Accept wildcard characters?  false

    -SetSubWebsToInherit [<SwitchParameter>]
        If specified, all child webs will be reset to inherit the theme of the specified web or root web.

        Required?                    false
        Position?                    named
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -AssignmentCollection [<SPAssignmentCollection>]
        Manages objects for the purpose of proper disposal. Use of objects, such as SPWeb or SPSite, can use large amounts of memory and use of these objects in Windows PowerShell scripts requires proper memory management. Using the SPAssignment object, you can assign objects to a variable and dispose of the objects after they are needed to free up memory. When SPWeb, SPSite, or SPSiteAdministration objects are used, the objects are automatically disposed of if an assignment collection or the Global parameter is not used.

        When the Global parameter is used, all objects are contained in the global store. If objects are not immediately used, or disposed of by using the Stop-SPAssignment command, an out-of-memory scenario can occur.

        Required?                    false
        Position?                    named
        Default value
        Accept pipeline input?       true (ByValue)
        Accept wildcard characters?  false

    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer and OutVariable. For more information, type,
        "get-help about_commonparameters".

INPUTS

OUTPUTS

NOTES

        For more information, type "Get-Help Reset-SPTheme -detailed". For technical information, type "Get-Help Reset-SPTheme -full".

    ------------------EXAMPLE 1-----------------------

    PS C:\> Get-SPSite http://server_name | Reset-SPTheme -SetSubWebsToInherit

    This example resets the theme for the site collection http://server_name and resets all child webs to inherit from the root web.

    ------------------EXAMPLE 2-----------------------

    PS C:\> Get-SPWeb http://server_name/sub-web | Reset-SPTheme

    This example resets the theme for the web http://server_name/sub-web.

RELATED LINKS
    Get-SPWeb
    Get-SPSite

 

In the following example I’m resetting the theme(s) for an entire Site Collection. If one any child Sites within the Site Collection have a custom theme then they’ll be updated, not just the root (inheritance will not be changed):

PS C:\> Reset-SPTheme -Site http://example.com

In this next example I’m resetting all child Sites to inherit whatever theme has been specified for the root Site and I’m updating the root Site’s theme with changes to the source files:

PS C:\> Reset-SPTheme -Site http://example.com -SetSubWebsToInherit

For this last example I’m resetting the theme of a specific sub-Site:

PS C:\> Reset-SPTheme -Web http://example.com

As you can see, this is pretty easy to use and, if you’re deploying your branding via Features and you have theme support then a cmdlet like this can be quite critical when you need to push out updates to that brand.

26Jun/112

Getting (and taking ownership of) Checked Out Files using Windows PowerShell

Often when I’m working on a project I need to generate a list of all checked out files and provide that to my client just prior to release to production. Sometimes the client will manually inspect each of them and act as they see fit and other times they’ll ask me to just batch publish all of them (for which I use my Publish-SPListItems cmdlet). So, how do I generate the report for the client? It’s actually pretty easy using PowerShell and a couple of quick loops. Here’s an example that loops through every Site Collection in the Farm and generates a nice report:

function Get-CheckedOutFiles() {
    foreach ($web in (Get-SPSite -Limit All | Get-SPWeb -Limit All)) {
        Write-Host "Processing Web: $($web.Url)..."
        foreach ($list in ($web.Lists | ? {$_ -is [Microsoft.SharePoint.SPDocumentLibrary]})) {
            Write-Host "`tProcessing List: $($list.RootFolder.ServerRelativeUrl)..."
            foreach ($item in $list.CheckedOutFiles) {
                if (!$item.Url.EndsWith(".aspx")) { continue }
                $hash = @{
                    "URL"=$web.Site.MakeFullUrl("$($web.ServerRelativeUrl.TrimEnd('/'))/$($item.Url)");
                    "CheckedOutBy"=$item.CheckedOutBy;
                    "CheckedOutByEmail"=$item.CheckedOutByEmail
                }
                New-Object PSObject -Property $hash
            }
            foreach ($item in $list.Items) {
                if ($item.File.CheckOutStatus -ne "None") {
                    if (($list.CheckedOutFiles | where {$_.ListItemId -eq $item.ID}) -ne $null) { continue }
                    $hash = @{
                        "URL"=$web.Site.MakeFullUrl("$($web.ServerRelativeUrl.TrimEnd('/'))/$($item.Url)");
                        "CheckedOutBy"=$item.File.CheckedOutByUser;
                        "CheckedOutByEmail"=$item.File.CheckedOutByUser.Email
                    }
                    New-Object PSObject -Property $hash
                }
            }
        }
        $web.Dispose()
    }
}
Get-CheckedOutFiles | Out-GridView

Running the above will generate a fairly nice report with URLs and usernames and whatnot; you could also use the Export-Csv cmdlet to dump the results to a CSV file that you can then hand off to your end-users. One cool thing to point out about this is that it will also show you files that you normally can’t see – that is files that have been created by other users but have never had a check in. This is actually pretty cool and I stumbled upon this when trying to fine tune my Publish-SPListItems cmdlet. You see, if the file has never been checked in then iterating through the SPListItemCollection object will not reveal the item (or file I should say); this meant that my cmdlet, as it was previously written, was missing a bunch of files. So to work around this all I had to do was add an additional loop to iterate over the collection returned by the SPDocumentLibrary’s CheckedOutFiles property. For each SPCheckedOutFile object in that collection I then call TakeOverCheckOut() to grab the checked out file so that I can then publish.

I use this enough that I decided to turn it into a cmdlet that is now part of my custom extensions. Like the above script, I return back a custom object that contains the full URLs and other  useful information (such as the List, Site, and Site Collection identifiers). I also exposed a TakeOverCheckOut() and Delete() method which simply calls Microsoft’s implementation of those methods.

I called this cmdlet Get-SPCheckedOutFiles (note that I’d previously released this cmdlet under the name Get-SPFilesCheckedOut but have since reworked and renamed that original implementation).

Here’s the full help for the cmdlet:

PS C:\Users\spadmin> help Get-SPCheckedOutFiles -full

NAME
Get-SPCheckedOutFiles

SYNOPSIS
Retrieves check out details for a given List, Web, or Site.

SYNTAX
Get-SPCheckedOutFiles [-Site] <SPSitePipeBind> [-AssignmentCollection <SPAssignmentCollection>] [<CommonParameters>]

Get-SPCheckedOutFiles [-Web] <SPWebPipeBind> [-ExcludeChildWebs <SwitchParameter>] [-AssignmentCollection <SPAssignmentCollection>] [<CommonParameters>]

Get-SPCheckedOutFiles [[-Web] <SPWebPipeBind>] [-List] <SPListPipeBind> [-AssignmentCollection <SPAssignmentCollection>] [<CommonParameters>]


DESCRIPTION
Retrieves check out details for a given List, Web, or Site.

Copyright 2010 Falchion Consulting, LLC
> For more information on this cmdlet and others:
>
http://blog.falchionconsulting.com/
> Use of this cmdlet is at your own risk.
> Gary Lapointe assumes no liability.

PARAMETERS
-Site <SPSitePipeBind>
Specifies the URL or GUID of the Site to inspect.

The type must be a valid GUID, in the form 12345678-90ab-cdef-1234-567890bcdefgh; a valid URL, in the form
http://server_name; or an instance of a valid SPSite object.

Required? true
Position? 1
Default value
Accept pipeline input? true (ByValue, ByPropertyName)
Accept wildcard characters? false

-Web <SPWebPipeBind>
Specifies the URL or GUID of the Web to inspect.

The type must be a valid GUID, in the form 12345678-90ab-cdef-1234-567890bcdefgh; a valid URL, in the form
http://server_name; or an instance of a valid SPWeb object.

Required? true
Position? 1
Default value
Accept pipeline input? true (ByValue, ByPropertyName)
Accept wildcard characters? false

-List <SPListPipeBind>
The list whose checked out files are to be returned.

The value must be a valid URL in the form
http://server_name/lists/listname or /lists/listname. If a server relative URL is provided then the Web parameter must be provided.

Required? true
Position? 1
Default value
Accept pipeline input? true (ByValue, ByPropertyName)
Accept wildcard characters? false

-ExcludeChildWebs [<SwitchParameter>]
Excludes all child sites and only considers the specified site.

Required? false
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false

-AssignmentCollection [<SPAssignmentCollection>]
Manages objects for the purpose of proper disposal. Use of objects, such as SPWeb or SPSite, can use large amounts of memory and use of these objects in Windows PowerShell scripts requires proper memory management. Using the SPAssignment object, you can assign objects to a variable and dispose of the objects after they are needed to free up memory. When SPWeb, SPSite, or SPSiteAdministration objects are used, the objects are automatically disposed of if an assignment collection or the Global parameter is not used.

When the Global parameter is used, all objects are contained in the global store. If objects are not immediately used, or disposed of by using the Stop-SPAssignment command, an out-of-memory scenario can occur.

Required? false
Position? named
Default value
Accept pipeline input? true (ByValue)
Accept wildcard characters? false

<CommonParameters>
This cmdlet supports the common parameters: Verbose, Debug, ErrorAction, ErrorVariable, WarningAction, WarningVariable, OutBuffer and OutVariable. For more information, type, "get-help about_commonparameters".

INPUTS

OUTPUTS

NOTES

For more information, type "Get-Help Get-SPCheckedOutFiles -detailed". For technical information, type "Get-Help Get-SPCheckedOutFiles -full".

------------------EXAMPLE------------------

PS C:\> Get-SPCheckedOutFiles -Site "
http://server_name/"


This example outputs a list of files that are checked out for the given Site Collection


RELATED LINKS
Get-SPFile

 

In the following example I’m retrieving pages from the root Pages library that are checked out:

image

In this example I am running the cmdlet as the aptillon\spadmin user and I’m now able to see the checkout by the user aptillon\glapointe. I ran the cmdlet twice so you could see the default tabular view as well as the more detailed view. Again, you could easily use the Export-Csv cmdlet to dump this information to a file that you can provide your end-users.

I hope you find this cmdlet useful – it personally has proven invaluable to me, particularly when working on anonymous access internet sites as end-users are notorious about creating pages and not getting them checked in.

P.S. With this release the Publish-SPListItems cmdlet has been updated to now consider files that don’t have any existing check-ins.

25Jun/1125

Programmatically Setting SharePoint 2010 Calendar Overlays

I recently did a project where my client needed several calendars provisioned via a Feature Receiver when a particular type of Site Collection was created; they had one primary calendar and they wanted all the other calendars to be overlaid onto the primary one using SharePoint 2010’s Calendar overlay capabilities.

Here’s a quick summary of this feature if you’re not familiar with it. When you are looking at a calendar you should notice that there is a ribbon button titled “Calendar Overlay”:

image

Clicking this button brings you to the Calendar Overlay Settings page; from this page you can add or edit the overlay calendars. Clicking “New Calendar” brings you to an application page where you can define what calendar to overlay:

image

As you can see from the above screenshot, you can add not just a SharePoint calendar as an overlay but also an Exchange calendar. After configuring calendar overlays you will see overlaid items when looking at the month view for the calendar:

SNAGHTML16503622

In this example, the pink item is coming from the overlay calendar. Overlay items are added dynamically after the page loads (some JavaScript and Ajax take care of loading the items and rendering them on the page).

So that’s the basics from the end-users perspective. Now what about the technical details? Well, there’s not a lot of information out there that describes how this is done but here’s the gist of it – there’s a simple XML structure that is stored in the SPView’s CalendarSettings property; this property defines the overlays. Once you know that the rest is just a matter of figuring out what that structure looks like. The easiest way to start is to simply go through the browser and set up one or two overlays and then use some simple PowerShell to dump out that XML:

SNAGHTML16554e82[4]

Here’s a better view of what that XML looks like:

<AggregationCalendars>
<AggregationCalendar Id="{26ddb82c-9e2b-4c5d-9b7e-4ee25cf5c357}"
Type="SharePoint"
Name="My Overlay Calendar"
Description=""
Color="5"
AlwaysShow="False"
CalendarUrl="/Lists/MyOverlayCalendar/calendar.aspx">
<Settings WebUrl="http://demo"
ListId="{428bd2cb-a32d-4867-b658-6498158636a8}"
ViewId="{09928cd4-9a5e-44ed-9bf2-dfe1fc85661b}"
ListFormUrl="/Lists/MyOverlayCalendar/DispForm.aspx" />
</AggregationCalendar>
</AggregationCalendars>

I want to call particular attention to the WebUrl attribute of the Settings element; this value *must* be the full URL of the SPWeb object that contains the overlay calendar list. Okay, you’re thinking, not a huge deal, SharePoint stores the full URL for lots of things and it doesn’t really pose issues right? WRONG! Think about a scenario where you have an extended web application. So in my example I have an authoring site located at http://demo and I’ve extended this site for anonymous access under the URL http://demo.aptillon.com. Due to what I consider a design flaw with the overlays, the overlay feature will only work when the web application you are accessing the site as matches the web application defined for the WebUrl attribute. So if I were to try and access my overlay using the anonymous site I’d get the following error:

image

And of course, due to this error, the overlays will not show up. So even if I’ve only changed the protocol (http to https) I’d still get this same error. This means that, effectively, calendar overlays using SharePoint lists will only work under the context of the Web Application (and protocol) from which the overlay was defined. (I’ve torn through the code that does this and it’s something that Microsoft should be very embarrassed about – very poor performance and just flat out horribly implemented. Okay, I digress, let’s get back to the details.

Another thing you’ll want to do to understand this XML structure is to look at the code that constructs it. There’s two places to look and both require Reflector or some equivalent disassembler; the first is the SerializeAccessors() method of the Microsoft.SharePoint.ApplicationPages.Calendar.CalendarAccessorManagerImpl class. This method takes the properties provided to it and constructs the XML structure shown above. So where are these properties set? For that we need to look at the BtnOk_Click() method of the Microsoft.SharePoint.ApplicationPages.AggregationCustomizePage class. I’m not going to show the details of these methods here but suffice it to say these methods have everything you need to understand this structure.

As I previously noted, for my particular client I needed to set several overlays within a Feature Activated event; to make this easier (because there was technically several places I had to do this) I created a simple method that I could call; this method took in my target list and the list I wanted to overlay as well as several other properties. For this post I’ve taken that code and created a modified version of it which supports adding Exchange-based calendars. Here’s that code:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.SharePoint;

namespace Lapointe.SharePoint2010.Automation.Common.Lists
{
    public enum CalendarOverlayColor
    {
        LightYellow = 1,
        LightGreen = 2,
        Orange = 3,
        LightTurquise = 4,
        Pink = 5,
        LightBlue = 6,
        IceBlue1 = 7,
        IceBlue2 = 8,
        White = 9
    }

    public class SetListOverlay
    {
        public static void AddCalendarOverlay(SPList targetList, string viewName, string owaUrl, string exchangeUrl, string overlayName, string overlayDescription, CalendarOverlayColor color, bool alwaysShow, bool clearExisting)
        {
            AddCalendarOverlay(targetList, viewName, owaUrl, exchangeUrl, null, overlayName, overlayDescription, color, alwaysShow, clearExisting);
        }
        public static void AddCalendarOverlay(SPList targetList, string viewName, SPList overlayList, string overlayName, string overlayDescription, CalendarOverlayColor color, bool alwaysShow, bool clearExisting)
        {
            AddCalendarOverlay(targetList, viewName, null, null, overlayList, overlayName, overlayDescription, color, alwaysShow, clearExisting);
        }
        private static void AddCalendarOverlay(SPList targetList, string viewName, string owaUrl, string exchangeUrl, SPList overlayList, string overlayName, string overlayDescription, CalendarOverlayColor color, bool alwaysShow, bool clearExisting)
        {
            bool sharePoint = overlayList != null;
            string linkUrl = owaUrl;
            if (sharePoint)
                linkUrl = overlayList.DefaultViewUrl;

            SPView targetView = targetList.DefaultView;
            if (!string.IsNullOrEmpty(viewName))
                targetView = targetList.Views[viewName];

            XmlDocument xml = new XmlDocument();
            XmlElement aggregationElement = null;
            int count = 0;
            if (string.IsNullOrEmpty(targetView.CalendarSettings) || clearExisting)
            {
                xml.AppendChild(xml.CreateElement("AggregationCalendars"));
                aggregationElement = xml.CreateElement("AggregationCalendar");
                xml.DocumentElement.AppendChild(aggregationElement);
            }
            else
            {
                xml.LoadXml(targetView.CalendarSettings);
                XmlNodeList calendars = xml.SelectNodes("/AggregationCalendars/AggregationCalendar");
                if (calendars != null)
                    count = calendars.Count;
                aggregationElement = xml.SelectSingleNode(string.Format("/AggregationCalendars/AggregationCalendar[@CalendarUrl='{0}']", linkUrl)) as XmlElement;
                if (aggregationElement == null)
                {
                    if (count >= 10)
                        throw new SPException(string.Format("10 calendar ovarlays already exist for the calendar {0}.",targetList.RootFolder.ServerRelativeUrl));
                    aggregationElement = xml.CreateElement("AggregationCalendar");
                    xml.DocumentElement.AppendChild(aggregationElement);
                }
            }
            if (!aggregationElement.HasAttribute("Id"))
                aggregationElement.SetAttribute("Id", Guid.NewGuid().ToString("B", CultureInfo.InvariantCulture));

            aggregationElement.SetAttribute("Type", sharePoint ? "SharePoint" : "Exchange");
            aggregationElement.SetAttribute("Name", !string.IsNullOrEmpty(overlayName) ? overlayName : (overlayList == null ? "" : overlayList.Title));
            aggregationElement.SetAttribute("Description", !string.IsNullOrEmpty(overlayDescription) ? overlayDescription : (overlayList == null ? "" : overlayList.Description));
            aggregationElement.SetAttribute("Color", ((int)color).ToString());
            aggregationElement.SetAttribute("AlwaysShow", alwaysShow.ToString());
            aggregationElement.SetAttribute("CalendarUrl", linkUrl);

            XmlElement settingsElement = aggregationElement.SelectSingleNode("./Settings") as XmlElement;
            if (settingsElement == null)
            {
                settingsElement = xml.CreateElement("Settings");
                aggregationElement.AppendChild(settingsElement);
            }
            if (sharePoint)
            {
                settingsElement.SetAttribute("WebUrl", overlayList.ParentWeb.Site.MakeFullUrl(overlayList.ParentWebUrl));
                settingsElement.SetAttribute("ListId", overlayList.ID.ToString("B", CultureInfo.InvariantCulture));
                settingsElement.SetAttribute("ViewId", overlayList.DefaultView.ID.ToString("B", CultureInfo.InvariantCulture));
                settingsElement.SetAttribute("ListFormUrl", overlayList.Forms[PAGETYPE.PAGE_DISPLAYFORM].ServerRelativeUrl);
            }
            else
            {
                settingsElement.SetAttribute("ServiceUrl", exchangeUrl);
            }
            targetView.CalendarSettings = xml.OuterXml;
            targetView.Update();
            /*
            <AggregationCalendars>
                <AggregationCalendar 
                    Id="{cfc22c0b-688e-4555-b1d0-784081a91464}" 
                    Type="SharePoint" 
                    Name="My Overlay Calendar"
                    Description="" 
                    Color="1" 
                    AlwaysShow="True" 
                    CalendarUrl="/Lists/MyOverlayCalendar/calendar.aspx">
                    <Settings 
                        WebUrl="http://demo" 
                        ListId="{4a15e596-674f-4af7-a548-0b01470e8d75}" 
                        ViewId="{594c2916-14e7-4b08-ba36-1126b825bf45}" 
                        ListFormUrl="/Lists/MyOverlayCalendar/DispForm.aspx" />
                </AggregationCalendar>
                <AggregationCalendar 
                    Id="{cfc22c0b-688e-4555-b1d0-784081a91465}" 
                    Type="Exchange" 
                    Name="My Overlay Calendar"
                    Description="" 
                    Color="1" 
                    AlwaysShow="True" 
                    CalendarUrl="<url>">
                    <Settings ServiceUrl="<url>" />
                </AggregationCalendar>
            </AggregationCalendars>
            */
        }
    }
}

I’m not going to bore you with the details of this code as all I’m doing is basic XML manipulation. I created a couple of method overloads to allow for creating SharePoint or Exchange-based overlays. So, did you notice the namespace? Yup, no point in releasing code here if I’m not going to turn it into a cmdlet Smile

I’m not sure how useful this cmdlet will be in everyday use but imagine the scenario in which you have a primary calendar on your company portal and you want to add it as an overlay on every calendar throughout portal – you could easily do this using this cmdlet. Before we get to that, let’s see the full help for the cmdlet, which I called Set-SPListOverlay:

PS C:\Users\spadmin> help Set-SPListOverlay -full

NAME
    Set-SPListOverlay
    
SYNOPSIS
    Sets calendar overlays for the given list.
    
SYNTAX
    Set-SPListOverlay -Color  -OverlayTitle  [-OverlayDescription ] -OwaUrl  -WebServiceUrl  [-TargetList]  [-ViewName ] [-DoNotAlwaysShow ] [-ClearExisting ] [-AssignmentCollection ] []
    
    Set-SPListOverlay -Color  [-OverlayList]  [-OverlayTitle ] [-OverlayDescription ] [-TargetList]  [-ViewName ] [-DoNotAlwaysShow ] [-ClearExisting ] [-AssignmentCollection ] []
    
    Set-SPListOverlay [-OverlayLists]  [-TargetList]  [-ViewName ] [-DoNotAlwaysShow ] [-ClearExisting ] [-AssignmentCollection ] []
    
    
DESCRIPTION
    Sets calendar overlays for the given list.
    
    Copyright 2010 Falchion Consulting, LLC
    > For more information on this cmdlet and others:
    > http://blog.falchionconsulting.com/
    > Use of this cmdlet is at your own risk.
    > Gary Lapointe assumes no liability.
    

PARAMETERS
    -Color 
        The color to use for the overlay calendar.
        
        Required?                    true
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -TargetList 
        The calendar list to add the overlays to.
        
        Required?                    true
        Position?                    1
        Default value                
        Accept pipeline input?       true (ByValue)
        Accept wildcard characters?  false
        
    -ViewName []
        The name of the view to add the overlays to. If not specified the default view will be used.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -OverlayList 
        The calendar list to add as an overlay.
        
        Required?                    true
        Position?                    2
        Default value                
        Accept pipeline input?       true (ByValue)
        Accept wildcard characters?  false
        
    -OverlayLists 
        The calendar lists to add as an overlay.
        
        Required?                    true
        Position?                    2
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -OverlayTitle []
        The title to give the overlay calendar when viewed in the target calendar.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -OverlayDescription []
        The description to give the overlay calendar when viewed in the target calendar.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -OwaUrl 
        Outlook Web Access URL.
        
        Required?                    true
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -WebServiceUrl 
        Exchange Web Service URL.
        
        Required?                    true
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -DoNotAlwaysShow []
        Don't always show the calendar overlay.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -ClearExisting []
        Clear existing overlays. If not specified then all overlays will be appended to the list of existing over        lays (up until 10 - anything after 10 will be ignored)
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -AssignmentCollection []
        Manages objects for the purpose of proper disposal. Use of objects, such as SPWeb or SPSite, can use large amounts of memory and use of these objects in Windows PowerShell scripts requires proper memory management. Using the SPAssignment object, you can assign objects to a variable and dispose of the objects after they are needed to free up memory. When SPWeb, SPSite, or SPSiteAdministration objects are used, the objects are automatically disposed of if an assignment collection or the Global parameter is not used.
        
        When the Global parameter is used, all objects are contained in the global store. If objects are not immediately used, or disposed of by using the Stop-SPAssignment command, an out-of-memory scenario can occur.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       true (ByValue)
        Accept wildcard characters?  false
        
    
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer and OutVariable. For more information, type,
        "get-help about_commonparameters".
    
INPUTS
    
    
OUTPUTS
    
    
NOTES
    
    
        For more information, type "Get-Help Set-SPListOverlay -detailed". For technical information, type "Get-Help Set-SPListOverlay -full".
    
    ------------------EXAMPLE------------------
    
    PS C:\> Get-SPList "http://server_name/lists/MyCalendar" | Set-SPListOverlay -TargetList "http://server_name/lists/MyOverlayCalendar" -Color "Pink" -ClearExisting
    
    
    This example adds the MyOverlayCalendar calendar as an overlay to the MyCalendar list.
    
    
RELATED LINKS
    Get-SPList 

You can see from the different parameter sets that I’ve made provisions for setting the overlay as a SharePoint list or an Exchange calendar; additionally, I’ve made it so that if you have an array of lists that you wish to add as an overlay you can easily pass that array in as well. So now let’s look at that example:

$mainList = Get-SPList http://demo/lists/myCalendar
foreach ($site in (Get-SPSite http://demo -Limit All)) {
    foreach ($web in $site.AllWebs) {
        foreach ($list in ($web.Lists | ? {$_.BaseTemplate -eq "Events"})) {
            if ($list.ID -eq $mainList.ID) { continue }
            Set-SPListOverlay -TargetList $list `
                -OverlayList $mainList `
                -Color "Pink" `
                -OverlayTitle "Main Portal Calendar" `
                -ClearExisting
        }
        $web.Dispose()
    }
    $site.Dispose()
}

Pretty simple huh? I’m just grabbing the primary list and then I’m looping through all my Site Collections and Sites and then grabbing all the lists that have a base template of “Events”. Once I have the list then I simply call my cmdlet – that’s it – easy right?

Okay, so what if you have a calendar with a bunch of overlays and you need to grab those calendars and do something to them? Well, I threw in a bonus cmdlet called Get-SPListOverlays. This one is really simple – it merely takes in the primary list and then calls a simple helper method that parses the XML structure and grabs each list. I’m not going to bother showing the code as it’s real basic and this post is long enough (just download the source if you’d like to see it) but I will show the cmdlet help:

PS C:\Users\spadmin> help Get-SPListOverlays -Full
NAME
    Get-SPListOverlays
    
SYNOPSIS
    Retrieve all SPList objects set as a calendar overlay on the given list.
    
SYNTAX
    Get-SPListOverlays [-Identity]  [-Web ] [-AssignmentCollection ] []
    
    
DESCRIPTION
    Retrieve all SPList objects set as a calendar overlay on the given list.
    
    Copyright 2010 Falchion Consulting, LLC
    > For more information on this cmdlet and others:
    > http://blog.falchionconsulting.com/
    > Use of this cmdlet is at your own risk.
    > Gary Lapointe assumes no liability.
    

PARAMETERS
    -Identity 
        The calendar whose calendar overlays will be retrieved.
        
        The value must be a valid URL in the form http://server_name/lists/listname or /lists/listname. If a server relative URL is provided then the Web parameter must be provided.
        
        Required?                    true
        Position?                    1
        Default value                
        Accept pipeline input?       true (ByValue)
        Accept wildcard characters?  false
        
    -Web []
        Specifies the URL or GUID of the Web containing the calendar whose overlays will be retrieved.
        
        The type must be a valid GUID, in the form 12345678-90ab-cdef-1234-567890bcdefgh; a valid name of Microsoft SharePoint Foundation 2010 Web site (for example, MySPSite1); or an instance of a valid SPWeb object.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       true (ByValue)
        Accept wildcard characters?  false
        
    -AssignmentCollection []
        Manages objects for the purpose of proper disposal. Use of objects, such as SPWeb or SPSite, can use large amounts of memory and use of these objects in Windows PowerShell scripts requires proper memory management. Using the SPAssignment object, you can assign objects to a variable and dispose of the objects after they are needed to free up memory. When SPWeb, SPSite, or SPSiteAdministration objects are used, the objects are automatically disposed of if an assignment collection or the Global parameter is not used.
        
        When the Global parameter is used, all objects are contained in the global store. If objects are not immediately used, or disposed of by using the Stop-SPAssignment command, an out-of-memory scenario can occur.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       true (ByValue)
        Accept wildcard characters?  false
        
    
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer and OutVariable. For more information, type,
        "get-help about_commonparameters".
    
INPUTS
    
    
OUTPUTS
    
    
NOTES
    
    
        For more information, type "Get-Help Get-SPListOverlays -detailed". For technical information, type "Get-Help Get-SPListOverlays -full".
    
    ------------------EXAMPLE------------------
    
    PS C:\> $lists = Get-SPListOverlays "http://server_name/lists/mylist"
    
    
    This example retrieves the calendar overlays for the calendar at http://server_name/lists/mycalendar.
    
    
RELATED LINKS
    Get-SPList 
    Set-SPListOverlay 
    Get-SPWeb 

In the end this turned out to all be pretty simple to do but it was certainly a challenge trying to figure it all out as there’s no documentation (official or otherwise) that I’ve been able to find.

30Apr/110

Retrieving and Configuring the SharePoint 2010 Developer Dashboard using PowerShell

It’s been almost a year to the day since I’ve released my SharePoint 2010 cmdlets and, despite many good intentions to get them documented on my blog, things have just fallen by the wayside; this was primarily due to me going out on my own and writing my first book – but now that the book is done and I’ve begun to establish myself as an independent consultant, I believe it’s about time I start blogging about all these hidden cmdlets that I’ve created. So, to start I’m going to take a couple of cmdlets that I originally developed for some conference presentations; specifically Get-SPDeveloperDashboard and Set-SPDeveloperDashboard.

Before I show these two new cmdlets, let’s look at what it currently takes to retrieve and manipulate the developer dashboard using Windows PowerShell:

[Microsoft.SharePoint.Administration.SPWebService]::ContentService.DeveloperDashboardSettings

As you can see from the preceding figure, you obtain an instance of the SPDeveloperDashboardSettings object via the DeveloperDashboardSettings property of an SPWebService instance (obtained using the static ContentService property of the SPWebService class). Note that there are several properties that we can manipulate beyond just the simple DisplayLevel property that is used to enable or disable the developer dashboard (or to put it into on demand mode). Some people still like to use STSADM to change the DisplayLevel property but doing so doesn’t allow you to manipulate the other properties available; often the reason people use STSADM is because it’s slightly less verbose if all you wish to do is change the DisplayLevel property. Here’s an example of how you would do it with PowerShell:

$dds = [Microsoft.SharePoint.Administration.SPWebService]::ContentService.DeveloperDashboardSettings
$dds.DisplayLevel = "On"
$dds.Update()

So, not a whole lot of code but still more than the single STSADM line (that and people have a hard time remembering the full object path to get to the SPDeveloperDashboardSettings object – I personally can remember this easier than the STSADM key names).

Because of this slightly higher level of complexity I decided to create these cmdlets, but I also went ahead and added some PowerShell type extensions so that I could get to the developer dashboard from an SPFarm instance. I’ll examine that before we get into the cmdlets; if you download my source code you should notice a file named Lapointe.SharePoint2010.Automation.Cmdlets.Types.ps1xml in the {Project Root}\PowerShell\Types folder. Here’s the relevant contents of that file:

<?xml version="1.0" encoding="utf-8"?>
<Types>
  <Type>
    <Name>Microsoft.SharePoint.Administration.SPFarm</Name>
    <Members>
      <ScriptProperty>
        <Name>DeveloperDashboard</Name>
        <GetScriptBlock>[Microsoft.SharePoint.Administration.SPWebService]::ContentService.DeveloperDashboardSettings</GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
</Types>

What I’ve done here is essentially create a type extension using XML; the <Name /> element defines the full type name that you want to extend and the <Members /> element contains all the extensions. In this case I’ve added a new property named DeveloperDashboard and I provided the same script we saw previously so that the SPDeveloperDashboardSettings object will be returned. It’s important to understand that you are not limited to just get properties – you can create set properties as well as methods (type help about_types for more information about creating type extensions). With this type extension added we can now access the developer dashboard in a slightly simpler manner:

$dds = (Get-SPFarm).DeveloperDashboard

Using this approach there really isn’t a need for the Get-SPDeveloperDashboard cmdlet that I created, as the cmdlet only saves about seven characters; however, this approach isn’t obvious – what I want is users to be able to type Get-Command *dashboard* so that they can see all the cmdlets related to the developer dashboard. (Plus, I created the cmdlet originally just for demonstration purposes but it does make things a little more obvious). So now that we have the type extension out of the way, let’s take a look at the cmdlet. Here’s a dump of the full help for the Get-SPDeveloperDashboard cmdlet:

PS C:\> help Get-SPDeveloperDashboard -Full

NAME
    Get-SPDeveloperDashboard
    
SYNOPSIS
    Retrieves the Developer Dashboard Settings object.
    
SYNTAX
    Get-SPDeveloperDashboard [-AssignmentCollection <spassignmentcollection>] [<commonparameters>]
    
    
DESCRIPTION
    Retrieves the Developer Dashboard Settings object.
    
    Copyright 2010 Falchion Consulting, LLC
    > For more information on this cmdlet and others:
    > http://blog.falchionconsulting.com/
    > Use of this cmdlet is at your own risk.
    > Gary Lapointe assumes no liability.

PARAMETERS
    -AssignmentCollection [<spassignmentcollection>]
        Manages objects for the purpose of proper disposal. Use of objects, such as SPWeb or SPSite, 
        can use large amounts of memory and use of these objects in Windows PowerShell scripts requires 
        proper memory management. Using the SPAssignment object, you can assign objects to a variable 
        and dispose of the objects after they are needed to free up memory. When SPWeb, SPSite, or 
        SPSiteAdministration objects are used, the objects are automatically disposed of if an assignment 
        collection or the Global parameter is not used.
        
        When the Global parameter is used, all objects are contained in the global store. If objects are 
        not immediately used, or disposed of by using the Stop-SPAssignment command, an out-of-memory 
        scenario can occur.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       true (ByValue)
        Accept wildcard characters?  false
        
    <commonparameters>
        This cmdlet supports the common parameters: Verbose, Debug, ErrorAction, ErrorVariable, 
        WarningAction, WarningVariable, OutBuffer and OutVariable. For more information, 
        type, "get-help about_commonparameters".
    
INPUTS
    
OUTPUTS
    
NOTES
    
        For more information, type "Get-Help Get-SPDeveloperDashboard -detailed". 
        For technical information, type "Get-Help Get-SPDeveloperDashboard -full".
    
    ------------------EXAMPLE------------------
    
    PS C:\> $dash = Get-SPDeveloperDashboard
    
    This example returns back the developer dashboard settings object.
    
RELATED LINKS
    Set-SPDeveloperDashboard 

So obviously the cmdlet is pretty simple as there aren’t any parameters beyond the standard parameters (remember, the -AssignmentCollection parameter is included as part of the cmdlet base class but as the SPDeveloperDashboardSettings object is not disposable there is no reason to use it.

The code for this cmdlet is actually shorter than the help for it:

using System.Collections.Generic;
using System.Management.Automation;
using Lapointe.PowerShell.MamlGenerator.Attributes;
using Microsoft.SharePoint.PowerShell;
using Microsoft.SharePoint.Administration;

namespace Lapointe.SharePoint2010.Automation.Cmdlets.Farm
{
    [Cmdlet(VerbsCommon.Get, "SPDeveloperDashboard", SupportsShouldProcess = false),   SPCmdlet(RequireLocalFarmExist = true, RequireUserFarmAdmin = false)]
    [CmdletDescription("Retrieves the Developer Dashboard Settings object.")]
    [RelatedCmdlets(typeof(SPCmdletSetDeveloperDashboard))]
    [Example(Code = "PS C:\\> $dash = Get-SPDeveloperDashboard",       Remarks = "This example returns back the developer dashboard settings object.")]
    public class SPCmdletGetDeveloperDashboard : SPGetCmdletBaseCustom<SPDeveloperDashboardSettings>
    {
        protected override IEnumerable<SPDeveloperDashboardSettings> RetrieveDataObjects()
        {
            WriteObject(SPWebService.ContentService.DeveloperDashboardSettings);

            return null;
        }
    }
}

The following figure shows how you can call the cmdlet:

Get-SPDeveloperDashboard

Note that I’ve also added a new view for the SPDeveloperDashboardSettings object type (as shown in the first example – to see all the properties use the Select-Object cmdlet as shown in the second example). The custom views are added just like the custom type extensions – for views, however, you create another XML file which you can see in my source code under the {Project Root}\PowerShell\Format  folder. The following XML snippet illustrates the relevant portion of that file:

<?xml version="1.0" encoding="utf-8"?>
<Configuration>
  <ViewDefinitions>
   <View>
    <Name>SPDeveloperDashboardSettings</Name>
    <ViewSelectedBy>
      <TypeName>Microsoft.SharePoint.Administration.SPDeveloperDashboardSettings</TypeName>
    </ViewSelectedBy>
    <TableControl>
      <TableHeaders>
        <TableColumnHeader>
          <Label>Display Level</Label>
          <Width>14</Width>
          <Alignment>left</Alignment>
        </TableColumnHeader>
        <TableColumnHeader>
          <Label>Trace Enabled</Label>
          <Width>14</Width>
          <Alignment>left</Alignment>
        </TableColumnHeader>
        <TableColumnHeader>
          <Label>Required Permissions</Label>
          <Alignment>left</Alignment>
        </TableColumnHeader>
      </TableHeaders>
      <TableRowEntries>
        <TableRowEntry>
          <TableColumnItems>
            <TableColumnItem>
              <PropertyName>DisplayLevel</PropertyName>
            </TableColumnItem>
            <TableColumnItem>
              <PropertyName>TraceEnabled</PropertyName>
            </TableColumnItem>
            <TableColumnItem>
              <PropertyName>RequiredPermissions</PropertyName>
            </TableColumnItem>
          </TableColumnItems>
        </TableRowEntry>
      </TableRowEntries>
    </TableControl>
  </View>
 </ViewDefinitions>
</Configuration>

Okay, so we’ve made it easier to retrieve the developer dashboard, now I want to change the values in one step (because retrieving the object, changing the value, and calling Update() is just too much work). To do this I created the Set-SPDeveloperDashboard cmdlet. This cmdlet is a bit more complex in that I’ve exposed all the relevant properties of the SPDeveloperDashboardSettings object with an equivalent parameter. Here’s the full help for the cmdlet:

PS C:\> help Set-SPDeveloperDashboard -Full

NAME
    Set-SPDeveloperDashboard
    
SYNOPSIS
    Sets the Developer Dashboard Settings.
    
SYNTAX
    Set-SPDeveloperDashboard [-AutoLaunchEnabled <Boolean>] [-DisplayLevel <Off | OnDemand | On>] [-MaximumCriticalEventsToTrack <Int32>] 
    [-MaximumSQLQueriesToTrack <Int32>] [-RequiredPermissions <EmptyMask | ViewListItems | AddListItems | EditListItems | DeleteListItems |
    ApproveItems | OpenItems | ViewVersions | DeleteVersions | CancelCheckout | ManagePersonalViews | ManageLists | ViewFormPages | Open |
    ViewPages | AddAndCustomizePages | ApplyThemeAndBorder | ApplyStyleSheets | ViewUsageData | CreateSSCSite | ManageSubwebs | CreateGroups
    | ManagePermissions | BrowseDirectories | BrowseUserInfo | AddDelPrivateWebParts | UpdatePersonalWebParts | ManageWeb | 
    UseClientIntegration | UseRemoteAPIs | ManageAlerts | CreateAlerts | EditMyUserInfo | EnumeratePermissions | FullMask>] 
    [-TraceEnabled <Boolean>] [-AdditionalEventsToTrack <String[]>] [-AssignmentCollection <SPAssignmentCollection>] [<CommonParameters>]
    
DESCRIPTION
    Sets the Developer Dashboard Settings.
    
    Copyright 2010 Falchion Consulting, LLC
    > For more information on this cmdlet and others:
    > http://blog.falchionconsulting.com/
    > Use of this cmdlet is at your own risk.
    > Gary Lapointe assumes no liability.

PARAMETERS
    -AutoLaunchEnabled [<Boolean>]
        Indicates whether the developer dashboard can be auto launched.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -DisplayLevel [<SPDeveloperDashboardLevel>]
        Indicates whether the developer dashboard is set to Off, On, or On Demand.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -MaximumCriticalEventsToTrack [<Int32>]
        The maximum number of critical events and asserts that will be recorded in a single transaction (i.e. one request or timer job).
        If a single transaction has more than this number of asserts the remainder will be ignored. This can be set to 0 to disable
        assert tracking.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -MaximumSQLQueriesToTrack [<Int32>]
        The maximum number of SQL queries that will be recorded in a single transaction (i.e. one request or timer job). If a single
        transaction executes more than this number of requests the query will be counted but the query call stack and text will not be kept.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -RequiredPermissions [<SPBasePermissions>]
        A permission mask defining the permissions required to see the developer dashboard. This defaults to SPBasePermissions.AddAndCustomizePages.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -TraceEnabled [<Boolean>]
        Whether a link to display full verbose trace will be available at the bottom of the page when the developer dashboard is launched or not.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -AdditionalEventsToTrack [<String[]>]
        A list of URL tags to track in addition to events with severity above High.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -AssignmentCollection [<SPAssignmentCollection>]
        Manages objects for the purpose of proper disposal. Use of objects, such as SPWeb or SPSite, 
        can use large amounts of memory and use of these objects in Windows PowerShell scripts requires 
        proper memory management. Using the SPAssignment object, you can assign objects to a variable 
        and dispose of the objects after they are needed to free up memory. When SPWeb, SPSite, or 
        SPSiteAdministration objects are used, the objects are automatically disposed of if an assignment 
        collection or the Global parameter is not used.
        
        When the Global parameter is used, all objects are contained in the global store. If objects are 
        not immediately used, or disposed of by using the Stop-SPAssignment command, an out-of-memory 
        scenario can occur.
        
        Required?                    false
        Position?                    named
        Default value                
        Accept pipeline input?       true (ByValue)
        Accept wildcard characters?  false
        
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer and OutVariable. For more information, type,
        "get-help about_commonparameters".
    
INPUTS
    
OUTPUTS
    
NOTES
    
        For more information, type "Get-Help Set-SPDeveloperDashboard -detailed". For technical information,
        type "Get-Help Set-SPDeveloperDashboard -full".
    
    ------------------EXAMPLE 1-----------------------
    
    PS C:\> Set-SPDeveloperDashboard -RequiredPermissions "ManageWeb,ManageSubwebs"
    
    This example sets the required permissions to view the developer dashboard.
    
    ------------------EXAMPLE 2-----------------------
    
    PS C:\> Set-SPDeveloperDashboard -DisplayLevel OnDemand -TraceEnabled $true
    
    This example enables the developer dashboard.
    
RELATED LINKS
    Get-SPDeveloperDashboard 

The code for this cmdlet is obviously going to be slightly longer than the Get-SPDeveloperDashboard cmdlet, but again, it’s very simple as most of the code is just for defining the parameters:

using System.Collections.Generic;
using System.Management.Automation;
using Lapointe.PowerShell.MamlGenerator.Attributes;
using Microsoft.SharePoint;
using Microsoft.SharePoint.PowerShell;
using Microsoft.SharePoint.Administration;

namespace Lapointe.SharePoint2010.Automation.Cmdlets.Farm
{
    [Cmdlet(VerbsCommon.Set, "SPDeveloperDashboard", SupportsShouldProcess = false),
    SPCmdlet(RequireLocalFarmExist = true, RequireUserFarmAdmin = false)]
    [CmdletDescription("Sets the Developer Dashboard Settings.")]
    [RelatedCmdlets(typeof(SPCmdletGetDeveloperDashboard))]
    [Example(Code = "PS C:\\> Set-SPDeveloperDashboard -DisplayLevel OnDemand -TraceEnabled $true",
        Remarks = "This example enables the developer dashboard.")]
    [Example(Code = "PS C:\\> Set-SPDeveloperDashboard -RequiredPermissions \"ManageWeb,ManageSubwebs\"",
        Remarks = "This example sets the required permissions to view the developer dashboard.")]
    public class SPCmdletSetDeveloperDashboard : SPSetCmdletBaseCustom<SPDeveloperDashboardSettings>
    {
        public SPCmdletSetDeveloperDashboard()
        {
            SPDeveloperDashboardSettings dash = SPWebService.ContentService.DeveloperDashboardSettings;
            AutoLaunchEnabled = dash.AutoLaunchEnabled;
            DisplayLevel = dash.DisplayLevel;
            MaximumCriticalEventsToTrack = dash.MaximumCriticalEventsToTrack;
            MaximumSQLQueriesToTrack = dash.MaximumSQLQueriesToTrack;
            RequiredPermissions = dash.RequiredPermissions;
            TraceEnabled = dash.TraceEnabled;
            AdditionalEventsToTrack = ((List<string>) dash.AdditionalEventsToTrack).ToArray();
        }

        [Parameter(HelpMessage = "Indicates whether the developer dashboard can be auto launched.")]
        public bool AutoLaunchEnabled { get; set; }

        [Parameter(HelpMessage = "Indicates whether the developer dashboard is set to Off, On, or On Demand.")]
        public SPDeveloperDashboardLevel DisplayLevel { get; set; }

        [Parameter(HelpMessage = "The maximum number of critical events and asserts that will be recorded in a single transaction (i.e. one request or timer job). If a single transaction has more than this number of asserts the remainder will be ignored. This can be set to 0 to disable assert tracking.")]
        public int MaximumCriticalEventsToTrack { get; set; }

        [Parameter(HelpMessage = "The maximum number of SQL queries that will be recorded in a single transaction (i.e. one request or timer job). If a single transaction executes more than this number of requests the query will be counted but the query call stack and text will not be kept. ")]
        public int MaximumSQLQueriesToTrack { get; set; }

        [Parameter(HelpMessage = "A permission mask defining the permissions required to see the developer dashboard. This defaults to SPBasePermissions.AddAndCustomizePages.")]
        public SPBasePermissions RequiredPermissions { get; set; }

        [Parameter(HelpMessage = "Whether a link to display full verbose trace will be available at the bottom of the page when the developer dashboard is launched or not.")]
        public bool TraceEnabled { get; set; }

        [Parameter(HelpMessage = "A list of URL tags to track in addition to events with severity above High. ")]
        public string[] AdditionalEventsToTrack { get; set; }

        protected override void UpdateDataObject()
        {
            SPDeveloperDashboardSettings dash = SPWebService.ContentService.DeveloperDashboardSettings;

            dash.AutoLaunchEnabled = AutoLaunchEnabled;
            dash.DisplayLevel = DisplayLevel;
            dash.MaximumCriticalEventsToTrack = MaximumCriticalEventsToTrack;
            dash.MaximumSQLQueriesToTrack = MaximumSQLQueriesToTrack;
            dash.RequiredPermissions = RequiredPermissions;
            dash.TraceEnabled = TraceEnabled;
            dash.AdditionalEventsToTrack.Clear();
            ((List<string>)dash.AdditionalEventsToTrack).AddRange(AdditionalEventsToTrack);

            dash.Update();
        }
    }
}

The following figure shows how you can call the cmdlet:

Set-SPDeveloperDashboard -DisplayLevel On -RequiredPermissions "ManageWeb","ManageSubwebs"

So that wraps up my first (very long overdue) post for my 2010 cmdlets – look for more posts coming soon as well as an update to my index page listing all the available cmdlets.

-Gary

14May/1016

Announcing My SharePoint 2010 PowerShell Cmdlets & STSADM Commands Now Available for Download

I’ve been wanting to release the SharePoint 2010 version of my STSADM extensions for quite some time but honestly just haven’t had the time to migrate as many as I would have liked. With over 145 STSADM extensions for SharePoint 2007 it was a challenge determining which ones I should focus on initially for the migration.

But today I’m happy to announce my initial release which contains 46 PowerShell cmdlets and 56 STSADM commands specific to SharePoint 2010. Yup, you read right, I’ve decided to maintain support for my STSADM commands and have been migrating them over as I create the equivalent replacement PowerShell cmdlet (though I recommend you don’t use them and suck it up and get used to PowerShell). You should note that there are more STSADM commands than PowerShell cmdlets – that’s because some of the things I was doing with STSADM can now easily be done with out of the box PowerShell cmdlets (I also have new PowerShell cmdlets that do not have an STSADM equivalent – everything new I create will be a cmdlet and I’ll create no new STSADM commands).

It’s going to take me a while to create all the posts needed to explain each cmdlet (assuming I create one at all) so for now I’ve created this simple table which lists all the STSADM commands and PowerShell cmdlets that are available in this initial release (I’ll eventually update my command index page but for now let this serve as the main reference for what is available as of 5/14/2010):

STSADM Commands

PowerShell Cmdlets

Notes

gl-activatefeature

Enable-SPFeature2

There’s an OOTB Enable-SPFeature cmdlet, this one simple adds some capabilities which were present in my existing STSADM command.

gl-addaudiencerule

New-SPAudienceRule

 

gl-addavailablesitetemplate

  I’ll eventually create a cmdlet for this.

gl-adduser2

  Use the OOTB New-SPUser cmdlet.

gl-adduserpolicyforwebapp

Add-SPWebApplicationUserPolicy

 

gl-applytheme

  This can be done pretty easily using Get-SPWeb and the ApplyTheme() method of the SPWeb object.

gl-backup

  Use the OOTB Backup-SPFarm and Backup-SPSite cmdlets.

gl-backupsites

Backup-SPSite2

Extends Backup-SPSite by including IIS settings.

gl-convertsubsitetositecollection

ConvertTo-SPSite

 

gl-copycontenttypes

Copy-SPContentType

 

gl-copylist

Copy-SPList

 

gl-copylistsecurity

Copy-SPListSecurity

 

gl-createaudience

New-SPAudience

 

gl-createcontentdb

  Use the OOTB New-SPContentDatabase cmdlet.

gl-createpublishingpage

New-SPPublishingPage

 

gl-createquotatemplate

New-SPQuotaTemplate

 

gl-createwebapp

  Use the OOTB New-SPWebApplication cmdlet

gl-deactivatefeature

Disable-SPFeature2

Extends the OOTB Disable-SPFeature cmdlet.

gl-deleteallusers

  I probably won’t replicate this as it is easily done using the OOTB Remove-SPUser cmdlet.

gl-deleteaudience

Remove-SPAudience

 

gl-deletelist

Remove-SPList

 

gl-deletewebapp

  Use the OOTB Remove-SPWebApplication cmdlet.

gl-disableuserpermissionforwebapp

  This is fairly easy to do OOTB so I may not create a cmdlet for it.

gl-editquotatemplate

Set-SPQuotaTemplate

 

gl-enableuserpermissionforwebapp

  This is fairly easy to do OOTB so I may not create a cmdlet for it.

gl-enumaudiencerules

Export-SPAudienceRules

 

gl-enumavailablepagelayouts

Get-SPPublishingPageLayout

 

gl-enumavailablesitetemplates

Get-SPAvailableWebTemplates

 

gl-enumeffectivebaseperms

  This is fairly easy to do OOTB so I may not create a cmdlet for it.

gl-enumfeatures

  Use the OOTB Get-SPFeature cmdlet.

gl-enuminstalledsitetemplates

  Use the OOTB Get-SPWebTemplate cmdlet.

gl-enumpagewebparts

Get-SPWebPartList

 

gl-enumunghostedfiles

Get-SPCustomizedPages

 

gl-execadmsvcjobs

Start-SPAdminJob2

I honestly need to research this a bit more as I’m not sure it’s necessary anymore but I’ve replicated the functionality in case someone finds it useful.

gl-exportaudiences

Export-SPAudiences

 

gl-exportcontenttypes

Export-SPContentType

 

gl-exportlist

Export-SPWeb2

I’ve just extended the Export-SPWeb2 cmdlet to add additional parameters.

gl-exportlistsecurity

Export-SPListSecurity

 

gl-extendwebapp

  Use the OOTB New-SPWebApplicationExtension cmdlet.

gl-fixpublishingpagespagelayouturl

Repair-SPPageLayoutUrl

 

gl-importaudiences

Import-SPAudiences

 

gl-importlist

Import-SPWeb2

I’ve just extended the Import-SPWeb2 cmdlet to add additional parameters.

gl-importlistsecurity

Import-SPListSecurity

 

gl-listaudiencetargeting

Set-SPListAudienceTargeting

 

gl-managecontentdbsettings

  Use the OOTB Set-SPContentDatabase cmdlet.

gl-propagatecontenttype

Propagate-SPContentType

 

gl-publishitems

Publish-SPListItems

 

gl-reghostfile

Reset-SPCustomizedPages

 

gl-removeavailablesitetemplate

  I’ll eventually create a cmdlet for this (maybe).

gl-repairsitecollectionimportedfromsubsite

Repair-SPSite

 

gl-replacewebpartcontent

Replace-SPWebPartContent

 

gl-setbackconnectionhostnames

Set-SPBackConnectionHostNames

 

gl-setselfservicesitecreation

  Not sure if I’ll migrate this or not.

gl-syncquotas

Set-SPQuota

 

gl-tracelog

  Use the OOTB Set-SPDiagnosticConfig cmdlet.

gl-unextendwebapp

  Use the OOTB Remove-SPWebApplication cmdlet.
 

Get-SPAudience

 
 

Get-SPAudienceManager

 
 

Get-SPContentType

 
 

Get-SPFile

 
 

Get-SPLimitedWebPartManager

 
 

Get-SPList

 
 

Get-SPPublishingPage

 
 

Get-SPQuotaTemplate

 
 

Set-SPAudience

 

For those that know a thing or two about cmdlet development you might be interested in knowing that I am dynamically generating the help XML file for the cmdlets. If you download the source you’ll find a class which uses reflection to interrogate the assembly and dynamically build the help file just prior to building the WSP package. This saved me literally days of hand editing XML.

You can download the source and WSP files here or from the Downloads page:

After you deploy the package you can type “help <cmdlet name>” to get detailed help about each cmdlet, including parameter descriptions and example usage. If you want to see the list of cmdlets installed type the following:

gcm | where {$_.DLL –like "*lapointe*"}

As always, your use of these cmdlets/stsadm commands is at your own risk – I do as much testing as I can but every environment is different and there’s simply not enough time in a day. If you have any suggestions or feedback please don’t hesitate to leave a comment – I appreciate all of them!

19Oct/095

Creating Custom SharePoint 2010 Cmdlets using Visual Studio 2010

With SharePoint 2010 we now have the ability to create custom PowerShell cmdlets that can be deployed just like any other SharePoint artifact using SharePoint Solution Packages (WSP) created with Visual Studio 2010. With SharePoint 2007 it was necessary to build a custom setup (MSI) package which had to be run on every server in the farm. This setup package would register a custom snap-in that you'd have to create which would be responsible for registering all of your custom cmdlets with the PowerShell runtime.

With SharePoint 2010 we no longer have to create a custom snap-in or setup package. When the Microsoft.SharePoint.PowerShell snap-in is loaded it examines the {SharePointRoot}/Config/PowerShell/Registration folder for any XML files and dynamically registers the cmdlets specified in the XML. As long as the SharePoint binaries have been installed on the server then you can utilize this feature (if the farm has not yet been created then you'll have to manually GAC the assembly and deploy the registration XML file as solution deployments only work when the farm exists).

To facilitate a standard and consistent scripting experience SharePoint 2010 introduces five new base classes that all SharePoint 2010 PowerShell cmdlets should be derived from:

SharePoint 2010 PowerShell Cmdlet Base Classes

When creating your custom cmdlet you should carefully choose the correct base class for your cmdlet. When creating a cmdlet that is meant to work with persistent objects (objects that are to be used across calls) you should utilize one of the four task based base classes: SPRemoveCmdletBase, SPNewCmdletBase, SPSetCmdletBase, or SPGetCmdletBase. When creating cmdlets that return non-persistent objects/data or perform tasks that do not require a persistent object (e.g., Start-SP*) then you should use the SPCmdlet base class. A good example of a cmdlet that would use the SPCmdlet base class would be one what returns a report or some other information without returning back any specific objects.

Let's now take a look at an example of a custom cmdlet that we'll eventually package up in a SharePoint Solution Package:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.PowerShell;
using Microsoft.SharePoint.Administration;
using System.Management.Automation;

namespace Lapointe.SharePoint2010.PowerShell.Demo.Quotas
{
[Cmdlet(VerbsCommon.Get, "SPQuotaTemplate"),
SPCmdlet(RequireLocalFarmExist = true, RequireUserFarmAdmin = true)]
public class SPCmdletGetQuotaTemplate : SPGetCmdletBase<SPQuotaTemplate>
{
protected override void InternalValidate()
{
if (this.Identity != null)
{
base.DataObject = this.Identity.Read();
if (base.DataObject == null)
{
base.WriteError(new PSArgumentException("The quota template does not exist."), ErrorCategory.InvalidArgument, this.Identity);
base.SkipProcessCurrentRecord();
}
}
}

protected override IEnumerable<SPQuotaTemplate> RetrieveDataObjects()
{
List<SPQuotaTemplate> list = new List<SPQuotaTemplate>();
if (base.DataObject != null)
{
list.Add(base.DataObject);
return list;
}
SPWebService webService = SPWebService.ContentService;
if (webService != null)
{
foreach (SPQuotaTemplate quota in webService.QuotaTemplates)
{
list.Add(quota);
}
}

return list;
}

[Parameter(Mandatory = false, ValueFromPipeline = true, Position = 0), Alias(new string[] { "Name" })]
public SPQuotaTemplatePipeBind Identity
{
get;
set;
}
}
}

In the code example above I'm returning back SPQuotaTemplate objects based on the Identity (or Name) passed into the cmdlet. If the Identity parameter is not provided then all quota templates are returned to the pipeline. In the InternalValidate method I'm checking if the Identity parameter has been provided, and if it has, I set the base class's DataObject property by calling the Read method of the SPQuotaTemplatePipeBind object. In the override RetrieveDataObjects method I then check the DataObject property and return the value as an item in a generic list. If the DataObject property has not been set then I loop through all existing quota templates and return them as generic list. Note that if you are returning lots of items or large items it is better, and preferable, to directly call the WriteResult method and return back null - for this case I know there are typically not a lot of templates and they are not large so I just return back a single collection rather than calling WriteResult.

Pay particular attention to the SPQuotaTemplatePipeBind type - In SharePoint an object can be represented in numerous ways, for example, an SPSite object can be represented by either an URL or a GUID. In order to prevent the need to multiple parameters to support these various types Microsoft has introduced the PipeBind object which eliminates the need for these superfluous parameters and from having to create multiple parameter sets to support them. In the case of the SPQuotaTemplatePipeBind object I can pass in either an actual instance of an SPQuotaTemplate object or a name representing a quota template.

You're not limited to what is available out of the box. You can easily create your own PipeBind objects by simply inheriting from the SPCmdletPipeBind class. Take a look at the following example which demonstrates how to create a custom SPListPipeBind object:

using System;
using System.Collections.Generic;
using Microsoft.SharePoint;
using Microsoft.SharePoint.PowerShell;
using System.Management.Automation;
using System.Globalization;

namespace Lapointe.SharePoint2010.PowerShell.Demo.Lists
{
public sealed class SPListPipeBind : SPCmdletPipeBind<SPList>
{
private bool m_IsAbsoluteUrl;
private bool m_IsCollection;
private Guid m_SiteGuid;
private Guid m_WebGuid;
private Guid m_ListGuid;
private string m_WebUrl;
private string m_ListUrl;

public SPListPipeBind(SPList instance)
: base(instance)
{
}

public SPListPipeBind(Guid guid)
{
this.m_ListGuid = guid;
}

public SPListPipeBind(string inputString)
{
if (inputString != null)
{
inputString = inputString.Trim();
try
{
this.m_ListGuid = new Guid(inputString);
}
catch (FormatException)
{
}
catch (OverflowException)
{
}
if (this.m_ListGuid.Equals(Guid.Empty))
{
this.m_ListUrl = inputString;
if (this.m_ListUrl.StartsWith("http", true, CultureInfo.CurrentCulture))
{
this.m_IsAbsoluteUrl = true;
}
if (WildcardPattern.ContainsWildcardCharacters(this.m_ListUrl))
{
this.m_IsCollection = true;
}
}
}
}

public SPListPipeBind(Uri listUri)
{
this.m_ListUrl = listUri.ToString();
}

protected override void Discover(SPList instance)
{
this.m_ListGuid = instance.ID;
this.m_WebGuid = instance.ParentWeb.ID;
this.m_SiteGuid = instance.ParentWeb.Site.ID;
}

public override SPList Read()
{
return this.Read(null);
}

public SPList Read(SPWeb web)
{
SPList list = null;
string parameterDetails = string.Format(CultureInfo.CurrentCulture, "Id or Url : {0}", new object[] { "Empty or Null" });
if (this.IsCollection)
{
return null;
}
try
{
if (Guid.Empty != this.ListGuid)
{
if (web == null && Guid.Empty != this.m_WebGuid && Guid.Empty != this.m_SiteGuid)
{
parameterDetails = string.Format(CultureInfo.CurrentCulture, "Id or Url: {0} and Web Id: {1}", new object[] { this.ListGuid.ToString(), this.m_WebGuid.ToString() });
using (SPSite site = new SPSite(this.m_SiteGuid))
{
web = site.OpenWeb(this.m_WebGuid);
list = web.Lists[ListGuid];
}
}
else
{
parameterDetails = string.Format(CultureInfo.CurrentCulture, "Id or Url: {0} and web Url {1}", new object[] { this.ListUrl, web.Url });
list = web.Lists[ListGuid];
}
}
else if (!string.IsNullOrEmpty(this.ListUrl))
{
string serverRelativeListUrl = null;
if (this.m_IsAbsoluteUrl)
{
serverRelativeListUrl = Utilities.GetServerRelUrlFromFullUrl(this.ListUrl).Trim('/');
}
else
{
serverRelativeListUrl = this.ListUrl.Trim('/');
}
if (web == null)
{
parameterDetails = string.Format(CultureInfo.CurrentCulture, "Id or Url : {0}", new object[] { this.ListUrl });
using (SPSite site = new SPSite(this.ListUrl))
{
web = site.OpenWeb();
}
}
else
{
parameterDetails = string.Format(CultureInfo.CurrentCulture, "Id or Url : {0} and web Url {1}", new object[] { this.ListUrl, web.Url });
}
if (!web.Exists)
{
list = null;
}
else
{
list = web.GetList(serverRelativeListUrl);
}
}
}
catch (Exception exception)
{
throw new SPCmdletPipeBindException(string.Format("The SPList Pipebind object could not be found ({0}).", parameterDetails), exception);
}
if (list == null)
{
throw new SPCmdletPipeBindException(string.Format("The SPList Pipebind object could not be found ({0}).", parameterDetails));
}
return list;
}

public bool IsCollection
{
get
{
return this.m_IsCollection;
}
}

public Guid ListGuid
{
get
{
return this.m_ListGuid;
}
}

public string ListUrl
{
get
{
return this.m_ListUrl;
}
}
}
}

There are two core components that are required for a custom PipeBind object. The first is to have a constructor that takes in the type that you wish to convert (in this example, a string, URI, or GUID) to the target object. The second is to override the Read method which is used to convert the argument value passed into the constructor into the target type. In some cases you'll need additional information which must be provided by the calling code - for example, if a GUID is passed in, representing the List ID, then you will also need to provide the SPWeb object which contains the List; this is done by creating an overload for the Read method which accepts an SPWeb object. It's up to the calling code to determine which overload to call.

Let's now look at how we can package our SPCmdletGetQuotaTemplate class into a SharePoint Solution Package using Visual Studio 2010.

From a new instance of Visual Studio 2010:

  1. Click File > New > Project to create a new Visual Studio Project
  2. In the New Project dialog select Visual C#/SharePoint/2010 in the Installed Templates panel and then select Empty Project:

    New Project Dialog

  3. After you click OK you will be taken to the SharePoint Configuration Wizard:

    SharePoint Configuration Wizard

    You can specify any site to use for debugging as we won't be using it for PowerShell development (note that when you start the debugger you'll be given a warning if the specified site's web.config does not allow debugging). PowerShell cmdlets must be deployed to the GAC so select Deploy as full-trust solution and click the Finish button to create the project.

The first thing we need to do with our new empty project is to add a couple of project references:

  1. Right-click the References folder in the project and select Add Reference...
  2. In the Add Reference dialog's .NET tab select Microsoft.SharePoint.PowerShell and System.Management.Automation
  3. Click OK to add the references to the project

Now that we have our references added we can setup our project structure. PowerShell cmdlets are not deployed using Features so we can delete the starting Feature folder that is created:

  1. Expand the Features folder
  2. Right-click the Feature1 Feature and click Delete

The next step is to add a SharePoint Mapped Folder:

  1. Right-click the project and click Add > SharePoint Mapped Folder...
  2. Add the {SharePointRoot}/Config/PowerShell/Registration folder
    1. Note that you can add the Format and Help folders as well but I won't be using those in this example as creating help and format files are outside the scope of this article (I usually will add the {SharePointRoot}/Config/PowerShell folder and then manually add the three sub-folders so that I can keep things grouped together in one parent folder within my project).
  3. Click OK to add the mapped folder
    1. If a folder is created under the Registration folder then go ahead and delete it (this sub-folder is automatically added in Beta1 but may not be added come RTM)

In the new Registration mapped folder create a new XML file (you can name it anything you like but I usually give it the same name as my project) and paste the following XML into the file:

<?xml version="1.0" encoding="utf-8" ?>
<ps:Config xmlns:ps="urn:Microsoft.SharePoint.PowerShell"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:Microsoft.SharePoint.PowerShell SPCmdletSchema.xsd">

<ps:Assembly Name="$SharePoint.Project.AssemblyFullName$">
<ps:Cmdlet>
<ps:VerbName>Get-SPQuotaTemplate</ps:VerbName>
<ps:ClassName>Lapointe.SharePoint2010.PowerShell.Demo.Quotas.SPCmdletGetQuotaTemplate</ps:ClassName>
<ps:HelpFile>Lapointe.SharePoint2010.PowerShell.Demo.dll-help.xml</ps:HelpFile>
</ps:Cmdlet>
</ps:Assembly>
</ps:Config>

Note that the <ps:HelpFile /> element does require a value but the file specified does not have to exist.

Now we simply need to paste in the code for the SPCmdletGetQuotaTemplate class from above:

  1. Create a folder below the project root called Quotas
  2. Add a new class file named SPCmdletGetQuotaTemplate.cs
  3. Paste the code from above into this file (be sure to adjust your namespaces in the class file and the XML file if you used a different project name than the one shown)

You now have a complete SharePoint 2010 PowerShell Solution - all that's left is to build and deploy it:

  1. Right-click the project name and select Deploy

Notice what is happening in the output window - IIS application pools are being recycled along with the retraction and deployment of the solution. Because this is a PowerShell solution we don't need IIS to be recycled so let's create a new deployment configuration to remove the recycling of the application pools which should speed up our deployment time:

  1. Right-click the project and select Properties
  2. In the properties dialog select the Deploy tab
  3. In the Edit Configurations group select New to create a new deployment action
  4. Name the new deployment action PowerShell and configure the deployment steps as shown below:Add New Deployment Configuration
  5. Click OK to save the new deployment configuration

Now that we have our custom deployment configuration we need to tell our project to use this configuration. Make sure the Properties Window is visible (type F4 if not) and select the project. Select the PowerShell configuration we just created in the Active Deployment Configuration drop-down.

Our final configuration setting change is to configure the project so that it will open PowerShell when we start the debugger:

  1. Right-click the project and select Properties to return to the project's properties dialog
  2. Click the Debug tab
  3. Select the radio button next to Start external program and specify the following value: C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe
  4. Paste the following into the Command line arguments text box: -NoExit  " & ' C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\CONFIG\POWERSHELL\Registration\sharepoint.ps1 ' "

You can now start the debugger (F5) which will load a PowerShell console and register the SharePoint 2010 snap-in which results in the loading of your new custom cmdlet. To verify that the cmdlet is loaded type Get-Command Get-SPQuotaTemplate | Format-List. You should see the following output:

PS C:\> Get-Command Get-SPQuotaTemplate | Format-List

Name             : Get-SPQuotaTemplate
CommandType      : Cmdlet
Definition       : Get-SPQuotaTemplate [[-Identity] <SPQuotaTemplatePipeBind>]
                   [-AssignmentCollection <SPAssignmentCollection>] [-Verbose]
                   [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <
                   ActionPreference>] [-ErrorVariable <String>] [-WarningVariab
                   le <String>] [-OutVariable <String>] [-OutBuffer <Int32>]

Path             :
AssemblyInfo     :
DLL              : C:\Windows\assembly\GAC_MSIL\Lapointe.SharePoint2010.PowerSh
                   ell.Demo\1.0.0.0__xxxxxxxxxxxxxxxx\Lapointe.SharePoint2010.P
                   owerShell.Demo.dll
HelpFile         : C:\Program Files\Common Files\Microsoft Shared\Web Server Ex
                   tensions\14\CONFIG\PowerShell\Help\Lapointe.SharePoint2010.P
                   owerShell.Demo.dll-help.xml
ParameterSets    : {[[-Identity] <SPQuotaTemplatePipeBind>] [-AssignmentCollect
                   ion <SPAssignmentCollection>] [-Verbose] [-Debug] [-ErrorAct
                   ion <ActionPreference>] [-WarningAction <ActionPreference>]
                   [-ErrorVariable <String>] [-WarningVariable <String>] [-OutV
                   ariable <String>] [-OutBuffer <Int32>]}
ImplementingType : Lapointe.SharePoint2010.PowerShell.Demo.Quotas.SPCmdletGetQu
                   otaTemplate
Verb             : Get
Noun             : SPQuotaTemplate

As you can see, creating and deploying custom PowerShell cmdlets for SharePoint 2010 using Visual Studio 2010 is now super easy. The only complexity lies in the logic of the cmdlet itself.

As you probably expected I have already been hard at work on creating some new cmdlets to replace some of my old PowerShell cmdlets as well as a few select STSADM commands. I'll be releasing these new cmdlets with full source shortly - keep checking back here for more example code and downloads!

26Apr/091

Getting an SPWebApplication object using PowerShell

A SharePoint deployment isn’t much of a deployment if there are no web applications.  A web application in SharePoint contains one or more content databases, each of which can contain one or more site collections, etc., etc.  The SPWebApplication class has tons of methods and properties for directly or indirectly manipulating all things related to web applications - you can do backups, add content databases and site collections, set alert settings, manipulate the web.config file, etc.

There are a couple of different ways in which we can work with the SPWebApplication using PowerShell.  The first is to get a specific object using the static Lookup method and the second, which is useful for looping through all web applications, is to use the SPFarm object’s Service property.  The first approach is shown below:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$webapp = [Microsoft.SharePoint.Administration.SPWebApplication]::Lookup("http://portal")
If you need to loop through all web applications you would write something like the following:
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$farm = [Microsoft.SharePoint.Administration.SPFarm]::Local
$websvcs = $farm.Services | where -FilterScript {$_.GetType() -eq [Microsoft.SharePoint.Administration.SPWebService]}
$webapps = @()
foreach ($websvc in $websvcs) {
    foreach ($webapp in $websvc.WebApplications) {
        $webapps = $webapps + $webapp
    }
}

The code above isn’t the most intuitive but could be easily wrapped into a function to abstract out the complexity (note that there may be a better way to do the above - I’m still figuring this whole PowerShell thing out:) ).  Personally I think the above sucks - it’s not intuitive and it’s difficult to maintain and forces me to do additional filtering if I want a subset of the items returned - thus the motivation behind my Get-SPWebApplication cmdlet.  Here’s a couple examples that do the same thing as the above two examples but using my cmdlet instead:

$webapp = Get-SPWebApplication http://portal
$webapps = Get-SPWebApplication *

On the first line I’m retrieving a specific web application and on the second line I’m using a wildcard character to retrieve all web applications in the farm.  You could easily utilize the wildcard capabilities to reduce the set of web applications that are returned to those matching a specific pattern.   You can also pass in multiple, comma separated URLs such as in the following example:

$webapps = Get-SPWebApplication http://portal,http://mysites

The code for the cmdlet is reasonably simple - most of the work is in dealing with the fact that wildcards are allowed thus making it necessary to loop through all the web applications and the corresponding alternate URL mappings in order to identify the web applications to return:

   1: using System;
   2: using System.Management.Automation;
   3: using Lapointe.SharePoint.PowerShell.Commands.OperationHelpers;
   4: using Lapointe.SharePoint.PowerShell.Commands.Validators;
   5: using Microsoft.SharePoint;
   6: using Microsoft.SharePoint.Administration;
   7:  
   8: namespace Lapointe.SharePoint.PowerShell.Commands.WebApplications
   9: {
  10:     [Cmdlet(VerbsCommon.Get, "SPWebApplication", SupportsShouldProcess=true, DefaultParameterSetName = "Url")]
  11:     public class GetSPWebApplicationCommand : PSCmdletBase
  12:     {
  13:         /// <summary>
  14:         /// Gets or sets the URL.
  15:         /// </summary>
  16:         /// <value>The URL.</value>
  17:         [Parameter(
  18:             ParameterSetName = "Url",
  19:             Mandatory = true,
  20:             Position = 0,
  21:             ValueFromPipeline = true,
  22:             ValueFromPipelineByPropertyName = true,
  23:             HelpMessage = "The URL of the web application to return.  Supports wildcards.")]
  24:         [ValidateNotNullOrEmpty]
  25:         [ValidateUrl(true)]
  26:         public string[] Url { get; set; }
  27:  
  28:  
  29:         /// <summary>
  30:         /// Processes the record.
  31:         /// </summary>
  32:         protected override void ProcessRecordEx()
  33:         {
  34:             foreach (string url in Url)
  35:             {
  36:                 if (!WildcardPattern.ContainsWildcardCharacters(url))
  37:                 {
  38:                     string webApp = url.TrimEnd('/');
  39:                     WriteObject(SPWebApplication.Lookup(new Uri(webApp)));
  40:                 }
  41:                 else
  42:                 {
  43:                     WildcardPattern wildCard = new WildcardPattern(url.TrimEnd('/'), WildcardOptions.IgnoreCase);
  44:                     if (SPFarm.Local == null)
  45:                         throw new SPException(
  46:                             "The SPFarm object is null.  Make sure you are running as a Farm Administrator.");
  47:  
  48:                     foreach (SPService svc in SPFarm.Local.Services)
  49:                     {
  50:                         if (!(svc is SPWebService))
  51:                             continue;
  52:  
  53:                         foreach (SPWebApplication webApp in ((SPWebService) svc).WebApplications)
  54:                         {
  55:                             foreach (SPAlternateUrl altUrl in webApp.AlternateUrls)
  56:                             {
  57:                                 if (wildCard.IsMatch(altUrl.Uri.AbsolutePath.TrimEnd('/')))
  58:                                 {
  59:                                     WriteObject(webApp);
  60:                                     break;
  61:                                 }
  62:                             }
  63:                         }
  64:                     }
  65:                 }
  66:             }
  67:         }
  68:     }
  69: }

Here’s the full help for the cmdlet:

NAME
    Get-SPWebApplication

SYNOPSIS
    Gets one or more SPWebApplication objects representing a SharePoint 2007 Web Application.

SYNTAX
    Get-SPWebApplication [-Url] <String[]> [-WhatIf] [-Confirm] [<CommonParameters>]


DETAILED DESCRIPTION
    Pass in a comma separated list of URLs or a string array of URLs to obtain a collection of SPWebAppl
    ication objects.

    Copyright 2008 Gary Lapointe
      > For more information on these PowerShell cmdlets:
      > http://blog.falchionconsulting.com/
      > Use of these cmdlets is at your own risk.
      > Gary Lapointe assumes no liability.


PARAMETERS
    -Url <String[]>
        Specifies the URL of the web application(s) to retrieve. Wildcards are permitted. If you specify
         multiple URLs, use commas to separate the URLs.

        Required?                    true
        Position?                    1
        Default value
        Accept pipeline input?       true (ByValue, ByPropertyName)
        Accept wildcard characters?  false

    -WhatIf


        Required?                    false
        Position?                    named
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -Confirm


        Required?                    false
        Position?                    named
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    <CommonParameters>
        This cmdlet supports the common parameters: -Verbose, -Debug,
        -ErrorAction, -ErrorVariable, and -OutVariable. For more information,
        type, "get-help about_commonparameters".

INPUT TYPE
    String


RETURN TYPE
    Collection of SPWebApplication objects.


NOTES


        For more information, type "Get-Help Get-SPWebApplication -detailed". For technical information,
         type "Get-Help Get-SPWebApplication -full".

    --------------  EXAMPLE 1 --------------

    C:\PS>$webapp = get-spwebapplication -url http://portal


    This example returns back a single SPWebApplication object.






RELATED LINKS

http://blog.falchionconsulting.com

25Apr/090

PowerShell CmdLet Name Changes

Ever since I released my PowerShell CmdLets I’ve been unhappy about my choice to use the -gl in the name of the cmdlet.  I felt it would be useful for numerous reasons but I didn’t like that it “broke the rules” of cmdlet naming conventions.  And then Microsoft announced, via the PowerShell team blog, that some code will be added to V2 to enforce the prescribed naming conventions.  You can find the post here: http://blogs.msdn.com/powershell/archive/2009/04/16/increasing-visibility-of-cmdlet-design-guidelines.aspx.  The specific piece of concern is that a warning will be generated if more than one hyphen is present in the cmdlet name.

As a result of this enforcement of the guidance and in anticipation of eventually having a V2 version of my cmdlets I decided to go ahead and pull the -gl suffix from my cmdlet names.  I thought about keeping an alias in place so that existing scripts would not break but in the end decided that there was probably a pretty small user base actually using my them and that it would be better to just get people to make the move.  I will try and update my existing PowerShell posts to account for the removal of the name (guess it’s a good thing I’m behind on documenting most of them :) ).

25Apr/093

Getting an SPFarm object using PowerShell

There are several core SharePoint objects that PowerShell programmers may need to work with in order to manipulate SharePoint via PowerShell scripts.  Getting these objects is pretty simple but not all that intuitive to users who are still trying to learn PowerShell and the SharePoint API.

The SPFarm object is the top level object for working with SharePoint and it provides access to all the global settings for all servers, services, and solutions that are installed in the farm.  To get an SPFarm object and see the public properties of that object you can either load up the Microsoft.SharePoint assembly and call the static “Local” property of the SPFarm class or use my new cmdlet, Get-SPFarm.  The first approach is shown below (note that you could of course easily move this into a function in a script and achieve the same thing as my cmdlet):

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$farm = [Microsoft.SharePoint.Administration.SPFarm]::Local
$farm

The problem I have with the above code is that it’s just ugly so I created a real simple cmdlet that allows getting the SPFarm object with one line of code (which, again, could be achieved using a function in a script):

$farm = Get-SPFarm
$farm

Isn’t that much cleaner and easier to read:)?  The results of running the above code are shown below:

AlternateUrlCollections       : {Central Administration, SharePoint My Sites (80), SharePoint Portal (80), SharePoint Shared Services Admin (80)}
Servers                       : {SHAREPOINT1, sharepoint1.spdev.com, spsql1}
Services                      : {, , WSS_Administration, ...}
TimerService                  : SPTimerService Name=SPTimerV3 Parent=SPFarm Name=SharePoint_ConfigDB
Solutions                     : {lapointe.sharepoint.stsadm.commands.wsp}
TypeName                      : Farm
PairConnectionString          : 
IsPaired                      : False
CanMigrate                    : False
PersistedFileChunkSize        : 4194304
ErrorReportingAutomaticUpload : False
FeatureDefinitions            : {FeatureDefinition/001f4bd7-746d-403b-aa09-a6cc43de7942, FeatureDefinition/00bfea71-1c5e-4a24-b310-ba51c3eb7a57, FeatureDefinition/00bfea71-1e1d-4562-b56a-f05371bb0115, FeatureDefinition/00bfea71-2062-426c-90bf-714c59600103...}
BuildVersion                  : 12.0.0.6318
ErrorReportingEnabled         : True
DownloadErrorReportingUpdates : False
CEIPEnabled                   : False
TraceSessionGuid              : 89c8c935-99ff-48ce-9376-31daaaf32b85
ExternalBinaryStoreClassId    : 00000000-0000-0000-0000-000000000000
DiskSizeRequired              : 0
CanSelectForBackup            : True
CanRenameOnRestore            : False
CanSelectForRestore           : True
CanUpgrade                    : True
NeedsUpgradeIncludeChildren   : False
NeedsUpgrade                  : False
UpgradeContext                : Microsoft.SharePoint.Upgrade.SPUpgradeContext
Name                          : SharePoint_ConfigDB
DisplayName                   : SharePoint_ConfigDB
Id                            : 1e94781b-07f0-4f79-831e-235e0b17518c
Status                        : Online
Parent                        : SPFarm Name=SharePoint_ConfigDB
Version                       : 2279
Properties                    : {}
Farm                          : SPFarm Name=SharePoint_ConfigDB
UpgradedPersistedProperties   : {}

Obviously this is a pretty simple cmdlet and there’s not a whole of lot of advantages to doing this as a cmdlet instead of a function in a script.  The reason I went the cmdlet route for this (and the many others that I will be documenting) versus just a function is because I wanted to be able to access all my building block “stuff” in a consistent way and I wanted features such as parameter sets (which don’t make sense here but do in a lot of the others that I have).

Here’s an example script that displays all the services running on each server in the farm:

$farm = Get-SPFarm
foreach ($svr in $farm.Servers) {
    Write-Host($svr.DisplayName)
    Write-Host("-----------------------------")
    foreach ($svc in $svr.ServiceInstances) {
        Write-Host($svc.TypeName)
    }
    Write-Host("");
}

The code above produces output similar to the following:

SHAREPOINT1
-----------------------------
Session State
Windows SharePoint Services Search
Information Management Policy Configuration Service
Office SharePoint Server Search
Shared Services Timer
Office SharePoint Server Search Admin Web Service
Excel Calculation Services
Single Sign-on Service
SSP Job Control Service
Portal Service
Business Data Catalog
Office SharePoint Server Search
Document Conversions Launcher Service
Document Conversions Load Balancer Service
Windows SharePoint Services Web Application
Central Administration
Windows SharePoint Services Incoming E-Mail
Windows SharePoint Services Administration
Windows SharePoint Services Search
Windows SharePoint Services Timer
Office SharePoint Usage Analytics Service

sharepoint1.spdev.com
-----------------------------
Windows SharePoint Services Outgoing E-Mail

spsql1
-----------------------------
Windows SharePoint Services Database

The code for the cmdlet is extremely simple.  It takes no parameters and simply writes out the SPFarm.Local property:

   1: using System.Management.Automation;
   2: using Lapointe.SharePoint.PowerShell.Commands.OperationHelpers;
   3: using Microsoft.SharePoint.Administration;
   4:  
   5: namespace Lapointe.SharePoint.PowerShell.Commands.Farm
   6: {
   7:     [Cmdlet(VerbsCommon.Get, "SPFarm", SupportsShouldProcess=true)]
   8:     public class GetSPFarmCommand : PSCmdletBase
   9:     {
  10:  
  11:         /// <summary>
  12:         /// Processes the record.
  13:         /// </summary>
  14:         protected override void ProcessRecordEx()
  15:         {
  16:             WriteObject(SPFarm.Local);
  17:         }
  18:     }
  19: }

You can see the help for the Get-SPFarm cmdlet by typing “get-help get-spfarm -full”:

NAME
    Get-SPFarm

SYNOPSIS
    Gets an SPFarm object representing a SharePoint 2007 server farm.


SYNTAX
    Get-SPFarm [-WhatIf] [-Confirm] [<CommonParameters>]


DETAILED DESCRIPTION
    Copyright 2008 Gary Lapointe
      > For more information on these PowerShell cmdlets:
      > http://blog.falchionconsulting.com/
      > Use of these cmdlets is at your own risk.
      > Gary Lapointe assumes no liability.


RELATED LINKS

http://blog.falchionconsulting.com

REMARKS
    For more information, type: "get-help Get-SPFarm -detailed".
    For technical information, type: "get-help Get-SPFarm -full".




NAME
    Get-SPFarm

SYNOPSIS
    Gets an SPFarm object representing a SharePoint 2007 server farm.

SYNTAX
    Get-SPFarm [-WhatIf] [-Confirm] [<CommonParameters>]


DETAILED DESCRIPTION
    Copyright 2008 Gary Lapointe
      > For more information on these PowerShell cmdlets:
      > http://blog.falchionconsulting.com/
      > Use of these cmdlets is at your own risk.
      > Gary Lapointe assumes no liability.


PARAMETERS
    -WhatIf


        Required?                    false
        Position?                    named
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -Confirm


        Required?                    false
        Position?                    named
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    <CommonParameters>
        This cmdlet supports the common parameters: -Verbose, -Debug,
        -ErrorAction, -ErrorVariable, and -OutVariable. For more information,
        type, "get-help about_commonparameters".

INPUT TYPE



RETURN TYPE
    SPFarm


NOTES


        For more information, type "Get-Help Get-SPFarm -detailed". For technical information,
        type "Get-Help Get-SPFarm -full".


RELATED LINKS

http://blog.falchionconsulting.com

8Apr/094

Working with SPWeb(Info) Objects Using PowerShell

I know, I know, I’m way behind on documenting my PowerShell cmdlets – I will be striving to get them done as soon as possible.  I’ve already documented one of them, the Get-SPSite cmdlet, and I will now continue with the Get-SPWeb cmdlet.  Hopefully I’ll be able to wrap up the others much quicker as they are a lot simpler – then I can finally start building new ones :)

Like the Get-SPSite cmdlet the Get-SPWeb cmdlet addresses some common issues found with working with SPWeb objects.  For additional details about the disposal problem when working with many common SharePoint objects via PowerShell see the post about the Get-SPSite cmdlet.  For those that aren’t familiar with the SPWeb object (Microsoft.SharePoint.SPWeb), it’s the equivalent programmatic element for working with sites within site collections.

First lets look at the SPWebInfo object and some its uses, the Get-SPWeb cmdlet is extremely simple so I’ll save it for last.  Consider the following code snippet:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$site = New-Object Microsoft.SharePoint.SPSite("http://portal")
foreach ($web in $site.AllWebs) {
    Write-Host $web.Url
}

The above code results in a memory leak.  If you don’t re-loop through each SPWeb object in the collection and dispose of the object by calling the “Dispose()” method you will end up with unmanaged resources left in memory that could eventually cause issues if you do a lot of processing like this (eventually the GC will dispose of the objects but that could take quite some time).  The same issue is true for the SPSite object.

Another option would be to use a different approach to get all the SPWeb objects within a site collection, an approach that can be used for more dynamic querying of objects and returns back an object that would not require disposal – an SPWebInfo object.  Here’s an example of how you could do something similar to the above using my Get-SPSite cmdlet and the AllWebs property that the SPSiteInfo object exposes:

$site = Get-SPSite("http://portal")
foreach ($web in $site.AllWebs) {
    Write-Host $web.Url
}

The above code still uses the AllWebs property but in this case it’s part of my SPSiteInfo object so rather than exposing a collection of SPWeb objects it returns a collection of SPWebInfo objects.  As a result you no longer need to dispose of the objects returned.

The one obvious downside of this approach is that when you access the property I have to loop through all the webs internally and copy their data to my custom object before I can return back to the calling application.  Typically though, we’re more concerned about flexibility and ease of use rather than performance when performing the simple administrative tasks that we’d be looking to perform using PowerShell.

What I like about the approach I put together is that I can now do filtered queries without having to worry about whether or not I disposed of the objects.  Here’s an example of how to find all the webs that use a specific site template (in this case “STS”):

$site = Get-SPSite("http://portal")
$webs = $site.AllWebs | where -FilterScript {$_.WebTemplate -eq "STS"}
foreach ($web in $webs) {
    Write-Host $web.Url
}

Or I can simply get a specific SPWebInfo (and therefore the SPWeb) object by calling the Get-SPWeb cmdlet directly (I don’t currently support wildcards so this is only helpful for getting a specific web instance):

$web = Get-SPWeb "http://devthekey"
Write-Host $web.WebTemplate

It’s important to remember that the SPWebInfo object is meant to be read-only as most of the properties are just copies of the variables but there are some exceptions such as the SPListCollection object returned by the Lists property or the SPRecycleBinItemCollection object returned by the RecycleBin property.  In general, if you have to call the Update() method of the SPWeb object to save your changes then you have to use the actual SPWeb object, otherwise you can work directly with the SPWebInfo object and forego the need to instantiate and dispose of the SPWeb object.

What about when you do need to access the actual SPWeb object?  There are two approaches for this: the first is to use the SPBase property which will create a new SPWeb instance and store that instance as a private member variable for future access to the property thus avoiding the overhead of creating another instance on subsequent calls; the second is to use the GetSPObject() method which creates a new instance of the SPWeb object but does not store a copy so it’s a nice easy way to get an entirely new instance of the actual SPWeb object (useful for when you’ve made a change which requires a reload due to caching).  In both cases you are responsible for disposing of the returned object.

The following code snippet shows the SPWebInfo class:

   1: using System;
   2: using System.Collections;
   3: using System.Collections.Generic;
   4: using System.Globalization;
   5: using System.Linq;
   6: using System.Text;
   7: using System.Web.Configuration;
   8: using Microsoft.SharePoint;
   9: using Microsoft.SharePoint.Administration;
  10: using Microsoft.SharePoint.Navigation;
  11: using Microsoft.SharePoint.Utilities;
  12: using Microsoft.SharePoint.Workflow;
  13:  
  14: namespace Lapointe.SharePoint.PowerShell.Commands.Proxies
  15: {
  16:     public class SPWebInfo : ISPInfo
  17:     {
  18:         private SPSite m_Site;
  19:         private SPWeb m_Web;
  20:         private List<SPWebInfo> m_Webs;
  21:         private Guid m_FirstUniqueAncestorWeb;
  22:         private Guid m_FirstUniqueRoleDefinitionWeb;
  23:         private SPListCollection m_Lists;
  24:         private SPListTemplateCollection m_ListTemplates;
  25:         private SPFeatureCollection m_Features;
  26:  
  27:         internal SPWebInfo(SPWeb web)
  28:         {
  29:             ID = web.ID;
  30:             Alerts = web.Alerts;
  31:             AllowAnonymousAccess = web.AllowAnonymousAccess;
  32:             AllowAutomaticASPXPageIndexing = web.AllowAutomaticASPXPageIndexing;
  33:             AllowRssFeeds = web.AllowRssFeeds;
  34:             AllowUnsafeUpdates = web.AllowUnsafeUpdates;
  35:             AllProperties = web.AllProperties;
  36:             AllRolesForCurrentUser = web.AllRolesForCurrentUser;
  37:             AllUsers = web.AllUsers;
  38:             AllWebTemplatesAllowed = web.AllWebTemplatesAllowed;
  39:             AlternateCssUrl = web.AlternateCssUrl;
  40:             AlternateHeader = web.AlternateHeader;
  41:             AnonymousPermMask64 = web.AnonymousPermMask64;
  42:             AnonymousState = web.AnonymousState;
  43:             ASPXPageIndexed = web.ASPXPageIndexed;
  44:             ASPXPageIndexMode = web.ASPXPageIndexMode;
  45:             AssociatedGroups = web.AssociatedGroups;
  46:             AssociatedMemberGroup = web.AssociatedMemberGroup;
  47:             AssociatedOwnerGroup = web.AssociatedOwnerGroup;
  48:             AssociatedVisitorGroup = web.AssociatedVisitorGroup;
  49:             Audit = web.Audit;
  50:             AuthenticationMode = web.AuthenticationMode;
  51:             Author = web.Author;
  52:             AvailableContentTypes = web.AvailableContentTypes;
  53:             AvailableFields = web.AvailableFields;
  54:             CacheAllSchema = web.CacheAllSchema;
  55:             Configuration = web.Configuration;
  56:             ContentTypes = web.ContentTypes;
  57:             Created = web.Created;
  58:             CurrencyLocaleID = web.CurrencyLocaleID;
  59:             CurrentChangeToken = web.CurrentChangeToken;
  60:             CurrentUser = web.CurrentUser;
  61:             CustomMasterUrl = web.CustomMasterUrl;
  62:             DataRetrievalServicesSettings = web.DataRetrievalServicesSettings;
  63:             Description = web.Description;
  64:             try
  65:             {
  66:                 DocTemplates = web.DocTemplates;
  67:             }
  68:             catch (SPException) {}
  69:             EffectiveBasePermissions = web.EffectiveBasePermissions;
  70:             EffectivePresenceEnabled = web.EffectivePresenceEnabled;
  71:             EventHandlersEnabled = web.EventHandlersEnabled;
  72:             EventReceivers = web.EventReceivers;
  73:             ExecuteUrl = web.ExecuteUrl;
  74:             Exists = web.Exists;
  75:             ExternalSecurityProviderSetting = web.ExternalSecurityProviderSetting;
  76:             Fields = web.Fields;
  77:             FieldTypeDefinitionCollection = web.FieldTypeDefinitionCollection;
  78:             Files = web.Files;
  79:             //FirstUniqueAncestor = web.FirstUniqueAncestor;
  80:             Folders = web.Folders;
  81:             Groups = web.Groups;
  82:             HasExternalSecurityProvider = web.HasExternalSecurityProvider;
  83:             HasUniqueRoleAssignments = web.HasUniqueRoleAssignments;
  84:             HasUniqueRoleDefinitions = web.HasUniqueRoleDefinitions;
  85:             IncludeSupportingFolders = web.IncludeSupportingFolders;
  86:             IsADAccountCreationMode = web.IsADAccountCreationMode;
  87:             IsADEmailEnabled = web.IsADEmailEnabled;
  88:             IsRootWeb = web.IsRootWeb;
  89:             Language = web.Language;
  90:             LastItemModifiedDate = web.LastItemModifiedDate;
  91:             Locale = web.Locale;
  92:             MasterUrl = web.MasterUrl;
  93:             Modules = web.Modules;
  94:             Name = web.Name;
  95:             Navigation = web.Navigation;
  96:             NoCrawl = web.NoCrawl;
  97:             //SPWeb ParentWeb = web;
  98:             ParentWebId = web.ParentWebId;
  99:             ParserEnabled = web.ParserEnabled;
 100:             PortalMember = web.PortalMember;
 101:             PortalName = web.PortalName;
 102:             PortalSubscriptionUrl = web.PortalSubscriptionUrl;
 103:             PortalUrl = web.PortalUrl;
 104:             PresenceEnabled = web.PresenceEnabled;
 105:             Properties = web.Properties;
 106:             Provisioned = web.Provisioned;
 107:             PublicFolderRootUrl = web.PublicFolderRootUrl;
 108:             QuickLaunchEnabled = web.QuickLaunchEnabled;
 109:             RecycleBin = web.RecycleBin;
 110:             RegionalSettings = web.RegionalSettings;
 111:             RequestAccessEmail = web.RequestAccessEmail;
 112:             RequestAccessEnabled = web.RequestAccessEnabled;
 113:             ReusableAcl = web.ReusableAcl;
 114:             RoleAssignments = web.RoleAssignments;
 115:             RoleDefinitions = web.RoleDefinitions;
 116:             RootFolder = web.RootFolder;
 117:             ServerRelativeUrl = web.ServerRelativeUrl;
 118:             Site = web.Site.ID;
 119:             SiteAdministrators = web.SiteAdministrators;
 120:             SiteGroups = web.SiteGroups;
 121:             SiteLogoDescription = web.SiteLogoDescription;
 122:             SiteLogoUrl = web.SiteLogoUrl;
 123:             SiteUserInfoList = web.SiteUserInfoList;
 124:             SiteUsers = web.SiteUsers;
 125:             SyndicationEnabled = web.SyndicationEnabled;
 126:             Theme = web.Theme;
 127:             ThemeCssUrl = web.ThemeCssUrl;
 128:             Title = web.Title;
 129:             TreeViewEnabled = web.TreeViewEnabled;
 130:             Url = web.Url;
 131:             UserIsSiteAdmin = web.UserIsSiteAdmin;
 132:             UserIsWebAdmin = web.UserIsWebAdmin;
 133:             Users = web.Users;
 134:             ViewStyles = web.ViewStyles;
 135:             //SPWebCollection Webs = web;
 136:             WebTemplate = web.WebTemplate;
 137:             WebTemplateId = web.WebTemplateId;
 138:             WorkflowTemplates = web.WorkflowTemplates;
 139:         }
 140:  
 141:         /// <summary>
 142:         /// Returns a newly created instance of the object on the first access.  Subsequent accesses will utilize an internal member variable.
 143:         /// The caller is responsible for disposing of the returned SPWeb object and it's parent SPSite object (web.Site.Dispose()).
 144:         /// </summary>
 145:         /// <value>The SP base.</value>
 146:         public IDisposable SPBase
 147:         {
 148:             get
 149:             {
 150:                 if (m_Web == null)
 151:                 {
 152:                     if (m_Site == null)
 153:                         m_Site = new SPSite(Site);
 154:  
 155:                     m_Web = m_Site.OpenWeb(ID);
 156:                 }
 157:                 return m_Web;
 158:             }
 159:         }
 160:  
 161:         /// <summary>
 162:         /// Returns a newly created instance of the object every time without storing an internal member variable for subsequent access.
 163:         /// The caller is responsible for disposing of the returned SPWeb object and it's parent SPSite object (web.Site.Dispose()).
 164:         /// </summary>
 165:         /// <returns></returns>
 166:         public IDisposable GetSPObject()
 167:         {
 168:             return new SPSite(Site).OpenWeb(ID);
 169:         }
 170:  
 171:         public SPAlertCollection Alerts { get; internal set; }
 172:         public bool AllowAnonymousAccess { get; internal set; }
 173:         public bool AllowAutomaticASPXPageIndexing { get; internal set; }
 174:         public bool AllowRssFeeds { get; internal set; }
 175:         public bool AllowUnsafeUpdates { get; internal set; }
 176:         public Hashtable AllProperties { get; internal set; }
 177:         public SPRoleDefinitionBindingCollection AllRolesForCurrentUser { get; internal set; }
 178:         public SPUserCollection AllUsers { get; internal set; }
 179:         public bool AllWebTemplatesAllowed { get; internal set; }
 180:         public string AlternateCssUrl { get; internal set; }
 181:         public string AlternateHeader { get; internal set; }
 182:         public SPBasePermissions AnonymousPermMask64 { get; internal set; }
 183:         public SPWeb.WebAnonymousState AnonymousState { get; internal set; }
 184:         public bool ASPXPageIndexed { get; internal set; }
 185:         public WebASPXPageIndexMode ASPXPageIndexMode { get; internal set; }
 186:         public IList<SPGroup> AssociatedGroups { get; internal set; }
 187:         public SPGroup AssociatedMemberGroup { get; internal set; }
 188:         public SPGroup AssociatedOwnerGroup { get; internal set; }
 189:         public SPGroup AssociatedVisitorGroup { get; internal set; }
 190:         public SPAudit Audit { get; internal set; }
 191:         public AuthenticationMode AuthenticationMode { get; internal set; }
 192:         public SPUser Author { get; internal set; }
 193:         public SPContentTypeCollection AvailableContentTypes { get; internal set; }
 194:         public SPFieldCollection AvailableFields { get; internal set; }
 195:         public bool CacheAllSchema { get; internal set; }
 196:         public short Configuration { get; internal set; }
 197:         public SPContentTypeCollection ContentTypes { get; internal set; }
 198:         public DateTime Created { get; internal set; }
 199:         public int CurrencyLocaleID { get; internal set; }
 200:         public SPChangeToken CurrentChangeToken { get; internal set; }
 201:         public SPUser CurrentUser { get; internal set; }
 202:         public string CustomMasterUrl { get; internal set; }
 203:         public SPDataRetrievalServicesSettings DataRetrievalServicesSettings { get; internal set; }
 204:         public string Description { get; internal set; }
 205:         public SPDocTemplateCollection DocTemplates { get; internal set; }
 206:         public SPBasePermissions EffectiveBasePermissions { get; internal set; }
 207:         public bool EffectivePresenceEnabled { get; internal set; }
 208:         public bool EventHandlersEnabled { get; internal set; }
 209:         public SPEventReceiverDefinitionCollection EventReceivers { get; internal set; }
 210:         public string ExecuteUrl { get; internal set; }
 211:         public bool Exists { get; internal set; }
 212:         public string ExternalSecurityProviderSetting { get; internal set; }
 213:         public SPFeatureCollection Features
 214:         {
 215:             get
 216:             {
 217:                 if (m_Features != null)
 218:                     return m_Features;
 219:  
 220:                 using (SPSite site = new SPSite(Site))
 221:                 using (SPWeb web = site.OpenWeb(ID))
 222:                 {
 223:                     m_Features = web.Features;
 224:                 }
 225:                 return m_Features;
 226:  
 227:             }
 228:         }
 229:         public SPFieldCollection Fields { get; internal set; }
 230:         public SPFieldTypeDefinitionCollection FieldTypeDefinitionCollection { get; internal set; }
 231:         public SPFileCollection Files { get; internal set; }
 232:         public Guid FirstUniqueAncestorWeb
 233:         {
 234:             get
 235:             {
 236:                 if (m_FirstUniqueAncestorWeb != Guid.Empty)
 237:                     return m_FirstUniqueAncestorWeb;
 238:  
 239:                 using (SPSite site = new SPSite(Site))
 240:                 using (SPWeb web = site.OpenWeb(ID))
 241:                 using (SPWeb ancestor = web.FirstUniqueAncestorWeb)
 242:                 {
 243:                     m_FirstUniqueAncestorWeb = ancestor.ID;
 244:                 }
 245:                 return m_FirstUniqueAncestorWeb;
 246:  
 247:             }
 248:         }
 249:         public Guid FirstUniqueRoleDefinitionWeb
 250:         {
 251:             get
 252:             {
 253:                 if (m_FirstUniqueRoleDefinitionWeb != Guid.Empty)
 254:                     return m_FirstUniqueRoleDefinitionWeb;
 255:  
 256:                 using (SPSite site = new SPSite(Site))
 257:                 using (SPWeb web = site.OpenWeb(ID))
 258:                 using (SPWeb ancestor = web.FirstUniqueRoleDefinitionWeb)
 259:                 {
 260:                     m_FirstUniqueRoleDefinitionWeb = ancestor.ID;
 261:                 }
 262:                 return m_FirstUniqueRoleDefinitionWeb;
 263:  
 264:             }
 265:         }
 266:         public SPFolderCollection Folders { get; internal set; }
 267:         public SPGroupCollection Groups { get; internal set; }
 268:         public bool HasExternalSecurityProvider { get; internal set; }
 269:         public bool HasUniqueRoleAssignments { get; internal set; }
 270:         public bool HasUniqueRoleDefinitions { get; internal set; }
 271:         public Guid ID { get; internal set; }
 272:         public bool IncludeSupportingFolders { get; internal set; }
 273:         public bool IsADAccountCreationMode { get; internal set; }
 274:         public bool IsADEmailEnabled { get; internal set; }
 275:         public bool IsRootWeb { get; internal set; }
 276:         public uint Language { get; internal set; }
 277:         public DateTime LastItemModifiedDate { get; internal set; }
 278:         public SPListCollection Lists
 279:         {
 280:             get
 281:             {
 282:                 if (m_Lists != null)
 283:                     return m_Lists;
 284:  
 285:                 using (SPSite site = new SPSite(Site))
 286:                 using (SPWeb web = site.OpenWeb(ID))
 287:                 {
 288:                     m_Lists = web.Lists;
 289:                 }
 290:                 return m_Lists;
 291:  
 292:             }
 293:         }
 294:         public SPListTemplateCollection ListTemplates
 295:         {
 296:             get
 297:             {
 298:                 if (m_ListTemplates != null)
 299:                     return m_ListTemplates;
 300:  
 301:                 using (SPSite site = new SPSite(Site))
 302:                 using (SPWeb web = site.OpenWeb(ID))
 303:                 {
 304:                     m_ListTemplates = web.ListTemplates;
 305:                 }
 306:                 return m_ListTemplates;
 307:  
 308:             }
 309:         }
 310:         public CultureInfo Locale { get; internal set; }
 311:         public string MasterUrl { get; internal set; }
 312:         public SPModuleCollection Modules { get; internal set; }
 313:         public string Name { get; internal set; }
 314:         public SPNavigation Navigation { get; internal set; }
 315:         public bool NoCrawl { get; internal set; }
 316:         public SPWeb ParentWeb { get; internal set; }
 317:         public Guid ParentWebId { get; internal set; }
 318:         public bool ParserEnabled { get; internal set; }
 319:         public bool PortalMember { get; internal set; }
 320:         public string PortalName { get; internal set; }
 321:         public string PortalSubscriptionUrl { get; internal set; }
 322:         public string PortalUrl { get; internal set; }
 323:         public bool PresenceEnabled { get; internal set; }
 324:         public SPPropertyBag Properties { get; internal set; }
 325:         public bool Provisioned { get; internal set; }
 326:         public string PublicFolderRootUrl { get; internal set; }
 327:         public bool QuickLaunchEnabled { get; internal set; }
 328:         public SPRecycleBinItemCollection RecycleBin { get; internal set; }
 329:         public SPRegionalSettings RegionalSettings { get; internal set; }
 330:         public string RequestAccessEmail { get; internal set; }
 331:         public bool RequestAccessEnabled { get; internal set; }
 332:         public SPReusableAcl ReusableAcl { get; internal set; }
 333:         public SPRoleAssignmentCollection RoleAssignments { get; internal set; }
 334:         public SPRoleDefinitionCollection RoleDefinitions { get; internal set; }
 335:         public SPFolder RootFolder { get; internal set; }
 336:         public string ServerRelativeUrl { get; internal set; }
 337:         public Guid Site { get; internal set; }
 338:         public SPUserCollection SiteAdministrators { get; internal set; }
 339:         public SPGroupCollection SiteGroups { get; internal set; }
 340:         public string SiteLogoDescription { get; internal set; }
 341:         public string SiteLogoUrl { get; internal set; }
 342:         public SPList SiteUserInfoList { get; internal set; }
 343:         public SPUserCollection SiteUsers { get; internal set; }
 344:         public bool SyndicationEnabled { get; internal set; }
 345:         public string Theme { get; internal set; }
 346:         public string ThemeCssUrl { get; internal set; }
 347:         public string Title { get; internal set; }
 348:         public bool TreeViewEnabled { get; internal set; }
 349:         public string Url { get; internal set; }
 350:         public bool UserIsSiteAdmin { get; internal set; }
 351:         public bool UserIsWebAdmin { get; internal set; }
 352:         public SPUserCollection Users { get; internal set; }
 353:         public SPViewStyleCollection ViewStyles { get; internal set; }
 354:         public List<SPWebInfo> Webs
 355:         {
 356:             get
 357:             {
 358:                 if (m_Webs != null)
 359:                     return m_Webs;
 360:  
 361:                 m_Webs = new List<SPWebInfo>();
 362:                 using (SPSite site = new SPSite(Site))
 363:                 using (SPWeb web = site.OpenWeb(ID))
 364:                 {
 365:                     foreach (SPWeb subWeb in web.Webs)
 366:                     {
 367:                         try
 368:                         {
 369:                             m_Webs.Add(new SPWebInfo(subWeb));
 370:                         }
 371:                         finally
 372:                         {
 373:                             subWeb.Dispose();
 374:                         }
 375:                     }
 376:                 }
 377:                 return m_Webs;
 378:  
 379:             }
 380:         }
 381:         public string WebTemplate { get; internal set; }
 382:         public int WebTemplateId { get; internal set; }
 383:         public SPWorkflowTemplateCollection WorkflowTemplates { get; internal set; }
 384:    }
 385: }

The following is the code of the core Get-SPWeb cmdlet:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Management.Automation;
   4: using Lapointe.SharePoint.PowerShell.Commands.OperationHelpers;
   5: using Lapointe.SharePoint.PowerShell.Commands.Proxies;
   6: using Lapointe.SharePoint.PowerShell.Commands.Validators;
   7: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
   8: using Microsoft.SharePoint;
   9:  
  10: namespace Lapointe.SharePoint.PowerShell.Commands.Webs
  11: {
  12:     [Cmdlet(VerbsCommon.Get, "SPWeb", SupportsShouldProcess=true, DefaultParameterSetName = "Url")]
  13:     public class GetSPWebCommand : PSCmdletBase
  14:     {
  15:         /// <summary>
  16:         /// Gets or sets the URL.
  17:         /// </summary>
  18:         /// <value>The URL.</value>
  19:         [Parameter(
  20:             ParameterSetName = "Url",
  21:             Mandatory = true,
  22:             Position = 0,
  23:             ValueFromPipeline = true,
  24:             ValueFromPipelineByPropertyName = true,
  25:             HelpMessage = "The URL of the web to return")]
  26:         [ValidateNotNullOrEmpty]
  27:         [ValidateUrl(false)]
  28:         public string[] Url { get; set; }
  29:  
  30:  
  31:         /// <summary>
  32:         /// Processes the record.
  33:         /// </summary>
  34:         protected override void ProcessRecordEx()
  35:         {
  36:             foreach (string url in Url)
  37:             {
  38:                 string siteUrl = url.TrimEnd('/');
  39:                 using (SPSite site = new SPSite(siteUrl))
  40:                 using (SPWeb web = site.AllWebs[Utilities.GetServerRelUrlFromFullUrl(siteUrl)])
  41:                 {
  42:                     WriteObject(new SPWebInfo(web));
  43:                 }
  44:             }
  45:         }
  46:     }
  47: }

The following is the full help for the cmdlet.

PS C:\> get-help get-spweb -full

NAME
    Get-SPWeb

SYNOPSIS
    Gets one or more SPWebInfo objects representing a SharePoint 2007 Web.

SYNTAX
    Get-SPWeb [-Url] <String[]> [-WhatIf] [-Confirm] [<CommonParameters>]


DETAILED DESCRIPTION
    Pass in a comma separated list of URLs or a string array of URLs to obtain a collection of SPWebInfo objects.  Thes
    e objects do not need to be disposed.

    The SPWebInfo object that is returned contains almost all of the same properties of the SPWeb object but does not r
    equire disposal and should be generally considered read-only.  You can get to the actual SPWeb object by using the
    SPBase property or the GetSPObject() method.  The SPBase property results in a copy of the SPWeb object being persi
    sted in the SPWebInfo object for faster access on future calls.  Always remember to dispose of the SPWeb object if
    used. Some collection properties may be directly updated without the need to access the SPSite object.

    Copyright 2008 Gary Lapointe
      > For more information on these PowerShell cmdlets:
      > http://stsadm.blogspot.com/
      > Use of these cmdlets is at your own risk.
      > Gary Lapointe assumes no liability.


PARAMETERS
    -Url <String[]>
        Specifies the URL of the web(s) to retrieve. Wildcards are not permitted. If you specify multiple URLs, use com
        mas to separate the URLs.

        Required?                    true
        Position?                    1
        Default value
        Accept pipeline input?       true (ByValue, ByPropertyName)
        Accept wildcard characters?  false

    -WhatIf


        Required?                    false
        Position?                    named
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -Confirm


        Required?                    false
        Position?                    named
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    <CommonParameters>
        This cmdlet supports the common parameters: -Verbose, -Debug,
        -ErrorAction, -ErrorVariable, and -OutVariable. For more information,
        type, "get-help about_commonparameters".

INPUT TYPE
    String


RETURN TYPE
    Collection of SPWebInfo objects.


NOTES


        For more information, type "Get-Help Get-SPWeb -detailed". For technical information, type "Get-Help Get-SPW
        eb -full".

    --------------  EXAMPLE 1 --------------

    C:\PS>$web = get-spweb -url http://portal


    This example returns back a single SPWebInfo object.






RELATED LINKS

http://stsadm.blogspot.com

Note that if you receive an exception during the execution of this cmdlet simply pass in the “-debug” parameter in order to display the full stack trace which you can use to either debug yourself or report back to here to help me improve the code.