MOSS MVP

I've moved my blog to http://blog.falchionconsulting.com!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Monday, October 19, 2009

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!

SharePoint 2010: PSConfig and PowerShell

The information in this article is based on BETA 2 of SharePoint 2010 - there may be differences with the RTM release.

Update 12/18/2009: I've updated the post to reflect BETA 2 changes and have considerably simplified the script removing all the dependencies on helper functions so that the core pieces required to build a basic farm are focused on rather than the complex elements to support tracing and error handling as I previously had it.

If you've ever done a scripted install of SharePoint 2007 then you are familiar with how to script the initial farm creation using psconfig.exe. Unfortunately psconfig is still with us in SharePoint 2010 but we do have some PowerShell cmdlets which replace all the psconfig commands.

Before we look at how to use PowerShell lets refresh our memories by looking at an install script for SharePoint 2007. I usually have two scripts, one for the first server and a second script for additional servers - here's the first script (I've omitted the variable declarations):

   1: ECHO %DATE% %TIME%:  Building configuration database
   2: psconfig -cmd configdb -create -server %SERVER_DB% -database %DB_CONFIG_NAME% -user %ACCT_SPFARM% -password %ACCT_SPFARM_PWD% -admincontentdatabase %DB_CENTRALADMINCONTENT_NAME% 
   3: if not errorlevel 0 goto errhnd
   4:  
   5: ECHO %DATE% %TIME%: Installing help content
   6: psconfig -cmd helpcollections -installall 
   7: if not errorlevel 0 goto errhnd
   8:  
   9: ECHO %DATE% %TIME%: Securing resources
  10: psconfig -cmd secureresources 
  11: if not errorlevel 0 goto errhnd
  12:  
  13: ECHO %DATE% %TIME%: Installing services
  14: psconfig -cmd services -install 
  15: if not errorlevel 0 goto errhnd
  16:  
  17: ECHO %DATE% %TIME%: Installing features
  18: psconfig -cmd installfeatures 
  19: if not errorlevel 0 goto errhnd
  20:  
  21: ECHO %DATE% %TIME%: Creating central admin site
  22: psconfig -cmd adminvs -provision -port %CENTRALADMIN_PORT% -windowsauthprovider enablekerberos 
  23: if not errorlevel 0 goto errhnd
  24:  
  25: ECHO %DATE% %TIME%: Adding application content to central admin site
  26: psconfig -cmd applicationcontent -install
  27: if not errorlevel 0 goto errhnd
  28:  
  29: goto end
  30:  
  31: :errhnd
  32:  
  33: echo An error occured - terminating script.
  34:  
  35: :end

And here's the second script (variable declarations omitted):

   1: ECHO %DATE% %TIME%: Connecting to farm
   2: psconfig -cmd configdb -connect -server %SERVER_DB% -database %DB_CONFIG_NAME% -user %ACCT_SPFARM% -password %ACCT_SPFARM_PWD%
   3: if not errorlevel 0 goto errhnd
   4:  
   5: ECHO %DATE% %TIME%: Installing services
   6: psconfig -cmd services install
   7: if not errorlevel 0 goto errhnd
   8:  
   9: ECHO %DATE% %TIME%: Installing features
  10: psconfig -cmd installfeatures
  11: if not errorlevel 0 goto errhnd
  12:  
  13: ECHO %DATE% %TIME%: Setting security on registry and file system
  14: psconfig -cmd secureresources
  15: if not errorlevel 0 goto errhnd
  16:  
  17: goto end
  18:  
  19: :errhnd
  20:  
  21: echo An error occured - terminating script.
  22:  
  23: :end

Obviously the two scripts are very similar with the main difference being the parameters passed to the configdb command and with fewer commands being called. So how would we do this using SharePoint 2010's PowerShell cmdlets? First lets list each psconfig command and what the PowerShell equivalent is:

PSConfig Command PowerShell Cmdlet
configdb -create New-SPConfigurationDatabase
configdb -connect Connect-SPConfigurationDatabase
helpcollections -installall Install-SPHelpCollection
secureresources Initialize-SPResourceSecurity
services -install Install-SPService
installfeatures Install-SPFeature (provide the -AllExistingFeatures parameter)
adminvs -provision New-SPCentralAdministration
applicationcontent -install Install-SPApplicationContent

Now that we know what PowerShell cmdlets to use lets rework our install script. Instead of using a batch file for variables I'm going to use an XML file which can be read in and parsed as needed. I'm also going to make it so that you can run the same script from any server and just specify a switch parameter indicating whether or not you are connecting to an existing farm or not.

Let's first take a look at the XML that we'll use to manage our settings - we'll store this in a file called FarmConfigurations.xml:

<Farm FarmAccount="sp2010\spfarm" 
ConfigDB="SharePoint_ConfigDB"
AdminContentDB="SharePoint_Content_Admin"
DatabaseServer="spsql1"
Passphrase="Pa$$w0rd">
<CentralAdmin Port="1234" AuthProvider="NTLM">
<Servers>
<Server Name="spsvr1" />
</Servers>
</CentralAdmin>
</Farm>

In this XML I'm storing the core farm settings in the <Farm /> element which includes attributes for the farm account to use as well as the database server and names for the configuration and central admin content database. I then have a <CentralAdmin /> element which contains the servers that the Central Admin site should be installed on along with the port and authentication provider to use.

So now that we have our configuration settings all we need now is the core script to actually create our farm. I named this script BuildFarm.ps1 but you can name it whatever you want:

function Install-SharePointFarm([bool]$connectToExisting, 
[string]$settingsFile = "FarmConfigurations.xml") {

[xml]$config = Get-Content $settingsFile

$farmAcct = Get-Credential $config.Farm.FarmAccount

$configDb = $config.Farm.ConfigDB
$adminContentDb = $config.Farm.adminContentDb
$server = $config.Farm.DatabaseServer
if ($config.Farm.Passphrase.Length -gt 0) {
$passphrase = (ConvertTo-SecureString $config.Farm.Passphrase `
-AsPlainText -force)
} else {
Write-Warning "Using the Farm Admin's password for a passphrase"
$passphrase = $farmAcct.Password
}

#Only build the farm if we don't currently have a farm created
if (([Microsoft.SharePoint.Administration.SPFarm]::Local) -eq $null) {
if ($connectToExisting) {
#Connecting to farm
Connect-SPConfigurationDatabase -DatabaseName $configDb `
-DatabaseServer $server -Passphrase $passphrase
} else {
#Creating new farm
New-SPConfigurationDatabase -DatabaseName $configDb `
-DatabaseServer $server `
-AdministrationContentDatabaseName $adminContentDb `
-Passphrase $passphrase -FarmCredentials $farmAcct
}
#Verifying farm creation
$spfarm = Get-SPFarm -ErrorAction SilentlyContinue -ErrorVariable err
if ($spfarm -eq $null -or $err) {
throw "Unable to verify farm creation."
}

#ACLing SharePoint Resources
Initialize-SPResourceSecurity

#Installing Services
Install-SPService

#Installing Features
Install-SPFeature -AllExistingFeatures
} else {
Write-Warning "Farm already exists. Skipping creation."
}

$installSCA = (($config.Farm.CentralAdmin.Servers.Server | `
where {$_.Name -eq $env:computername}) -ne $null)
$url = "http://$($env:computername):$($config.Farm.CentralAdmin.Port)"
$sca=[Microsoft.SharePoint.Administration.SPWebApplication]::Lookup($url)
if ($installSCA -and $sca -eq $null) {
#Provisioning Central Administration
New-SPCentralAdministration -Port $config.Farm.CentralAdmin.Port `
-WindowsAuthProvider $config.Farm.CentralAdmin.AuthProvider

#Installing Help
Install-SPHelpCollection -All

#Installing Application Content
Install-SPApplicationContent
}
}

Notice that the script takes a parameter which allows you to specify whether you are connecting to an existing farm. We can call the script using the following syntax (replace $false with $true if connecting to an existing farm):

PS C:\> . .\buildfarm.ps1
PS C:\> Install-SharePointFarm $false "FarmConfigurations.xml"

In a series of upcoming post I'll be building on what I created here by demonstrating how to create web applications and how to start each of the services that are included, including the extremely complex search service (gone are the days of a single line setup for search - we now have a dozen or so cmdlets that have to be run to get search started).

SharePoint 2010: STSADM and PowerShell

The information in this article is based on BETA 2 of SharePoint 2010 - there will likely be differences with the RTM release.

Finally, the NDA is lifted and we can openly talk about SharePoint 2010! There's so many cool things to talk about it's hard to decide where to begin, but as the focus of my blog has generally been on STSADM then I suppose that's a good place to start. The first thing we should do is load the new SharePoint 2010 Management Shell which can be found under the Microsoft SharePoint 2010 Products section of the start menu:

 image

This new console window is actually just a PowerShell console which pre-loads the SharePoint PowerShell Snap-in.  You can see that by looking at the target for the shortcut:

C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe -NoExit  " & ' C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\CONFIG\POWERSHELL\Registration\\sharepoint.ps1 ' "

If you look at the SharePoint.ps1 file you'll see the following:

image

From this you can see that you can easily load up any PowerShell console app (either the native PowerShell console or a any other editor such as PowerGUI or the PowerShell ISE) and simply run the Add-PsSnapin cmdlet to load the new SharePoint 2010 cmdlets as shown above.

Note that the script is also changing the threading model of PowerShell so that commands are entered in the same thread rather than a different thread for each line of execution (I'll cover this in more detail in a later post but it's important to understand that this has a huge impact on how objects are disposed within PowerShell - see Zach's post about threading here: http://sharepoint.microsoft.com/blogs/zach/Lists/Posts/Post.aspx?ID=34)

I'll cover the PowerShell stuff in bit but first lets take a look at the STSADM changes. I did a simple stsadm -help command and saved that to a file in both my 2007 and 2010 environment and then compared those files. Here's what I found:

  • The following STSADM commands no longer exist:
    • associatewebapp
    • createcmsmigrationprofile
    • createssp
    • deletecmsmigrationprofile
    • deletessp
    • deletessptimerjob
    • editcmsmigrationprofile
    • editssp
    • enablecmsurlredirect
    • enumssp
    • enumssptimerjobs
    • grantiis7permission
    • listqueryprocessoroptions
    • mysite
    • preupgradecheck
    • restoressp
    • runcmsmigrationprofile
    • setdefaultssp
    • setqueryprocessoroptions
    • setsharedwebserviceauthn
    • setsspport
    • trimauditlog
  • The following STSADM commands have been added:
    • add-adsdefaultapplication
    • add-ecsblockedexcelfiletype
    • monitordb
    • patchpostaction
    • remove-adsdefaultapplication
    • remove-ecsblockedexcelfiletype

From the list above you can see that the bulk of the items removed have to do with the SSP and CMS migration features which were part of the 2007 product but have been removed from the 2010 product (2010 has a whole new services architecture which is really cool, but outside the scope of this article - stay tuned! - 2010 also does not support upgrading from Microsoft CMS, you must upgrade to SharePoint 2007 and then SharePoint 2010).

Also note that very few new commands have been added - in fact I was surprised to see that any were added - why? Because PowerShell is what we should all be using - STSADM is dead - just don't use it, forget that these new commands have been added as there are PowerShell equivalents (I wouldn't be surprised if they get pulled come RTM). That said, at beta there may be some stuff that still only exists with STSADM but expect that not to be the case going forward.

So now that we have that out of the way, lets talk about some PowerShell (as of this post I'm officially done creating STSADM extensions - at least for 2010, I may still have need for some new stuff with the 2007 product). In your new SharePoint 4.0 Management Console type the following command: gcm -pssnapin microsoft.sharepoint.powershell | select Name, Definition | fl > .\sp2010cmdlets.txt

Take a look at the file you just generated, you now have a listing of the 535 new PowerShell cmdlets that ship with SharePoint 2010. For brevity sake I won't show the syntax but here's a listing of all the new cmdlets that are available:

Add-PluggableSecurityTrimmer
Add-SPClaimTypeMapping
Add-SPDiagnosticsPerformanceCounter
Add-SPInfoPathUserAgent
Add-SPServiceApplicationProxyGroupMember
Add-SPShellAdmin
Add-SPSiteSubscriptionFeaturePackMember
Add-SPSiteSubscriptionProfileConfig
Add-SPSolution
Add-SPUserSolution
Backup-SPConfigurationDatabase
Backup-SPFarm
Backup-SPSite
Clear-SPLogLevel
Clear-SPMetadataWebServicePartitionData
Clear-SPPerformancePointServiceApplicationTrustedLocation
Clear-SPSecureStoreCredentialMapping
Clear-SPSecureStoreDefaultProvider
Clear-SPSiteSubscriptionBusinessDataCatalogConfig
Connect-SPConfigurationDatabase
Copy-SPBusinessDataCatalogAclToChildren
Disable-SPBusinessDataCatalogEntity
Disable-SPFeature
Disable-SPInfoPathFormTemplate
Disable-SPSessionStateService
Disable-SPSingleSignOn
Disable-SPTimerJob
Disable-SPWebApplicationHttpThrottling
Disconnect-SPConfigurationDatabase
Dismount-SPContentDatabase
Dismount-SPStateServiceDatabase
Enable-SPBusinessDataCatalogEntity
Enable-SPFeature
Enable-SPInfoPathFormTemplate
Enable-SPSessionStateService
Enable-SPTimerJob
Enable-SPWebApplicationHttpThrottling
Export-SPBusinessDataCatalogModel
Export-SPEnterpriseSearchTopology
Export-SPInfoPathAdministrationFiles
Export-SPMetadataWebServicePartitionData
Export-SPSiteSubscriptionBusinessDataCatalogConfig
Export-SPSiteSubscriptionProfileConfig
Export-SPSiteSubscriptionSettings
Export-SPWeb
Get-PluggableSecurityTrimmer
Get-SPAccessServiceApplication
Get-SPAlternateURL
Get-SPAuthenticationProvider
Get-SPBackupHistory
Get-SPBrowserCustomerExperienceImprovementProgram
Get-SPBusinessDataCatalogMetadataObject
Get-SPBusinessDataCatalogThrottleConfig
Get-SPCertificateAuthority
Get-SPClaimProvider
Get-SPClaimProviderManager
Get-SPContentDatabase
Get-SPContentDeploymentJob
Get-SPContentDeploymentPath
Get-SPCustomLayoutsPage
Get-SPDatabase
Get-SPDataConnectionFile
Get-SPDataConnectionFileDependent
Get-SPDesignerSettings
Get-SPDiagnosticConfig
Get-SPDiagnosticsPerformanceCounter
Get-SPDiagnosticsProvider
Get-SPEnterpriseSearchAdministrationComponent
Get-SPEnterpriseSearchCrawlComponent
Get-SPEnterpriseSearchCrawlContentSource
Get-SPEnterpriseSearchCrawlCustomConnector
Get-SPEnterpriseSearchCrawlDatabase
Get-SPEnterpriseSearchCrawlExtension
Get-SPEnterpriseSearchCrawlMapping
Get-SPEnterpriseSearchCrawlRule
Get-SPEnterpriseSearchCrawlTopology
Get-SPEnterpriseSearchExtendedClickThroughExtractorJobDefinition
Get-SPEnterpriseSearchExtendedConnectorProperty
Get-SPEnterpriseSearchExtendedQueryProperty
Get-SPEnterpriseSearchIndexPartition
Get-SPEnterpriseSearchLanguageResourcePhrase
Get-SPEnterpriseSearchMetadataCategory
Get-SPEnterpriseSearchMetadataCrawledProperty
Get-SPEnterpriseSearchMetadataManagedProperty
Get-SPEnterpriseSearchMetadataMapping
Get-SPEnterpriseSearchPropertyDatabase
Get-SPEnterpriseSearchQueryAndSiteSettingsService
Get-SPEnterpriseSearchQueryAndSiteSettingsServiceInstance
Get-SPEnterpriseSearchQueryAndSiteSettingsServiceProxy
Get-SPEnterpriseSearchQueryAuthority
Get-SPEnterpriseSearchQueryComponent
Get-SPEnterpriseSearchQueryDemoted
Get-SPEnterpriseSearchQueryKeyword
Get-SPEnterpriseSearchQueryScope
Get-SPEnterpriseSearchQueryScopeRule
Get-SPEnterpriseSearchQuerySuggestionCandidates
Get-SPEnterpriseSearchQueryTopology
Get-SPEnterpriseSearchRankingModel
Get-SPEnterpriseSearchSecurityTrimmer
Get-SPEnterpriseSearchService
Get-SPEnterpriseSearchServiceApplication
Get-SPEnterpriseSearchServiceApplicationProxy
Get-SPEnterpriseSearchServiceInstance
Get-SPEnterpriseSearchSiteHitRule
Get-SPExcelBlockedFileType
Get-SPExcelDataConnectionLibrary
Get-SPExcelDataProvider
Get-SPExcelFileLocation
Get-SPExcelServiceApplication
Get-SPExcelUserDefinedFunction
Get-SPFarm
Get-SPFarmConfig
Get-SPFeature
Get-SPHelpCollection
Get-SPIisWebServiceApplicationPool
Get-SPIisWebServiceSettings
Get-SPInfoPathFormsService
Get-SPInfoPathFormTemplate
Get-SPInfoPathUserAgent
Get-SPInfoPathWebServiceProxy
Get-SPLogEvent
Get-SPLogLevel
Get-SPManagedAccount
Get-SPManagedPath
Get-SPMetadataServiceApplication
Get-SPMetadataServiceApplicationProxy
Get-SPMobileMessagingAccount
Get-SPPerformancePointSecureDataValues
Get-SPPerformancePointServiceApplication
Get-SPPerformancePointServiceApplicationTrustedLocation
Get-SPProcessAccount
Get-SPProduct
Get-SPProfileServiceApplicationSecurity
Get-SPSearchService
Get-SPSearchServiceInstance
Get-SPSecureStoreApplication
Get-SPSecurityTokenService
Get-SPServer
Get-SPServiceApplication
Get-SPServiceApplicationProxy
Get-SPServiceApplicationProxyGroup
Get-SPServiceApplicationSecurity
Get-SPServiceContext
Get-SPServiceEndpoint
Get-SPServiceInstance
Get-SPSessionStateService
Get-SPShellAdmin
Get-SPSite
Get-SPSiteAdministration
Get-SPSiteSubscription
Get-SPSiteSubscriptionConfig
Get-SPSiteSubscriptionEdiscoveryHub
Get-SPSiteSubscriptionEdiscoverySearchScope
Get-SPSiteSubscriptionFeaturePack
Get-SPSiteSubscriptionMetadataConfig
Get-SPSolution
Get-SPStateServiceApplication
Get-SPStateServiceApplicationProxy
Get-SPStateServiceDatabase
Get-SPTaxonomySession
Get-SPTimerJob
Get-SPTopologyWebServiceApplication
Get-SPTopologyWebServiceProxy
Get-SPTrustedIdentityTokenIssuer
Get-SPTrustedRootAuthority
Get-SPTrustedServiceTokenIssuer
Get-SPUsageApplication
Get-SPUsageDefinition
Get-SPUsageService
Get-SPUser
Get-SPUserSolution
Get-SPVisioExternalData
Get-SPVisioPerformance
Get-SPVisioSafeDataProvider
Get-SPVisioServiceApplication
Get-SPVisioServiceApplicationProxy
Get-SPWeb
Get-SPWebAnalyticsServiceApplication
Get-SPWebAnalyticsServiceApplicationProxy
Get-SPWebApplication
Get-SPWebApplicationHttpThrottlingMonitor
Get-SPWebPartPack
Get-SPWebTemplate
Get-SPWorkflowConfig
Grant-SPBusinessDataCatalogMetadataObject
Grant-SPObjectSecurity
Import-SPBusinessDataCatalogModel
Import-SPEnterpriseSearchTopology
Import-SPInfoPathAdministrationFiles
Import-SPMetadataWebServicePartitionData
Import-SPSiteSubscriptionBusinessDataCatalogConfig
Import-SPSiteSubscriptionProfileConfig
Import-SPSiteSubscriptionSettings
Import-SPWeb
Initialize-SPContentDatabase
Initialize-SPResourceSecurity
Initialize-SPStateServiceDatabase
Install-SPApplicationContent
Install-SPDataConnectionFile
Install-SPFeature
Install-SPHelpCollection
Install-SPInfoPathFormTemplate
Install-SPService
Install-SPSolution
Install-SPUserSolution
Install-SPWebPartPack
Install-SPWebTemplate
Merge-SPLogFile
Mount-SPContentDatabase
Mount-SPStateServiceDatabase
Move-SPBlobStorageLocation
Move-SPProfileManagedMetadataProperty
Move-SPSite
Move-SPUser
New-SPAccessServiceApplication
New-SPAlternateURL
New-SPAuthenticationProvider
New-SPBusinessDataCatalogServiceApplication
New-SPBusinessDataCatalogServiceApplicationProxy
New-SPCentralAdministration
New-SPClaimProvider
New-SPClaimsObject
New-SPClaimsPrincipal
New-SPClaimTypeMapping
New-SPConfigurationDatabase
New-SPContentDatabase
New-SPContentDeploymentJob
New-SPContentDeploymentPath
New-SPEnterpriseSearchCrawlComponent
New-SPEnterpriseSearchCrawlContentSource
New-SPEnterpriseSearchCrawlCustomConnector
New-SPEnterpriseSearchCrawlDatabase
New-SPEnterpriseSearchCrawlExtension
New-SPEnterpriseSearchCrawlMapping
New-SPEnterpriseSearchCrawlRule
New-SPEnterpriseSearchCrawlTopology
New-SPEnterpriseSearchExtendedConnectorProperty
New-SPEnterpriseSearchLanguageResourcePhrase
New-SPEnterpriseSearchMetadataCategory
New-SPEnterpriseSearchMetadataCrawledProperty
New-SPEnterpriseSearchMetadataManagedProperty
New-SPEnterpriseSearchMetadataMapping
New-SPEnterpriseSearchPropertyDatabase
New-SPEnterpriseSearchQueryAuthority
New-SPEnterpriseSearchQueryComponent
New-SPEnterpriseSearchQueryDemoted
New-SPEnterpriseSearchQueryKeyword
New-SPEnterpriseSearchQueryScope
New-SPEnterpriseSearchQueryScopeRule
New-SPEnterpriseSearchQueryTopology
New-SPEnterpriseSearchRankingModel
New-SPEnterpriseSearchSecurityTrimmer
New-SPEnterpriseSearchServiceApplication
New-SPEnterpriseSearchServiceApplicationProxy
New-SPEnterpriseSearchSiteHitRule
New-SPExcelBlockedFileType
New-SPExcelDataConnectionLibrary
New-SPExcelDataProvider
New-SPExcelFileLocation
New-SPExcelServiceApplication
New-SPExcelUserDefinedFunction
New-SPIisWebServiceApplicationPool
New-SPLogFile
New-SPManagedAccount
New-SPManagedPath
New-SPMetadataServiceApplication
New-SPMetadataServiceApplicationProxy
New-SPObaSolutionPackage

New-SPPerformancePointServiceApplication
New-SPPerformancePointServiceApplicationProxy
New-SPPerformancePointServiceApplicationTrustedLocation
New-SPProfileServiceApplication
New-SPProfileServiceApplicationProxy
New-SPSecureStoreApplication
New-SPSecureStoreApplicationField
New-SPSecureStoreServiceApplication
New-SPSecureStoreServiceApplicationProxy
New-SPSecureStoreTargetApplication
New-SPServiceApplicationProxyGroup
New-SPSite
New-SPSiteSubscription
New-SPSiteSubscriptionFeaturePack
New-SPStateServiceApplication
New-SPStateServiceApplicationProxy
New-SPStateServiceDatabase
New-SPSubscriptionSettingsServiceApplication
New-SPSubscriptionSettingsServiceApplicationProxy
New-SPTrustedIdentityTokenIssuer
New-SPTrustedRootAuthority
New-SPTrustedServiceTokenIssuer
New-SPUsageApplication
New-SPUsageLogFile
New-SPUser
New-SPVisioSafeDataProvider
New-SPVisioServiceApplication
New-SPVisioServiceApplicationProxy
New-SPWeb
New-SPWebAnalyticsServiceApplication
New-SPWebAnalyticsServiceApplicationProxy
New-SPWebApplication
New-SPWebApplicationExtension
New-SPWordConversionServiceApplication
Ping-SPEnterpriseSearchContentService
Publish-SPServiceApplication
Receive-SPSharedServiceApplicationInfo
Remove-PluggableSecurityTrimmer
Remove-SPAlternateURL
Remove-SPBusinessDataCatalogModel
Remove-SPBusinessDataCatalogSiteSubscriptionConfig
Remove-SPBusinessDataCatalogThrottleConfig
Remove-SPClaimProvider
Remove-SPClaimTypeMapping
Remove-SPConfigurationDatabase
Remove-SPContentDatabase
Remove-SPContentDeploymentJob
Remove-SPContentDeploymentPath
Remove-SPDiagnosticsPerformanceCounter
Remove-SPEnterpriseSearchCrawlComponent
Remove-SPEnterpriseSearchCrawlContentSource
Remove-SPEnterpriseSearchCrawlCustomConnector
Remove-SPEnterpriseSearchCrawlDatabase
Remove-SPEnterpriseSearchCrawlExtension
Remove-SPEnterpriseSearchCrawlMapping
Remove-SPEnterpriseSearchCrawlRule
Remove-SPEnterpriseSearchCrawlTopology
Remove-SPEnterpriseSearchExtendedConnectorProperty
Remove-SPEnterpriseSearchLanguageResourcePhrase
Remove-SPEnterpriseSearchMetadataCategory
Remove-SPEnterpriseSearchMetadataManagedProperty
Remove-SPEnterpriseSearchMetadataMapping
Remove-SPEnterpriseSearchPropertyDatabase
Remove-SPEnterpriseSearchQueryAuthority
Remove-SPEnterpriseSearchQueryComponent
Remove-SPEnterpriseSearchQueryDemoted
Remove-SPEnterpriseSearchQueryKeyword
Remove-SPEnterpriseSearchQueryScope
Remove-SPEnterpriseSearchQueryScopeRule
Remove-SPEnterpriseSearchQueryTopology
Remove-SPEnterpriseSearchRankingModel
Remove-SPEnterpriseSearchSecurityTrimmer
Remove-SPEnterpriseSearchServiceApplication
Remove-SPEnterpriseSearchServiceApplicationProxy
Remove-SPEnterpriseSearchSiteHitRule
Remove-SPExcelBlockedFileType
Remove-SPExcelDataConnectionLibrary
Remove-SPExcelDataProvider
Remove-SPExcelFileLocation
Remove-SPExcelUserDefinedFunction
Remove-SPIisWebServiceApplicationPool
Remove-SPInfoPathUserAgent
Remove-SPManagedAccount
Remove-SPManagedPath
Remove-SPPerformancePointServiceApplication
Remove-SPPerformancePointServiceApplicationProxy
Remove-SPPerformancePointServiceApplicationTrustedLocation
Remove-SPSecureStoreApplication
Remove-SPServiceApplication
Remove-SPServiceApplicationProxy
Remove-SPServiceApplicationProxyGroup
Remove-SPServiceApplicationProxyGroupMember
Remove-SPShellAdmin
Remove-SPSite
Remove-SPSiteSubscription
Remove-SPSiteSubscriptionFeaturePack
Remove-SPSiteSubscriptionFeaturePackMember
Remove-SPSiteSubscriptionMetadataConfig
Remove-SPSiteSubscriptionProfileConfig
Remove-SPSiteSubscriptionSettings
Remove-SPSocialItemByDate
Remove-SPSolution
Remove-SPSolutionDeploymentLock
Remove-SPStateServiceDatabase
Remove-SPTrustedIdentityTokenIssuer
Remove-SPTrustedRootAuthority
Remove-SPTrustedServiceTokenIssuer
Remove-SPUsageApplication
Remove-SPUser
Remove-SPUserSolution
Remove-SPVisioSafeDataProvider
Remove-SPWeb
Remove-SPWebApplication
Remove-SPWordConversionServiceJobHistory
Rename-SPServer
Restart-SPEnterpriseSearchQueryComponent
Restore-SPEnterpriseSearchServiceApplication
Restore-SPFarm
Restore-SPSite
Resume-SPEnterpriseSearchServiceApplication
Resume-SPStateServiceDatabase
Revoke-SPBusinessDataCatalogMetadataObject
Revoke-SPObjectSecurity
Set-SPAccessServiceApplication
Set-SPAlternateURL
Set-SPBrowserCustomerExperienceImprovementProgram
Set-SPBusinessDataCatalogMetadataObject
Set-SPBusinessDataCatalogServiceApplication
Set-SPBusinessDataCatalogThrottleConfig
Set-SPCentralAdministration
Set-SPClaimProvider
Set-SPContentDatabase
Set-SPContentDeploymentJob
Set-SPContentDeploymentPath
Set-SPCustomLayoutsPage
Set-SPDataConnectionFile
Set-SPDesignerSettings
Set-SPDiagnosticConfig
Set-SPDiagnosticsProvider
Set-SPEnterpriseSearchAdministrationComponent
Set-SPEnterpriseSearchCrawlContentSource
Set-SPEnterpriseSearchCrawlDatabase
Set-SPEnterpriseSearchCrawlRule
Set-SPEnterpriseSearchCrawlTopology
Set-SPEnterpriseSearchExtendedConnectorProperty
Set-SPEnterpriseSearchExtendedQueryProperty
Set-SPEnterpriseSearchIndexPartition
Set-SPEnterpriseSearchMetadataCategory
Set-SPEnterpriseSearchMetadataCrawledProperty
Set-SPEnterpriseSearchMetadataManagedProperty
Set-SPEnterpriseSearchMetadataMapping
Set-SPEnterpriseSearchPropertyDatabase
Set-SPEnterpriseSearchQueryAuthority
Set-SPEnterpriseSearchQueryComponent
Set-SPEnterpriseSearchQueryKeyword
Set-SPEnterpriseSearchQueryScope
Set-SPEnterpriseSearchQueryScopeRule
Set-SPEnterpriseSearchQueryTopology
Set-SPEnterpriseSearchRankingModel
Set-SPEnterpriseSearchService
Set-SPEnterpriseSearchServiceApplication
Set-SPEnterpriseSearchServiceApplicationProxy
Set-SPEnterpriseSearchServiceInstance
Set-SPExcelDataConnectionLibrary
Set-SPExcelDataProvider
Set-SPExcelFileLocation
Set-SPExcelServiceApplication
Set-SPExcelUserDefinedFunction
Set-SPFarmConfig
Set-SPIisWebServiceApplicationPool
Set-SPIisWebServiceSettings
Set-SPInfoPathFormsService
Set-SPInfoPathFormTemplate
Set-SPInfoPathWebServiceProxy
Set-SPLogLevel
Set-SPManagedAccount
Set-SPMetadataServiceApplication
Set-SPMetadataServiceApplicationProxy
Set-SPMobileMessagingAccount
Set-SPPassPhrase
Set-SPPerformancePointSecureDataValues
Set-SPPerformancePointServiceApplication
Set-SPProfileServiceApplication
Set-SPProfileServiceApplicationProxy
Set-SPProfileServiceApplicationSecurity
Set-SPSearchService
Set-SPSearchServiceInstance
Set-SPSecureStoreApplication
Set-SPSecureStoreDefaultProvider
Set-SPSecureStoreServiceApplication
Set-SPSecurityTokenService
Set-SPServiceApplication
Set-SPServiceApplicationSecurity
Set-SPServiceEndpoint
Set-SPSessionStateService
Set-SPSite
Set-SPSiteAdministration
Set-SPSiteSubscriptionConfig
Set-SPSiteSubscriptionEdiscoveryHub
Set-SPSiteSubscriptionMetadataConfig
Set-SPSiteSubscriptionProfileConfig
Set-SPStateServiceApplication
Set-SPStateServiceApplicationProxy
Set-SPStateServiceDatabase
Set-SPSubscriptionSettingsServiceApplication
Set-SPTimerJob
Set-SPTopologyWebServiceApplication
Set-SPTopologyWebServiceProxy
Set-SPTrustedIdentityTokenIssuer
Set-SPTrustedRootAuthority
Set-SPTrustedServiceTokenIssuer
Set-SPUsageApplication
Set-SPUsageDefinition
Set-SPUsageService
Set-SPUser
Set-SPVisioExternalData
Set-SPVisioPerformance
Set-SPVisioSafeDataProvider
Set-SPVisioServiceApplication
Set-SPWeb
Set-SPWebAnalyticsServiceApplication
Set-SPWebAnalyticsServiceApplicationProxy
Set-SPWebApplication
Set-SPWebApplicationHttpThrottlingMonitor
Set-SPWebTemplate
Set-SPWordConversionServiceApplication
Set-SPWorkflowConfig
Start-SPAdminJob
Start-SPAssignment
Start-SPContentDeploymentJob
Start-SPEnterpriseSearchQueryAndSiteSettingsServiceInstance
Start-SPEnterpriseSearchServiceInstance
Start-SPInfoPathFormTemplate
Start-SPServiceInstance
Start-SPTimerJob
Stop-SPAssignment
Stop-SPEnterpriseSearchQueryAndSiteSettingsServiceInstance
Stop-SPEnterpriseSearchServiceInstance
Stop-SPInfoPathFormTemplate
Stop-SPServiceInstance
Suspend-SPEnterpriseSearchServiceApplication
Suspend-SPStateServiceDatabase
Test-SPContentDatabase
Test-SPInfoPathFormTemplate
Uninstall-SPDataConnectionFile
Uninstall-SPFeature
Uninstall-SPHelpCollection
Uninstall-SPInfoPathFormTemplate
Uninstall-SPSolution
Uninstall-SPUserSolution
Uninstall-SPWebPartPack
Uninstall-SPWebTemplate
Unpublish-SPServiceApplication
Update-SPFarmEncryptionKey
Update-SPInfoPathAdminFileUrl
Update-SPInfoPathFormTemplate
Update-SPInfoPathUserFileUrl
Update-SPProfilePhotoStore
Update-SPSecureStoreApplicationServerKey
Update-SPSecureStoreCredentialMapping
Update-SPSecureStoreGroupCredentialMapping
Update-SPSecureStoreMasterKey
Update-SPSolution
Update-SPUserSolution
Upgrade-SPContentDatabase
Upgrade-SPEnterpriseSearchServiceApplication
Upgrade-SPSingleSignOnDatabase

If you're going to be working with the SharePoint PowerShell cmdlets then there are two cmdlets that you should learn before any others: Start-SPAssignment and Stop-SPAssignment. If you've used the custom cmdlets that I created for the 2007 product and read my articles around them then you should be familiar with the core issue that I was trying to solve with some of them - the handling of disposable objects. When you use a cmdlet such as Get-SPSite you are returning back a disposable object, an object that implements the IDisposable interface in order to release handles to unmanaged resources which cause memory leaks if not released. I chose to solve the issue by simply not returning back disposable objects and instead using a proxy object. Microsoft chose a different route.

If you look closely at the definitions of the cmdlets from the text file you created above you should notice that every cmdlet has an -AssignmentCollection parameter which takes in an SPAssignmentCollection object type. The purpose of this collection is to store disposable objects so that they can be disposed when your operations complete. You create a new assignment collection by calling the Start-SPAssignment cmdlet. You can then optionally pass the created assignment collection object into your subsequent calls by either using the pipeline or directly setting the parameter (otherwise the global collection will be used). Lets look at the help for the cmdlet to understand this better:

PS C:\> help start-spassignment

NAME
    Start-SPAssignment

SYNOPSIS
    Initiates a new assignment store.


SYNTAX
    Start-SPAssignment [-Global <SwitchParameter>] [<CommonParameters>]


DETAILED DESCRIPTION
    Use this command to properly dispose of objects used with variable assignme
    nts.
    Using SPWeb, SPSite, or SPSiteAdminsitration objects can use large amounts
    of memory and using these objects, or lists of these objects, in PowerShell
     scripts requires proper memory management. By default, all Get commands di
    spose of these objects immediately after the pipeline finishes, but using S
    PAssignment, you can assign the list of objects to a variable and dispose o
    f the objects after they are no longer needed. You can also ensure that the
     objects will remain as long as you need them, even throughout multiple ite
    rations of commands.
    There are three levels of assignment:
    * No assignment - The object is not assigned to a variable and is disposed
      of after each iteration of the command.
    * Simple assignment - All objects are assigned to the global assignment sto
      re. This is done by using the Global parameter. When using this level, al
      l objects are assigned to a global store and are disposed of when the Sto
      p-SPAssignment command is called.
    * Advanced assignment - Objects are assigned to named stores for disposal.
      You can dispose of objects by using the -Identity parameter with the Stop
      -SPAssignment command.
    Regardless of the level used, all objects are disposed of when the PowerShe
    ll runspace is closed.


RELATED LINKS

REMARKS
    To see the examples, type: "get-help Start-SPAssignment -examples".
    For more information, type: "get-help Start-SPAssignment -detailed".
    For technical information, type: "get-help Start-SPAssignment -full".

As you can see from the help text there are three levels of assignment: No assignment (dispose immediately), simple assignment (use a global store), and advanced assignment (use a named store). Let's take a look at the syntax (note that in Beta 1 most of the help files do not match the actual syntax so I rarely rely on the help text to tell me what the cmdlet syntax is - instead I use the Get-Command (gcm) cmdlet):

PS C:\> gcm Start-SPAssignment -syntax
Start-SPAssignment [-Global] [-AssignmentCollection <SPAssignmentCollection>] [
-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPre
ference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <
String>] [-OutBuffer <Int32>]

If you specify the -Global switch then you do not need to store the returned object or pass it into your cmdlets - internally this cmdlet stores the object in a static variable which is used by all subsequent calls - this is the "simple assignment" method mentioned in the help text. If you do not specify the -Global switch then you must store the returned object in a variable and pass that variable into all subsequent cmdlet calls that return disposable objects and provide the variable to the Stop-SPAssignment cmdlet with the same variable via the -SemiGlobal parameter (this may change to Identity for the RTM) - this is the "advanced assignment" mentioned in the help text. So what about the "no assignment" option? The "no assignment" option is basically when you do not create an SPAssignmentCollection using the Start-SPAssignment cmdlet which causes cmdlets that return disposable objects to dispose of the object immediately after the call to WriteObject. So as long as the pipeline is active the object remains un-disposed but at the end of the pipeline the object is disposed.

So now we'll look at the syntax for the Stop-SPAssignment cmdlet:

PS C:\> gcm Stop-SPAssignment -syntax
Stop-SPAssignment [[-SemiGlobal] <SPAssignmentCollection>] [-Global] [-Assignme
ntCollection <SPAssignmentCollection>] [-Verbose] [-Debug] [-ErrorAction <Actio
nPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-W
arningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]

As you can see this is pretty straightforward, if you used the -Global switch for the Start-SPAssignment cmdlet then you'll use it here, otherwise you would use the -SemiGlobal parameter and pass the variable you created earlier.

Here's a complete example demonstrating both approaches:

#Use of the Global assignment variable
Start-SPAssignment -Global
$site = Get-SPSite "http://portal"
$site | fl
Stop-SPAssignment -Global

#Use of a semi-global, or named variable
$gc = Start-SPAssignment
$site = $gc | Get-SPSite "http://mysites"
$site | fl
$gc | Stop-SPAssignment

For the second example you can see that I'm passing in the variable using the pipeline rather than setting the parameter directly (for both the Get-SPSite cmdlet and the Stop-SPAssignment cmdlet). I could have easily set the parameter names directly but this just results in less code.

So one thing you should be asking is when do I need to do this - every cmdlet takes an SPAssignmentCollection so does that mean that I always have to deal with this stuff? My simple answer is, no, you don't have to do this all the time, only when you know you are working with disposable objects. The problem is that most people don't know when they are working with disposable objects, and in many cases what disposable objects are. So my first best practice recommendation for SharePoint 2010 will be to always use the simple assignment approach (use the global assignment variable) unless you specifically know to do otherwise. So wrap all your scripts with a call to Start-SPAssignment and Stop-SPAssignment. If you have functions that do not return values then wrap those using the advanced approach as shown below:

function Set-SomethingInteresting([string]$siteUrl)
{
    trap {
        $gc | Stop-SPAssignment
    }
    
    $gc = Start-SPAssignment
    $site = $gc | Get-SPSite $siteUrl
    Write-Host "Doing something interesting..."
    $gc | Stop-SPAssignment
}
Set-SomethingInteresting "http://mysites"

So as you can see SharePoint 2010 introduces all kinds of really cool stuff - I've literally not even scratched the surface with what you can do with PowerShell and SharePoint 2010, let alone all the cool new features that 2010 introduces. Over the coming weeks and months I hope to provide details on how to do a full scripted install of SharePoint 2010 including detail on how to script out every single service - this will be critical information that every administrator and developer should have as PowerShell is going to be the recommended way to start and configure your services in the new architecture.