Wednesday, May 28, 2014

WCF/WF Service Deployment Automation with PowerShell

Today I want to cover about how to automate the WCF/WF service deployment and configuration in IIS by using PowerShell. During a normal manual deployment process, we copy the compiled assemblies to a folder in the web server, and then configure the IIS by setting up an application pool, website then application. Therefore, in order to automate the deployment process, first we need the following information:

  1. Copy assemblies source location
  2. Copy assemblies destination location
  3. Application pool name
  4. Website name
  5. Application name
  6. Create app pool? (optional)
  7. Is Workflow? (optional)
  8. Is 32-bit platform? (optional)

File Copy

For the copy assemblies part, we can rely on robust file copy tool (robocopy) to do the job. It is reliable, configurable, support auto resume, etc. Robocopy is available or come with windows resource toolkit. I am going to split the script into 2 parts. First part only cover the file copy, second part only cover the IIS configuration. So that the file copy is not tightly coupled with IIS configuration because sometime we only want the file copy but excluding IIS configuration, or skip the file copy and only want to configure IIS.

Script #1:

param (
      [string]$source = $(throw "-source is required."),
      [string]$destination = $(throw "-destination is required.")
)

write-output "`r`n"
write-output "Source is $($source)"
write-output "Destination is $($destination)"
write-output "`r`n"

#copy all the files including all level subfolders from source to destination.
#folder will be automatically created if not exist in the destination.
write-output "Copying file from source to destination..."
robocopy $source $destination "/v" "/e"
write-output "`r`n"

write-output "File copy operation has been completed."

This is how it look like after running the script:




IIS Configuration

Next, the IIS configuration part, we have to support new and existing application deployment. So, there are some checking required in the script to check for application pool and application existence. In the following script, it will prompt to user to ask whether want to overwrite existing application pool configuration and also another prompt for application configuration.

In order to use PowerShell to configure IIS, we need to import WebAdministration module into our script by using the Import-Module "WebAdministration" command. This module allow us to configure almost everything in IIS. In my following script, I use the module to create application pool, create application and change application setting. For more information about PowerShell with IIS, visit HERE.

In order to setup an application, basically we need the information of the website name, application name, application pool name, and physical files path. They are needed to be part of my script parameters. And then, I have 3 parameters with switch data type:
$createAppPool is the flag used to create application pool.
$isWorkflow is the flag used to configure the application to enable net.pipe protocol for Workflow Management Service to activate workflow instances. And also setting the autoStart mode to true and using AlwaysRunning start-mode to make the IIS worker process automatically start and running all the time.
$is32bit is the flag used to set the application mode to run as 32-bit process in WOW64 mode.


Script #2:

param (
      [string]$websiteName = $(throw "-websiteName is required."),
      [string]$appName = $(throw "-appName is required."),
      [string]$appPoolName = $(throw "-appPoolName is required."),
      [string]$physicalPath = $(throw "-physicalPath is required."),
      [switch]$createAppPool,
      [switch]$isWorkflow,
      [switch]$is32bit
)

#import the IIS administration module.
Import-Module "WebAdministration"

write-output "`r`n"
write-output "Website Name is $($websiteName)"
write-output "Application Name is $($appName)"
write-output "App Pool Name is $($appPoolName)"
write-output "Physical Path is $($physicalPath)"
write-output "Create App Pool is $($createAppPool)"
write-output "Workflow is $($isWorkflow)"
write-output "32-bit is $($is32bit)"
write-output "`r`n"

#reusable function to configure app pool.
function Configure-AppPool
{
      #when isWorkflow flag is supplied, set the autoStart, startMode and app pool recycling time.
      if ($isWorkflow.isPresent)
      {
            write-output "Configuring app pool $($appPoolName)...`r`n"
            Set-ItemProperty IIS:\AppPools\$appPoolName -name autoStart -value True
            Set-ItemProperty IIS:\AppPools\$appPoolName -name startMode -value AlwaysRunning
            Set-ItemProperty IIS:\AppPools\$appPoolName -name recycling.periodicRestart.time -value "00:00:00"
      }
     
      #when is32bit flag is supplied, set the app pool to run in 32-bit platform mode.
      if ($is32bit.isPresent)
      {
            write-output "Setting app pool $($appPoolName) run on 32-bit...`r`n"
            Set-ItemProperty IIS:\AppPools\$appPoolName -name enable32BitAppOnWin64 -value True
      }
}

#reusable function to enable net.pipe protocol for application
function Set-Application-EnabledProtocols
{
      #when isWorkflow flag is supplied, enable net.pipe protocol for the application.
      if ($isWorkflow.isPresent)
      {
            write-output "Enable net.pipe protocol for $($websiteName)\$($appName)...`r`n"
            Set-ItemProperty IIS:\Sites\$websiteName\$appName -name enabledProtocols -Value "http,net.pipe"
      }
}

write-output "Configuring IIS...`r`n"

#when createAppPool flag is supplied, create a new app pool with the provided name.
if ($createAppPool.isPresent)
{
      write-output "Creating app pool $($appPoolName)...`r`n"
     
      #check if the app pool is already exist.
      if (Test-Path IIS:\AppPools\$appPoolName)
      {
            #ask to overwrite the app pool setting?
            write-output "App pool $($appPoolName) already exists.`r`n"
            $overwriteAppPool = read-host "Do you want to overwrite the app pool setting? [Y]es [N]o"
           
            if ($overwriteAppPool -eq "Y" -or $overwriteAppPool -eq "y")
            {
                  #overwrite the app pool setting.
                  write-output "`r`n"
                  write-output "Overwrite App pool $($appPoolName) settings...`r`n"
                  Configure-AppPool
            }
      }
      else
      {
            #directly create new app pool and then configure it.
            New-Item IIS:\AppPools\$appPoolName
            write-output "`r`n"
            Configure-AppPool
      }    
}

write-output "Creating application $($websiteName)\$($appName)...`r`n"

#check if the application already exist.
$detectedApp = Get-WebApplication -name $appName -site $websiteName
if ($detectedApp -ne $null)
{
      #ask to overwrite the application setting?
      write-output "Application $($websiteName)\$($appName) already exists.`r`n"
      $overwriteApp = read-host "Do you want to overwrite the application setting? [Y]es [N]o"
     
      if ($overwriteApp -eq "Y" -or $overwriteApp -eq "y")
      {    
            #overwrite the application setting.
            write-output "`r`n"
            write-output "Overwrite application settings...`r`n"
            write-output "Assigning app pool $($appPoolName) to application $($websiteName)\$($appName)...`r`n"
            #Set-ItemProperty IIS:\Sites\$websiteName\$appName -name applicationPool -value $appPoolName
           
            Set-Application-EnabledProtocols
      }
}
else
{
      #directly create new application.
      New-Item IIS:\Sites\$websiteName\$appName -physicalPath $physicalPath -applicationPool $appPoolName -type Application
      write-output "`r`n"
     
      Set-Application-EnabledProtocols
}


write-output "IIS configuration has been completed.`r`n"

For the new setup, the output screen look like this:



If there is an existing application pool and application in the IIS, the screen look like this:



Lastly, if you want to automate in running both scripts together, you can combine both PowerShell scripts into one like below. The condition is script #1 and #2 must be sitting together in one same folder.


Script #1 + Script #2

param (
      [string]$source = $(throw "-source is required."),
      [string]$destination = $(throw "-destination is required."),
      [string]$websiteName = $(throw "-websiteName is required."),
      [string]$appName = $(throw "-appName is required."),
      [string]$appPoolName = $(throw "-appPoolName is required."),
      [switch]$createAppPool,
      [switch]$isWorkflow,
      [switch]$is32bit
)

write-output "`r`n"

#check if the app-deploy.ps1 file exist in the same folder with this script.
if (Test-Path .\app-deploy.ps1)
{
      #invoke app-deploy script with the provided arguments.
      Invoke-Expression -Command ".\app-deploy.ps1 -source $($source) -destination $($destination)"
}
else
{
      #terminate the script when app-deploy.ps1 not found.
      write-output "ERROR: Could not locate app-deploy.ps1 file in the same folder.`r`n"
      EXIT
}

#dynamically construct the optional command arguments before calling service-prepare.ps1 script.
$commandArguments = "-websiteName '$($websiteName)' -appName '$($appName)' -appPoolName '$($appPoolName)' -physicalPath '$($destination)'"

if ($createAppPool.isPresent)
{
      $commandArguments = $commandArguments + " -createAppPool"
}

if ($isWorkflow.isPresent)
{
      $commandArguments = $commandArguments + " -isWorkflow"
}

if ($is32bit.isPresent)
{
      $commandArguments = $commandArguments + " -is32bit"
}

if (Test-Path .\service-prepare.ps1)
{
      #invoke service-prepare script with the provided arguments.
      Invoke-Expression -Command ".\service-prepare.ps1 $($commandArguments)"
}
else
{
      #show error when service-prepare.ps1 is not exist in the same folder with this script.
      write-output "ERROR: Could not locate service-prepare.ps1 file in the same folder.`r`n"

}

Lastly verify the application pool and application creation and its setting in IIS. Enjoy power-shelling~


No comments:

Post a Comment

Send Transactional SMS with API

This post cover how to send transactional SMS using the Alibaba Cloud Short Message Service API. Transactional SMS usually come with One Tim...