There are a couple of different options to choose from when you want to deploy WF and WCF services into IIS and Windows Server AppFabric. Both IIS and Windows Server AppFabric ship with comprehensive PowerShell APIs which can be used to fully automate the deployment, configuration and management of services. This page walks through an example install script that deploys a WCF service and sets up a host in IIS monitored by AppFabric.

Pre-requisite Checking

The script begins by checking a couple of pre-requisites. If any of these checks fail then we do not attempt to install the service, instead the installing admin is told of the issues. There are a number of different checks we can make, in this script we check the OS version, that dependent services are installed and that the correct version of the .NET framework is available.

First we need a variable to hold whether or not we have a failure:

$failedPrereqs = $false

 

Next we move on to our first check: that the correct version of Windows being used:

$OSVersion = Get-WmiObject Win32_OperatingSystem

 

if(-not $OSVersion.Version.StartsWith('6.1')) {

    Write-Host "The operating system version is not supported, Windows 7 or Windows Server 2008 required."

    $failedPrereqs = $true

    

    # See http://msdn.microsoft.com/en-us/library/aa394239(v=VS.85).aspx for other properties of Win32_OperatingSystem

    # See http://msdn.microsoft.com/en-us/library/aa394084(VS.85).aspx for additional WMI classes

}

 

 

The script fetches the Win32_OperatingSystem WMI object for interrogation using Get-WmiObject. This object contains a good deal of useful information, links are provided above to let you drill down into other properties. The script checks the Version to ensure that we are working with either Windows 7 or Windows Server 2008, in which case the version starts with "6.1". 

 

Next we look for a couple of installed services:

# IIS is installed

$IISService = Get-Service -Name 'W3SVC' -ErrorAction SilentlyContinue

if(-not $IISService) {

    Write-Host "IIS is not installed on $env:computername"

    $FailedPrereqs = $true

}

 

# AppFabric is installed

$AppFabricMonitoringService = Get-Service -Name 'AppFabricEventCollectionService' -ErrorAction SilentlyContinue

if(-not $AppFabricMonitoringService) {

    Write-Host "AppFabric Monitoring Service is not installed on $env:computername"

    $FailedPrereqs = $true

}

 

$AppFabricMonitoringService = Get-Service -Name 'AppFabricWorkflowManagementService' -ErrorAction SilentlyContinue

if(-not $AppFabricMonitoringService) {

    Write-Host "AppFabric Workflow Management Service is not installed on $env:computername"

    $FailedPrereqs = $true

}

A basic pattern is repeated here using the Get-Service command to determine if a particular Windows Service is installed on the machine. 

With the service requirements checked, we look to see if we have the correct version of the .NET framework installed. In our case we want the RTM of version 4 and go to the registry to validate this.

$frameworkVersion = get-itemProperty -Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -ErrorAction SilentlyContinue

if(-not($frameworkVersion) -or (-not($frameworkVersion.Version -eq '4.0.30319'))){

    Write-Host 'The RTM version of the full .NET 4 framework is not installed.'

    $FailedPrereqs = $true

}

 

 

The registry provider is used, HKLM: [HKEY_LOCAL_MACHINE], to look up a path in the registry that should contain the version. If the key is not found or the value is incorrect we fail the test.

 

There is great feature in Windows Server 2008 R2 that allows very simple querying of the installed Windows features. To access the commands you need to import the ServerManager module.

>Import-Module ServerManager

 

To see a list of the command available in the module:

>Get-Command -Module ServerManager

 

CommandType     Name                  Definition                                                                              

-----------     ----                  ----------                                                                              

Cmdlet          Add-WindowsFeature    Add-WindowsFeature...

Cmdlet          Get-WindowsFeature    Get-WindowsFeature...

Cmdlet          Remove-WindowsFeature Remove-WindowsFeature...

 

 

A simple add/remove/get interface is provided to allow you to easily determine which Windows roles and features are installed and then add or remove as required. This is ideal for pre-requisite checking; we can now explicitly check to see if the WinRM IIS Extensions are installed:

 

import-module ServerManager

 

if(-not (Get-WindowsFeature 'WinRM-IIS-Ext').Installed) {

    Write-Host 'The WinRM IIS Extension is not installed'

}

 

 

Simply calling Get-WindowsFeature lists all features and marks-up those that are installed with [X]:

 

 

> Get-WindowsFeature

 

Display Name                                            Name                   

------------                                            ----                   

[ ] Active Directory Certificate Services               AD-Certificate         

    [ ] Certification Authority                         ADCS-Cert-Authority    

    [ ] Certification Authority Web Enrollment          ADCS-Web-Enrollment    

    [ ] Certificate Enrollment Web Service              ADCS-Enroll-Web-Svc    

    [ ] Certificate Enrollment Policy Web Service       ADCS-Enroll-Web-Pol    

[ ] Active Directory Domain Services                    AD-Domain-Services     

    [ ] Active Directory Domain Controller              ADDS-Domain-Controller 

    [ ] Identity Management for UNIX                    ADDS-Identity-Mgmt     

        [ ] Server for Network Information Services     ADDS-NIS               

        [ ] Password Synchronization                    ADDS-Password-Sync     

        [ ] Administration Tools                        ADDS-IDMU-Tools        

[ ] Active Directory Federation Services                AD-Federation-Services 

    [ ] Federation Service                              ADFS-Federation        

    [ ] Federation Service Proxy                        ADFS-Proxy             

    [ ] AD FS Web Agents                                ADFS-Web-Agents        

        [ ] Claims-aware Agent                          ADFS-Claims            

        [ ] Windows Token-based Agent                   ADFS-Windows-Token     

[ ] Active Directory Lightweight Directory Services     ADLDS                  

[ ] Active Directory Rights Management Services         ADRMS                  

    [ ] Active Directory Rights Management Server       ADRMS-Server           

    [ ] Identity Federation Support                     ADRMS-Identity         

[X] Application Server                                  Application-Server     

    [X] .NET Framework 3.5.1                            AS-NET-Framework       

    [X] AppFabric                                       AS-AppServer-Ext       

    [X] Web Server (IIS) Support                        AS-Web-Support         

    [X] COM+ Network Access                             AS-Ent-Services        

    [X] TCP Port Sharing                                AS-TCP-Port-Sharing    

    [X] Windows Process Activation Service Support      AS-WAS-Support         

        [X] HTTP Activation                             AS-HTTP-Activation     

        [X] Message Queuing Activation                  AS-MSMQ-Activation     

        [X] TCP Activation                              AS-TCP-Activation

...

 

 

The right hand column contains the name of the feature to use via the command.

 

 

 A simple function to check for a list of features:

function Check-FeatureSet{

    param(

        [Parameter(Mandatory=$true)]

        [array] $featureSetArray, 

        [Parameter(Mandatory=$true)]

        [string]$featuresName

    )

    Write-Host "Checking $featuresName for missing features..."

 

    foreach($feature in $featureSetArray){

        if(-not (Get-WindowsFeature $feature).Installed){

            Write-Host –ForegroundColor Red "The feature $feature is not installed"

        }

    }

}  

To call the function:

# array of strings containing .NET related features

$dotNetFeatureSet = @('NET-Framework','NET-Framework-Core','NET-Win-CFAC','NET-HTTP-Activation','NET-Non-HTTP-Activ') 

 

# array of string containing MSMQ related features

$messageQueueFeatureSet = @('MSMQ','MSMQ-Services','MSMQ-Server')

 

Check-FeatureSet $dotNetFeatureSet '.NET'

Check-FeatureSet $messageQueueFeatureSet 'Message Queuing'

 

After making each individual pre-requisite check the failure variable is evaluated. If true then the script ends with a suitable message, otherwise we go ahead with the install.

Installing the Service

The first step in an installation is to copy the required files from a known location. This is a pull model - the target server pulls the files across the network, rather than having the files pushed on to the server via an administration share or such like [e.g. file://mymachine/c$/Services/].

 

$sourcePath = '\\SomeMachine\MagicEightBallInstaller\'

$installPath = 'C:\Services\MagicEightBall'

    

if(-not (Test-Path $sourcePath)) {

    Write-Host 'Cannot find the source path ' $sourcePath

    Throw (New-Object System.IO.FileNotFoundException)

}

 

if(-not (Test-Path $installPath)) {

    New-Item -type directory -path $installPath

    Write-Host 'Created service directory at ' $installPath

}

 

Copy-Item -Path (Join-Path $sourcePath "*") -Destination $installPath -Recurse

    

Write-Host 'Copied the required service files to ' $installPath

 

The file structure is copied from a network share onto the machine the script is running on. The Test-Path command determines whether a path exists and allows appropriate action to be taken. To perform a recursive copy the Copy-Item command is called, using the Join-Path command to establish the source path. These path commands can be used with any provider, not just the file system. The script above will copy any file structure verbatim from the source to the target directory.

With the files and directories in place, we now need to host the service in IIS. To do this we need to use the PowerShell module for IIS:

 

 

import-module WebAdministration # requires admin-level privileges

Next...

 

 

$found = Get-ChildItem IIS:\AppPools | Where-Object {$_.Name -eq "NewAppPool"} 

if(-not $found){

        New-WebAppPool 'NewAppPool'

}

 

We want to isolate our service into its own pool so we check to see if NewAppPool exists and if not we create it. We are using the IIS: provider to treat the web server as if it was a file system, again we just use standard commands to query the path.

 

 

Set-ItemProperty IIS:\AppPools\NewAppPool -Name ProcessModel -Value @{IdentityType=3;Username="MyServer\Service.EightBall";Password="p@ssw0rd"} # 3 = Custom

 

Set-ItemProperty IIS:\AppPools\NewAppPool -Name ManagedRuntimeVersion -Value v4.0

 

Write-Host 'Created application pool NewAppPool'

Having created the application pool we set some properties. In particular we ensure that .NET v4 is used and that a custom identity is used. The @{} syntax allows us to construct new object instances - in this case a new process model object.

New-WebApplication -Site 'Default Web Site' -Name 'MagicEightBall' -PhysicalPath $installPath -ApplicationPool 'NewAppPool' -Force

With the application pool in place and configured, we next set-up the web application itself. The New-WebApplication command is all we need, giving it the site, application name, physical file system path and application pool.

Set-ItemProperty 'IIS:/Sites/Default Web Site/MagicEightBall' -Name EnabledProtocols 'http,net.tcp' # do not include spaces in the list! 

Write-Host 'Created web application MagicEightBall'

To enable both HTTP and net.tcp endpoints, we simply update the EnabledProtocols property of the web application. Thanks to default endpoints in WCF4, this is all we need to do get both protocols supported. Note: do not put spaces into the list of protocols.

We now have enough script to create the service host, but we want to add AppFabric monitoring. Windows Server AppFabric has a rich PowerShell API, to access it we need to import the module:

import-module ApplicationServer

Next we need to create our monitoring database:

 

[Reflection.Assembly]::LoadWithPartialName("System.Data")

 

$monitoringDatabase = 'MagicEightBallMonitoring'

$monitoringConnection = New-Object System.Data.SqlClient.SqlConnectionStringBuilder -argumentList "Server=localhost;Database=$monitoringDatabase;Integrated Security=true"

$monitoringConnection.Pooling = $true

 

 

We need a couple of variables: a database name and a connection string. We use the SqlConnectionStringBuilder out of the System.Data assembly to get our connection string. This demonstrates the deep integration between PowerShell and .NET.

 

Add-WebConfiguration -Filter connectionStrings -PSPath "MACHINE/WEBROOT/APPHOST/Default Web Site/MagicEightBall" -Value @{name="MagicEightBallMonitoringConnection"; connectionString=$monitoringConnection.ToString()}

We add the connection string to our web application configuration.

Initialize-ASMonitoringSqlDatabase -Admins 'Domain\AS_Administrators' -Readers 'DOMAIN\AS_Observers' -Writers 'DOMAIN\AS_Administrators' -ConnectionString $monitoringConnection.ToString() -Force

 

And then we create the actual database, passing in the security groups. While local machine groups can be used, in this case I'm mocking a domain group which is more appropriate for load balanced scenarios.

Set-ASAppMonitoring -SiteName 'Default Web Site' -VirtualPath 'MagicEightBall' -MonitoringLevel 'HealthMonitoring' -ConnectionStringName 'MagicEightBallMonitoringConnection'

 

The last step is to enable monitoring for the web application, above we are setting a 'health monitoring' level which is enough to populate the AppFabric dashboard inside the IIS manager.

Set-ASAppServiceMetadata -SiteName 'Default Web Site' -VirtualPath 'MagicEightBall' -HttpGetEnabled $True 

Last of all we ensure that meta data publishing is available for our service. This allows us to test the service using the WCFTestClient application.

Summary

The page has shown how to install a WCF service into IIS/WAS using the PowerShell modules that ship with IIS and Windows Server AppFabric.

 

See Also