
If you’ve ever configured Microsoft Internet Information Services (IIS), you probably know how much time it takes to create a directory structure for your site’s content first and then configure the IIS sites, bindings, application pools, web applications, and their properties. It would be a great time-saver if we could do all of these tasks using just one command or script.
The same thought came to our web team at some point, and they asked me for help. I didn’t know much about scripting for IIS at that time, but fortunately it turned out not to be very complicated. I found out that there is a PowerShell module called WebAdministration that allows us to do almost everything with IIS using PowerShell.
This script is for creating IIS web sites, their NTFS folders, default application pools, and bindings, and the second script is for creating application pools for specific web applications, web applications themselves, and NTFS folders for them and setting up some configuration parameters for application pools.
This guide will show you the scripts to create the IIS websites. You can see the script below with line-by-line explanations.
<#
.SYNOPSIS
.
.DESCRIPTION
Creates a number of websites as well as folder structure and
default application pools under them. Also sets up bindings
for the newly created websites.
.PARAMETER WebSiteName
This is the site name you are going to create. It will be complemented
by the site number if you are creating more than one site.
.PARAMETER WebSitesNumber
Specifies the number of websites that will be created.
.PARAMETER RootFSFolder
Specifies a root folder where all the websites will be created.
.PARAMETER Hostheaders
Specifies host headers that will be added to each website.
Use quotes for this parameter to avoid errors.
.PARAMETER EnviromentName
Specifies an environment name for the site name. If you omit this,
you will create a site without an environment name.
.PARAMETER DefaultAppPoolName
Specifies default name for application pool. If omitted, the
WebSiteName will be used as the default app pool name.
.PARAMETER Help
Displays the help screen.
.EXAMPLE
C:\PS>.\createIISSites.ps1 -WebSiteName app -WebSitesNumber 3 -RootFSFolder c:\webs -EnviromentName lab -DefaultAppPoolName platformsc -Hostheaders ".contentexpress.lan, .enservio.com"
Creates 3 IIS sites with the names app1lab, app2lab, app3lab, root folder c:\web
and subfolders for each site like c:\webs\app1lab, c:\webs\app2lab etc.,
default app pools for those sites with the names platformsc1, platformsc2 etc.,
and hostheaders for each site with the names app1.contentexpress.lan,
app2.contentexpress.lan, app2.enservio.com etc.
.NOTES
Author: Rock Brave
Date: May 15, 2016
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]$WebSiteName,
[Parameter(Mandatory=$True)]
[int]$WebSitesNumber,
[Parameter(Mandatory=$True)]
[string]$RootFSFolder,
[Parameter(Mandatory=$False)]
[string]$Hostheaders,
[Parameter(Mandatory=$False)]
[string]$EnviromentName,
[Parameter(Mandatory=$False)]
[string]$DefaultAppPoolName,
)
if(-not $DefaultAppPoolName){
$DefaultAppPoolName = $WebSiteName}
import-module WebAdministration
Function CreateAppPool {
Param([string] $appPoolName)
if(Test-Path ("IIS:\AppPools\" + $appPoolName)) {
Write-Host "The App Pool $appPoolName already exists" -ForegroundColor Yellow
return
}
$appPool = New-WebAppPool -Name $appPoolName
}
function CreatePhysicalPath {
Param([string] $fpath)
if(Test-path $fpath) {
Write-Host "The folder $fpath already exists" -ForegroundColor Yellow
return
}
else{
New-Item -ItemType directory -Path $fpath -Force
}
}
Function SetupBindings {
Param([string] $hostheaders)
$charCount = ($hostheaders.ToCharArray() | Where-Object {$_ -eq ','} | Measure-Object).Count + 1
$option = [System.StringSplitOptions]::RemoveEmptyEntries
$hhs=$hostheaders.Split(',',$charCount,$option)
get-Website | ? {$_.Name -ne "Default Web Site"} | % {
foreach ($h in $hhs){
$header=$_.Name + $h.Trim()
New-WebBinding -Name $_.Name -HostHeader $header -IP "*" -Port 80 -Protocol http
}
}
Get-WebBinding | ?{($_.bindingInformation).Length -eq 5} | Remove-WebBinding
}
for ($i=1;$i -le $WebSitesNumber;$i++) {
$fpath = $RootFSFolder + "\" + $WebSiteName + $i + $EnviromentName + "\" + $DefaultAppPoolName + $i + $EnviromentName
CreatePhysicalPath $fpath
$appPoolName = $DefaultAppPoolName + $i + $EnviromentName
$GenWebSiteName = $WebSiteName +$i + $EnviromentName
CreateAppPool $appPoolName
If(!(Test-Path "IIS:\Sites\$GenWebSiteName")){
New-Website -Name $GenWebSiteName -PhysicalPath $fpath -ApplicationPool $appPoolName
}
else {
Write-Host "The IIS site $GenWebSiteName already exists" -ForegroundColor Yellow
exit
}
}
SetupBindings $Hostheaders
This script is for creating IIS web sites, their NTFS folders, default application pools, and bindings, and the second script is for creating application pools for specific web applications, web applications themselves, and NTFS folders for them and setting up some configuration parameters for application pools.
This guide will show you the scripts to create the IIS websites. You can see the script below with line-by-line explanations.
<#
.SYNOPSIS
.
.DESCRIPTION
Creates a number of websites as well as folder structure and
default application pools under them. Also sets up bindings
for the newly created websites.
.PARAMETER WebSiteName
This is the site name you are going to create. It will be complemented
by the site number if you are creating more than one site.
.PARAMETER WebSitesNumber
Specifies the number of websites that will be created.
.PARAMETER RootFSFolder
Specifies a root folder where all the websites will be created.
.PARAMETER Hostheaders
Specifies host headers that will be added to each website.
Use quotes for this parameter to avoid errors.
.PARAMETER EnviromentName
Specifies an environment name for the site name. If you omit this,
you will create a site without an environment name.
.PARAMETER DefaultAppPoolName
Specifies default name for application pool. If omitted, the
WebSiteName will be used as the default app pool name.
.PARAMETER Help
Displays the help screen.
.EXAMPLE
C:\PS>.\createIISSites.ps1 -WebSiteName app -WebSitesNumber 3 -RootFSFolder c:\webs -EnviromentName lab -DefaultAppPoolName platformsc -Hostheaders ".contentexpress.lan, .enservio.com"
Creates 3 IIS sites with the names app1lab, app2lab, app3lab, root folder c:\web
and subfolders for each site like c:\webs\app1lab, c:\webs\app2lab etc.,
default app pools for those sites with the names platformsc1, platformsc2 etc.,
and hostheaders for each site with the names app1.contentexpress.lan,
app2.contentexpress.lan, app2.enservio.com etc.
.NOTES
Author: Rock Brave
Date: May 15, 2016
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]$WebSiteName,
[Parameter(Mandatory=$True)]
[int]$WebSitesNumber,
[Parameter(Mandatory=$True)]
[string]$RootFSFolder,
[Parameter(Mandatory=$False)]
[string]$Hostheaders,
[Parameter(Mandatory=$False)]
[string]$EnviromentName,
[Parameter(Mandatory=$False)]
[string]$DefaultAppPoolName,
)
if(-not $DefaultAppPoolName){
$DefaultAppPoolName = $WebSiteName}
import-module WebAdministration
Function CreateAppPool {
Param([string] $appPoolName)
if(Test-Path ("IIS:\AppPools\" + $appPoolName)) {
Write-Host "The App Pool $appPoolName already exists" -ForegroundColor Yellow
return
}
$appPool = New-WebAppPool -Name $appPoolName
}
function CreatePhysicalPath {
Param([string] $fpath)
if(Test-path $fpath) {
Write-Host "The folder $fpath already exists" -ForegroundColor Yellow
return
}
else{
New-Item -ItemType directory -Path $fpath -Force
}
}
Function SetupBindings {
Param([string] $hostheaders)
$charCount = ($hostheaders.ToCharArray() | Where-Object {$_ -eq ','} | Measure-Object).Count + 1
$option = [System.StringSplitOptions]::RemoveEmptyEntries
$hhs=$hostheaders.Split(',',$charCount,$option)
get-Website | ? {$_.Name -ne "Default Web Site"} | % {
foreach ($h in $hhs){
$header=$_.Name + $h.Trim()
New-WebBinding -Name $_.Name -HostHeader $header -IP "*" -Port 80 -Protocol http
}
}
Get-WebBinding | ?{($_.bindingInformation).Length -eq 5} | Remove-WebBinding
}
for ($i=1;$i -le $WebSitesNumber;$i++) {
$fpath = $RootFSFolder + "\" + $WebSiteName + $i + $EnviromentName + "\" + $DefaultAppPoolName + $i + $EnviromentName
CreatePhysicalPath $fpath
$appPoolName = $DefaultAppPoolName + $i + $EnviromentName
$GenWebSiteName = $WebSiteName +$i + $EnviromentName
CreateAppPool $appPoolName
If(!(Test-Path "IIS:\Sites\$GenWebSiteName")){
New-Website -Name $GenWebSiteName -PhysicalPath $fpath -ApplicationPool $appPoolName
}
else {
Write-Host "The IIS site $GenWebSiteName already exists" -ForegroundColor Yellow
exit
}
}
SetupBindings $Hostheaders
Now let’s go through it:
This large commented area at the beginning of the script (synopsis) creates a help section for the script. With the help of PowerShell keywords (.DESCRIPTION, .PARAMETER, .EXAMPLE), I gave my script the ability to use the standard Get-Help command to display a help screen.
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]$WebSiteName,
[Parameter(Mandatory=$True)]
[int]$WebSitesNumber,
[Parameter(Mandatory=$True)]
[string]$RootFSFolder,
[Parameter(Mandatory=$False)]
[string]$Hostheaders,
[Parameter(Mandatory=$False)]
[string]$EnviromentName,
[Parameter(Mandatory=$False)]
[string]$DefaultAppPoolName,
)
This large commented area at the beginning of the script (synopsis) creates a help section for the script. With the help of PowerShell keywords (.DESCRIPTION, .PARAMETER, .EXAMPLE), I gave my script the ability to use the standard Get-Help command to display a help screen.
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]$WebSiteName,
[Parameter(Mandatory=$True)]
[int]$WebSitesNumber,
[Parameter(Mandatory=$True)]
[string]$RootFSFolder,
[Parameter(Mandatory=$False)]
[string]$Hostheaders,
[Parameter(Mandatory=$False)]
[string]$EnviromentName,
[Parameter(Mandatory=$False)]
[string]$DefaultAppPoolName,
)
CmdletBinding() is a PowerShell keyword that tells the interpreter that this script should behave as a compiled C# application.
Param establishes the parameters section. As you can see, my script accepts five different parameters. Some of them are mandatory, and some are not.
$WebSiteName is the common name for the websites that are going to be created.
$WebSitesNumber indicates the number of websites I’m going to create.
$RootFSFolder is the folder where the all IIS sites subfolders are going to reside.
$Hostheaders is the headers that are going to be assigned to IIS sites.
$EnviromentName adds the environment name to each IIS site created by the script. Almost every company I’ve seen has several environments, such as development, staging, and production.
$DefaultAppPoolName specifies the default application pool name for the web site. This parameter is not mandatory because later I can assign the application pool name if it is not provided explicitly.
if(-not $DefaultAppPoolName){
$DefaultAppPoolName = $WebSiteName}
Param establishes the parameters section. As you can see, my script accepts five different parameters. Some of them are mandatory, and some are not.
$WebSiteName is the common name for the websites that are going to be created.
$WebSitesNumber indicates the number of websites I’m going to create.
$RootFSFolder is the folder where the all IIS sites subfolders are going to reside.
$Hostheaders is the headers that are going to be assigned to IIS sites.
$EnviromentName adds the environment name to each IIS site created by the script. Almost every company I’ve seen has several environments, such as development, staging, and production.
$DefaultAppPoolName specifies the default application pool name for the web site. This parameter is not mandatory because later I can assign the application pool name if it is not provided explicitly.
if(-not $DefaultAppPoolName){
$DefaultAppPoolName = $WebSiteName}
As mentioned above, if $DefaultAppPoolName is empty, it uses the $WebSiteName value.
import-module WebAdministration
This is command imports the WebAdministration module. I know that PowerShell now imports modules on the fly, but I still prefer do this explicitly to ensure that the module is really available.
Function CreateAppPool {
Param([string] $appPoolName)
if(Test-Path ("IIS:\AppPools\" + $appPoolName)) {
Write-Host "The App Pool $appPoolName already exists" -ForegroundColor Yellow
return
}
$appPool = New-WebAppPool -Name $appPoolName
}
The function above, as you can gather from its name, creates the application pool. It uses the $appPoolName parameter, tests to determine if a pool with the same name already exists (using Test-Path), and if not, creates the new application pool with the name from the $appPoolName parameter. If the pool name already exists, the function throws an error message and exits.
function CreatePhysicalPath {
Param([string] $fpath)
if(Test-path $fpath) {
Write-Host "The folder $fpath already exists" -ForegroundColor Yellow
return
}
else{
New-Item -ItemType directory -Path $fpath -Force
}
}
The CreatePhysicalPath function does almost the same thing for the NTFS folders. It gets the folder name as a parameter, checks if the folder already exists, spits the error message if it does, and creates a new one if it doesn’t.
Function SetupBindings {
Param([string] $hostheaders)
$charCount = ($hostheaders.ToCharArray() | Where-Object {$_ -eq ','} | Measure-Object).Count + 1
$option = [System.StringSplitOptions]::RemoveEmptyEntries
$hhs=$hostheaders.Split(',',$charCount,$option)
get-Website | ? {$_.Name -ne "Default Web Site"} | % {
foreach ($h in $hhs){
$header=$_.Name + $h.Trim()
New-WebBinding -Name $_.Name -HostHeader $header -IP "*" -Port 80 -Protocol http
}
}
Get-WebBinding | ?{($_.bindingInformation).Length -eq 5} | Remove-WebBinding
}
import-module WebAdministration
This is command imports the WebAdministration module. I know that PowerShell now imports modules on the fly, but I still prefer do this explicitly to ensure that the module is really available.
Function CreateAppPool {
Param([string] $appPoolName)
if(Test-Path ("IIS:\AppPools\" + $appPoolName)) {
Write-Host "The App Pool $appPoolName already exists" -ForegroundColor Yellow
return
}
$appPool = New-WebAppPool -Name $appPoolName
}
The function above, as you can gather from its name, creates the application pool. It uses the $appPoolName parameter, tests to determine if a pool with the same name already exists (using Test-Path), and if not, creates the new application pool with the name from the $appPoolName parameter. If the pool name already exists, the function throws an error message and exits.
function CreatePhysicalPath {
Param([string] $fpath)
if(Test-path $fpath) {
Write-Host "The folder $fpath already exists" -ForegroundColor Yellow
return
}
else{
New-Item -ItemType directory -Path $fpath -Force
}
}
The CreatePhysicalPath function does almost the same thing for the NTFS folders. It gets the folder name as a parameter, checks if the folder already exists, spits the error message if it does, and creates a new one if it doesn’t.
Function SetupBindings {
Param([string] $hostheaders)
$charCount = ($hostheaders.ToCharArray() | Where-Object {$_ -eq ','} | Measure-Object).Count + 1
$option = [System.StringSplitOptions]::RemoveEmptyEntries
$hhs=$hostheaders.Split(',',$charCount,$option)
get-Website | ? {$_.Name -ne "Default Web Site"} | % {
foreach ($h in $hhs){
$header=$_.Name + $h.Trim()
New-WebBinding -Name $_.Name -HostHeader $header -IP "*" -Port 80 -Protocol http
}
}
Get-WebBinding | ?{($_.bindingInformation).Length -eq 5} | Remove-WebBinding
}
This function is a little bit more sophisticated. As you can see, it accepts the $hostheaders string as a parameter, and there can be several headers separated by commas.
$charCount = ($hostheaders.ToCharArray() | Where-Object {$_ -eq ','} | Measure-Object).Count + 1
$charCount = ($hostheaders.ToCharArray() | Where-Object {$_ -eq ','} | Measure-Object).Count + 1
First, I need to count how many values there are. To do so, I count the commas in the string. But since I have commas only between values, I need to increment this number. The result of the whole operation is stored in the $charCount variable.
$option = [System.StringSplitOptions]::RemoveEmptyEntries
Here I’m setting up the $option variable. This variable will later be used for the split method to remove empty entries.
$hhs=$hostheaders.Split(',',$charCount,$option)
Then I’m splitting the values from the $hostheaders into different strings and saving the result in the $hhs variable.
As I now have an array of strings and each of them represents one header, I can actually start setting up headers for the websites.
get-Website | ? {$_.Name -ne "Default Web Site"} | %
With this command, I’m getting all websites which exist on this particular machine without the default one (Default Web Site).
foreach ($h in $hhs){
$header=$_.Name + $h.Trim()
Then I’m looping through my headers array and assigning the $header variable value, which consists of the current site name and the current header name.
New-WebBinding -Name $_.Name -HostHeader $header -IP "*" -Port 80 -Protocol http
Now I create a new binding using the New-WebBinding cmdlet site name and header.
Get-WebBinding | ?{($_.bindingInformation).Length -eq 5} | Remove-WebBinding
When all bindings are set, I remove the default one because I don’t need it.
for ($i=1;$i -le $WebSitesNumber;$i++) {
$fpath = $RootFSFolder + "\" + $WebSiteName + $i + $EnviromentName + "\" + $DefaultAppPoolName + $i + $EnviromentName
CreatePhysicalPath $fpath
$appPoolName = $DefaultAppPoolName + $i + $EnviromentName
$GenWebSiteName = $WebSiteName +$i + $EnviromentName
CreateAppPool $appPoolName
If(!(Test-Path "IIS:\Sites\$GenWebSiteName")){
New-Website -Name $GenWebSiteName -PhysicalPath $fpath -ApplicationPool $appPoolName
}
else {
Write-Host "The IIS site $GenWebSiteName already exists" -ForegroundColor Yellow
exit
}
}
SetupBindings $Hostheaders
I’m now done with the functions, and it’s time to take a look at the main script code.
for ($i=1;$i -le $WebSitesNumber;$i++)
$option = [System.StringSplitOptions]::RemoveEmptyEntries
Here I’m setting up the $option variable. This variable will later be used for the split method to remove empty entries.
$hhs=$hostheaders.Split(',',$charCount,$option)
Then I’m splitting the values from the $hostheaders into different strings and saving the result in the $hhs variable.
As I now have an array of strings and each of them represents one header, I can actually start setting up headers for the websites.
get-Website | ? {$_.Name -ne "Default Web Site"} | %
With this command, I’m getting all websites which exist on this particular machine without the default one (Default Web Site).
foreach ($h in $hhs){
$header=$_.Name + $h.Trim()
Then I’m looping through my headers array and assigning the $header variable value, which consists of the current site name and the current header name.
New-WebBinding -Name $_.Name -HostHeader $header -IP "*" -Port 80 -Protocol http
Now I create a new binding using the New-WebBinding cmdlet site name and header.
Get-WebBinding | ?{($_.bindingInformation).Length -eq 5} | Remove-WebBinding
When all bindings are set, I remove the default one because I don’t need it.
for ($i=1;$i -le $WebSitesNumber;$i++) {
$fpath = $RootFSFolder + "\" + $WebSiteName + $i + $EnviromentName + "\" + $DefaultAppPoolName + $i + $EnviromentName
CreatePhysicalPath $fpath
$appPoolName = $DefaultAppPoolName + $i + $EnviromentName
$GenWebSiteName = $WebSiteName +$i + $EnviromentName
CreateAppPool $appPoolName
If(!(Test-Path "IIS:\Sites\$GenWebSiteName")){
New-Website -Name $GenWebSiteName -PhysicalPath $fpath -ApplicationPool $appPoolName
}
else {
Write-Host "The IIS site $GenWebSiteName already exists" -ForegroundColor Yellow
exit
}
}
SetupBindings $Hostheaders
I’m now done with the functions, and it’s time to take a look at the main script code.
for ($i=1;$i -le $WebSitesNumber;$i++)
This command loops through the site numbers.
$fpath = $RootFSFolder + "\" + $WebSiteName + $i + $EnviromentName + "\" + $DefaultAppPoolName + $i + $EnviromentName
$fpath = $RootFSFolder + "\" + $WebSiteName + $i + $EnviromentName + "\" + $DefaultAppPoolName + $i + $EnviromentName
This long line sets the path to the NTFS folders used and stores the IIS site and application pool files.
CreatePhysicalPath $fpath
Now I’m using the CreatePhysicalPath function to create the folders.
$GenWebSiteName = $WebSiteName +$i + $EnviromentName
CreateAppPool $appPoolName
This line create an application pool with the corresponding name.
If(!(Test-Path "IIS:\Sites\$GenWebSiteName")){
New-Website -Name $GenWebSiteName -PhysicalPath $fpath -ApplicationPool $appPoolName
}
else {
Write-Host "The IIS site $GenWebSiteName already exists" -ForegroundColor Yellow
exit
As the folders and applications pools now exist, I can create the websites: First I check if the folder exists and if not it errors.
SetupBindings $Hostheaders
Because everything is in place now, I am setting up the bindings for the websites. The screenshots below show the results when running the script against my sandbox machine.
The folders
CreatePhysicalPath $fpath
Now I’m using the CreatePhysicalPath function to create the folders.
$GenWebSiteName = $WebSiteName +$i + $EnviromentName
CreateAppPool $appPoolName
This line create an application pool with the corresponding name.
If(!(Test-Path "IIS:\Sites\$GenWebSiteName")){
New-Website -Name $GenWebSiteName -PhysicalPath $fpath -ApplicationPool $appPoolName
}
else {
Write-Host "The IIS site $GenWebSiteName already exists" -ForegroundColor Yellow
exit
As the folders and applications pools now exist, I can create the websites: First I check if the folder exists and if not it errors.
SetupBindings $Hostheaders
Because everything is in place now, I am setting up the bindings for the websites. The screenshots below show the results when running the script against my sandbox machine.

Script output on the PowerShell console

The web sites in the IIS console#

The application pools

The bindings

No comments: