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:
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.
- Copy assemblies source location
- Copy assemblies destination location
- Application pool name
- Website name
- Application name
- Create app pool? (optional)
- Is Workflow? (optional)
- 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:
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.
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"
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~