SharePoint Calculator Service Part 5 – Service Application PowerShell

In Part 4 of this series, we integrated our Calculator service application creation and provisioning experience with the SharePoint Central Administration site.

While this provides a nice experience for ad-hoc SharePoint deployments, we would also like to support scripted deployments which are completely automated.

In this article, we’ll build a PowerShell cmdlet to integrate our service application creation experience into the SharePoint 2010 Management Shell to support these scenarios.

We’ll create a cmdlet named New-CalculatorServiceApplication that will take two required parameters: the name of the new service application, and the application pool that will host it. The cmdlet will then create the new service application and provision it.

Here’s the proposed syntax:

PS> gcm New-CalculatorServiceApplication -syntax
New-CalculatorServiceApplication [-Name] <String> [-ApplicationPool] <SPIisWebServiceApplicationPoolPipeBind>

The SPIisWebServiceApplicationPoolPipeBind parameter accepts a service application pool object, which can be created by the New-SPServiceApplicationPool cmdlet. Alternatively, an administration can use the Get-SPServiceApplicationPool cmdlet to select an existing service application pool to be used for the new service application.

The first thing we’ll do is create the New-CalculatorServiceApplication cmdlet by subclassing SPCmdlet:

CalculatorServiceCmdlets.cs

  1. [Cmdlet(VerbsCommon.New, "CalculatorServiceApplication", SupportsShouldProcess = true)]
  2. [SPCmdlet(RequireLocalFarmExist = true, RequireUserFarmAdmin = true)]
  3. internal sealed class NewCalculatorServiceApplication : SPCmdlet
  4. {
  5.     private SPIisWebServiceApplicationPoolPipeBind m_ApplicationPool;
  6.  
  7.     [Parameter(Mandatory = true, Position = 0)]
  8.     [ValidateNotNullOrEmpty]
  9.     public string Name
  10.     {
  11.         get;
  12.         set;
  13.     }
  14.  
  15.     [Parameter(Mandatory = true, Position = 1, ValueFromPipeline = true)]
  16.     [ValidateNotNull]
  17.     public SPIisWebServiceApplicationPoolPipeBind ApplicationPool
  18.     {
  19.         get { return m_ApplicationPool; }
  20.         set { m_ApplicationPool = value; }
  21.     }
  22.  
  23.     protected override void InternalProcessRecord()
  24.     {
  25.         // Ensure that the farm level required objects exist
  26.         SPFarm farm = SPFarm.Local;
  27.         if (null == farm)
  28.         {
  29.             ThrowTerminatingError(new InvalidOperationException("SharePoint server farm not found."),
  30.                 ErrorCategory.ResourceUnavailable, this);
  31.         }
  32.  
  33.         // Ensure that the service exists
  34.         CalculatorService service = farm.Services.GetValue<CalculatorService>();
  35.         if (null == service)
  36.         {
  37.             ThrowTerminatingError(new InvalidOperationException("Calculator service not found."),
  38.                 ErrorCategory.ResourceUnavailable, this);
  39.         }
  40.  
  41.         // Check for an existing sample web service application
  42.         CalculatorServiceApplication existingServiceApplication = service.Applications.GetValue<CalculatorServiceApplication>();
  43.         if (null != existingServiceApplication)
  44.         {
  45.             WriteError(new InvalidOperationException("Calculator service application exists."),
  46.                 ErrorCategory.ResourceExists, existingServiceApplication);
  47.  
  48.             // Skip the current record
  49.             SkipProcessCurrentRecord();
  50.         }
  51.  
  52.         // Get a reference to the specified application pool
  53.         SPIisWebServiceApplicationPool applicationPool = this.ApplicationPool.Read();
  54.         if (null == applicationPool)
  55.         {
  56.             WriteError(new InvalidOperationException("The specified application pool could not be found."),
  57.                 ErrorCategory.InvalidArgument, this);
  58.  
  59.             // Skip the current record.
  60.             SkipProcessCurrentRecord();
  61.         }
  62.  
  63.         if (ShouldProcess(this.Name))
  64.         {
  65.             // Create the sample application
  66.             CalculatorServiceApplication serviceApplication = new CalculatorServiceApplication(
  67.                 this.Name, service, applicationPool);
  68.             serviceApplication.Update();
  69.  
  70.             // Provision the sample application
  71.             serviceApplication.Provision();
  72.  
  73.             // Write the new sample application to the pipeline
  74.             WriteObject(serviceApplication);
  75.         }
  76.     }
  77. }

The Cmdlet attribute (line 1) defines the cmdlet verb and noun.

The SPCmdlet attribute (line 2) ensures that the local machine is joined to a SharePoint farm and that the caller is a farm administrator before the cmdlet is executed.

The Name and ApplicationPool properties (lines 5-21) are the two cmdlet parameters. The SPIisWebServiceApplicationPoolPipeBind class is a SharePoint class that has associated cmdlets for creating and looking up application pools that can be passed to the cmdlet as the ApplicationPool parameter, i.e., New-SPServiceApplicationPool.

The InternalProcessRecord method (lines 23-76) is where the work happens.

Lines 25-50 simply validate the farm configuration.

Line 53 reads the SPIisWebServiceApplicationPool parameter from the pipebind parameter.

Lines 65-71 create and provision a Calculator service application, which should look familiar since it’s the same code we used in the Central Administration site UI.

And finally, line 74 writes the new service application object out to the PowerShell pipeline where it can be piped as input to the next cmdlet in the pipeline.

Now, we need to tell SharePoint Management Shell about our new cmdlet. This is done by first creating an xml registration file as follows:

CalculatorServiceApplicationCmdlets.xml

  1. <ps:Config
  2.     xmlns:ps="urn:Microsoft.SharePoint.PowerShell"
  3.     xmlns:xsi="www.w3.org/2001/XMLSchema-instance"
  4.     xsi:schemaLocation="urn:Microsoft.SharePoint.PowerShell SPCmdletSchema.xsd">
  5.     <ps:Assembly Name="Sample.Calculator.Service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d5d38459f57e2f46">
  6.         <ps:Cmdlet>
  7.             <ps:VerbName>New-CalculatorServiceApplication</ps:VerbName>
  8.             <ps:ClassName>Sample.Calculator.Service.PowerShell.NewCalculatorServiceApplication</ps:ClassName>
  9.             <ps:HelpFile>Sample.Calculator.Service.PowerShell.dll-help.xml</ps:HelpFile>
  10.         </ps:Cmdlet>
  11.     </ps:Assembly>
  12. </ps:Config>

We simply register our assembly full name (line 5), our cmdlet verb and name (line 7), the class that implements the cmdlet (line 8), and a help file (line 9). We won’t actually create the help file for now since it’s optional.

And finally, we’ll update our temporary installation batch file to copy the xml file to the appropriate location:

xcopy /y "%_SETUPPATH%\PowerShell\*Cmdlets.xml" "%_INSTALLPATH%\Config\PowerShell\registration\*.*"

Done.

So what do we get for this code?

Well, it’s pretty simple for SharePoint administrators to create our service application from the Management Shell now:

PS C:\> $appPool = Get-SPServiceApplicationPool CalculatorServiceAppPool
PS C:\> New-CalculatorServiceApplication "Calculator" $appPool

DisplayName          TypeName             Id
-----------          --------             --
Calculator          Sample.Calculator... 9ed7060a-618a-4363-8753-6b9b4d6a5d15

The first command assigns the application pool named “CalculatorServiceAppPool” (which I previously created using the “New-SPServiceApplicationPool” cmdlet) to a variable, and the second command creates a new CalculatorServiceApplication with the name “Calculator” in the “CalculatorServiceApplicationAppPool”.

Or, I can do this on one line using the PowerShell pipeline:

PS C:\> Get-SPServiceApplicationPool CalculatorServiceAppPool | New-CalculatorServiceApplication "Calculator"

I can also use the built-in cmdlets to manage the new service application. For example:

PS C:\> Get-SPServiceApplication

DisplayName          TypeName             Id
-----------          --------             --
Security Token Se... Security Token Se... 7d88501a-a3e0-4289-aa96-62048803b1fb
Application Disco... Application Disco... e79b6d9d-f7a0-4ac9-bdda-a9df8dc5e138
WSS_UsageApplication Usage and Health ... 7f75ee92-3930-4496-b1e2-743829f74cf4
Calculator           Sample.Calculator... 9ed7060a-618a-4363-8753-6b9b4d6a5d15

So there you have it—a PowerShell cmdlet that makes it easy for administrators to automate deployment of the Calculator service application.