Introduction

I handle the builds for a particular piece of software at my place of work. I am responsible for deploying any new versions of code to all environments; including but not limited to: Development (Dev), Quality Assurance (QA), Staging aka User Acceptance Testing (UAT) and most importantly Production (Prod). When I was given this responsibility there was no existing structure or procedure for getting the job done. A build could take an hour or longer to execute because files were being copied in the worst possible way imaginable – by hand… In this day and age there is no reason for anyone, let alone a developer, to be doing any type of repetitive zero brain function task manually. Therefore I did what I always do when I see a pattern and a way to make it faster, I wrote a program. What used to take over an hour to do I can now do in less than a minute. I’m not exaggerating.

Windows Management Instrumentation (WMI)

The goal here for me was to copy a published version of code to multiple destinations and then recycle the application pool for each site. I never had to do remote IIS operations before – enter WMI. WMI, from what I understand, is both loved and hated by all. It can be buggy and sometimes it works just fine. In my particular case it worked wonderfully and continues to work very well to this day. If you want to read up on it take a look here.

Considerations

  1. WMI is only as powerful as to what you have access too. Just like you need permissions to alter files and folders on a remote system via UNC file paths – this is no different, WMI will do nothing for you if you do not have the proper access on the target system.
  2. WMI must be enabled on your system (host running the script) and the target system (web server) in order for WMI to work. WMI runs as a service.
  3. The script I am sharing will only work with IIS7 and greater. IIS6 uses something different and older obviously. I strongly suggest moving to IIS7.5 at this point. For IIS6 view the example here.

Powershell Script Functions

I have three functions to share and they are as follows:
  1. GetApplicationPools – literally get all application pools on a target web server
  2. RecycleApplicationPools – recycle a specific list of application pools on a target web server
  3. ApplicationPoolStateDescription – translate the application pool state unsigned integer into a human readable string
You will see below that for the Authentication argument it is hardcoded to the magic number 6. I don’t accept using magic numbers, but for this case I am giving it a pass since this is a script. The 6 represents PacketPrivacy apparently. The credentials being used for the WMI execution are purely based on who is running the script. If you start Powershell as an Administrator – then that session will have Administrative privileges and your identity will be shoved along for all commands executed. Get-WmiObject -Authentication

Get All Available Application Pools

#Get all application pool names for the specified server name
#Returns the names as an Array
function GetApplicationPools
{
 param([string]$server)
 
 #http://theolddogscriptingblog.wordpress.com/2012/07/09/powershell-iis-and-driving-myself-nuts/
 $lst = Get-WmiObject -Namespace 'rootwebadministration' `
      -Class ApplicationPool `
      -ComputerName $server `
      -Authentication 6
      
 $arrNames = @();

 #optional loop for additional filtering
 foreach($p in $lst)
 {
  #if($p.Name.ToLower().Contains("pattern") -eq $true)
  $arrNames = $arrNames + @($p.Name)
 }

 return $arrNames
}

Recycle Specified List of Application Pools

#Get all application pool names for the specified server name
function RecycleApplicationPools
{
 param([string]$server, [Array]$poolNames)
 
 if($arrPools.Length -gt 0)
 {
  (write-host ("Restarting Application Pools") -foregroundcolor "yellow" -backgroundcolor "black")

  [string]$strState = [string]::Empty;
  $pool = $null

  foreach($p in $poolNames)
  {
   #Using ApplicationPool WMI object
   #http://msdn.microsoft.com/en-us/library/ms690608(v=vs.90).aspx
   $pool = Get-WmiObject -Namespace 'rootwebadministration' `
        -Class ApplicationPool `
        -ComputerName $server `
        -Authentication 6 `
        -Filter ('Name="' + $p + '"')

   $pool.Recycle();    
   
   $strState = ApplicationPoolStateDescription -state $pool.GetState().ReturnValue
   
   (write-host ("`t" + $pool.Name + " = " + $strState) -foregroundcolor "cyan" -backgroundcolor "black")
  }
 }
 else
 {
  (write-host ("No application pools were restarted because the provided list was empty") -foregroundcolor "red" -backgroundcolor "black")
 }
}

Translate Application Pool State Unsigned Integer into Human Readable String

#Get the application pool's state's description
#Converts UInt to a string equivalent
#Returns the string
function ApplicationPoolStateDescription
{
 param([uint32]$state)

 [string]$strState = [string]::Empty;
 
 #GetState() Method's output as integer to string
 #http://msdn.microsoft.com/en-us/library/ms691445(v=vs.90).aspx
 switch($state)
 {
  0 { $strState = "starting" }
  1 { $strState = "started" }
  2 { $strState = "stopping" }
  3 { $strState = "stopped" }
  4 { $strState = "unknown" }
  default { $strState = "unknown2: " + $state.ToString() }
 }
 
 return ,$strState;
}

Conclusion

Don’t work harder than you have to. If you can automate a redundant process, you should – you aren’t gaining anything from doing it manually repeatedly other than muscle memory and a large loss of time. My deployments take less than a minute to do now. Work smart – not hard.

Resources

Leave a Reply

Your email address will not be published. Required fields are marked *