Oct 23, 2016

How to Create IIS Websites with PowerShell Script

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.

However, I couldn’t put everything into just one script, so I wrote two. The first 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.

Today I’ll discuss about only first of the scripts to create the IIS websites. You can see the script below with line-by-line explanations following.

<#
 .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,
  
 )
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}
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
 }
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
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++)
This command loops through the site numbers.

$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.

 
Script output on the PowerShell console


 
The web sites in the IIS console#


 
The application pools


 
The bindings


 
The folders

Post a Comment

 
TECH SUPPORT © 2012 - Designed by INFOSBIRD