IN THIS CHAPTER
Installing the necessary components
Browsing IIS
Scripting deployments
Managing IIS
Digesting log files
Extending Windows PowerShell to manage IIS 7 script deployments and changes
Managing services and configuration backups
Working with IIS logs
Internet Information Services (IIS) is the collection of web, FTP, and SMTP services that is shipped with Microsoft's Windows server and desktop operating systems. IIS 7.0 was shipped with Windows Server 2008 and Windows Vista. IIS 7.5 is being shipped with Windows Server 2008 R2 and Windows 7. The Windows PowerShell cmdlets and provider that are used with IIS 7 give you complete control over the web and FTP services available in IIS.
The Windows PowerShell provider and cmdlets give you a robust way of managing web services. Automating management and deployment tasks for IIS is useful for a variety of reasons:
The cmdlets that are used to manage IIS 7 come in both snap-in and module form. Starting with Windows Server 2008 R2 and Windows 7, the WebAdministration module is installed automatically when you install IIS 7.5. Windows Server 2008 and Windows Vista (SP1 and higher) require you to install the WebAdministration snap-in that is available on Microsoft's website (in both x86 and x64 formats) in order to manage IIS 7.0.
Note
You can download the WebAdministration snap-in from www.iis.net/download/PowerShell.
If you are using Windows Vista or Windows Server 2008 (not R2), you will need to install the WebAdministration snap-in in order to use the cmdlets. The snap-in is installed with a standard .msi installation package that you must run as an administrator. The installation registers the appropriate DLLs for you. The snap-in works with both Windows PowerShell 1.0 and Windows PowerShell 2.0.
The method used to install the web server role varies depending on whether you are running the Server or Desktop version of Windows.
If you are using Windows Server 2008 R2, the WebAdministration module is installed automatically when installing IIS. IIS 7 can be installed through the Server Manager GUI on Windows Server 2008 and Windows Server 2008 R2:
In addition to the GUI method, you can also install the Web Server role with the Add-WindowsFeature cmdlet that comes with the ServerManager module discussed in Chapter 7. The following line installs the minimal amount of IIS features required to install the WebAdministration module:
Add-WindowsFeature Web-Server
Note
All of the Windows features that can be installed for the Web Server (IIS) role begin with the prefix Web. You can get a complete listing by using Get-WindowsFeature Web*.
If you are using Windows 7, the WebAdministration module is installed with IIS. IIS is installed on Windows 7 and Windows Vista by turning on the Internet Information Services feature:
The technique for loading the cmdlets depends on whether you are using the IIS 7.0 snap-in or the IIS 7.5 module.
You can load the WebAdministration snap-in with:
Add-PSSnapin WebAdministration
You can load the WebAdministration module with:
Import-Module WebAdministration
If you are unsure whether the computer that is running your script will have the module or snap-in installed, you can use the following snippet of code to ensure that the proper version is loaded at the beginning of your IIS scripts.
if (Get-Module -ListAvailable WebAdministration) { Import-Module WebAdministration } elseif (Get-PSSnapin -Registered -EA SilentlyContinue WebAdministration) {
Add-PSSnapin WebAdministration } else { Write-Error “Cannot find the WebAdministration cmdlets” exit } # Add your IIS code here
Note
When you use WebAdministration to manage IIS on Windows Vista and Windows 7, you are required to run an elevated Windows PowerShell session by right-clicking the Windows PowerShell icon and selecting Run as Administrator.
IIS 7 also has a method to manage IIS via Windows Management Instrumentation (WMI). To use the WMI classes, you must install the IIS Management Scripts and Tools feature from either Server Manager (Windows Server 2008) or the Add/Remove Windows Features functionality in Windows Vista and Windows 7. You can install this feature with Add-WindowsFeature as follows:
Add-WindowsFeature Web-ScriptingTools
Note
The WebAdministration cmdlets and provider are so simple that the chances are very high that you will never install the WMI provider. That is, unless you expect to script against SMTP. The WebAdministration cmdlets do not give interfaces to managing SMTP, whereas the WMI provider does. The SMTP service is not integrated into IIS 7; SMTP continues to use IIS 6 as its foundation. This internal design is hidden from you, but it explains why there are no Windows PowerShell interfaces to working with SMTP in the WebAdministration provider and cmdlets.
The WMI provider is created within its own namespace in WMI. To use the Get-WmiObject cmdlet, you will need to specify rootWebAdministration with the -Namespace parameter. For example, to look at the VirtualDirectory class, you would use the following command:
Get-WmiObject -Namespace rootWebAdministration VirtualDirectory
The WebAdministration namespace contains more than 400 classes. You can view them all with the following command:
Get-WmiObject -Namespace rootWebAdministration -List
Note
Although it is possible to manage IIS via WMI via Windows PowerShell, this chapter does not go into those details. This chapter focuses on managing IIS 7 with the Windows PowerShell provider and cmdlets.
The WebAdministration module and snap-in come with a Windows PowerShell provider called WebAdministration that can be used to browse, create, modify, and delete items within IIS. When the module or snap-in is loaded, a default WebAdministration drive is created called IIS:.
You can browse to the IIS: drive by using either Set-Location or its alias cd. The root of IIS: has three folders, which are visible when using Get-ChildItem or dir:
cd IIS: dir
Name ---- AppPools Sites SslBindings
Get-ChildItem .Sites
Name ID State Physical Path Bindings ---- -- ----- -------- ---- -------- Web Site 1 Started %SystemDrive%inetpubwwwroot http *:80: https *:443:
cd .AppPools dir
Name State Applications ---- ----- ------------ Classic .NET AppPool Started DefaultAppPool Started Default Web Site /CertSrv
dir IIS:SslBindings
IP Address Port Store Sites -- ------- ---- ----- ----- 192.168.1.100 443 MY Default Web Site
Scripting new deployments within IIS becomes a breeze with Windows PowerShell. Once you work out all of the requirements for an application, you can automate the process to build web farms or testing environments for your sites.
Scripting configuration changes is an excellent technique you can use to reduce the risk of making changes to production web servers. You can work out your script in a development environment, test it in a QA environment, and then push that change to production.
The New-Item cmdlet enables you to create the following types of items within IIS: via the WebApplication provider:
To create items of the type Application or VirtualDirectory, you must use the dynamic parameter Type that is available only when New-Item is called within the WebAdministration provider.
New-Item also has two additional dynamic parameters when used with this provider. These parameters only exist for New-Item when they are used within the IIS provider.
Websites and FTP sites can be created very easily with the WebAdministration snap-in or module. As you will see throughout this chapter, there is often both a provider and direct cmdlet way of performing these tasks.
The following example illustrates how you can create a website using New-Item. Prior to using the New-Item cmdlet, it will create the underlying directory structure if it does not already exist. It also shows you how to use the Bindings parameter to add two bindings to the site.
$dir = ‘C:inetpubwwwrootPowerShellBible’ if (!(Test-Path $dir)) { md $dir } $bindings = @() $bindings += @{protocol=‘http’;bindinginformation=‘192.168.1.100:8080:’} $bindings += @{protocol=‘https’;bindinginformation=‘*:443:’} New-Item IIS:SitesPowerShellBible -PhysicalPath $dir -Bindings $bindings
The WebAdministration module also provides a cmdlet called New-Website that creates a site without needing to use the provider. New-Website has the following parameters worth noting:
The following example illustrates how this cmdlet is used:
$dir = ‘C:inetpubwwwrootPSBible’ if (!(Test-Path $dir)) { md $dir }
$params = @{ Name=‘PowerShellBible’ Port=443 IPAddress=‘*’ HostHeader = ‘powertoe.wordpress.com’ PhysicalPath=$dir ApplicationPool=‘AppPool1’ SSL=$true } New-Website @params
Note
When using the New-Website cmdlet to create a website, you cannot specify multiple bindings. When using the SSL switch, the website will be created with an HTTPS binding only. This can easily be addressed after the website has been created, but you should be aware of the differences between New-Item and New-Website if you expect to script deployments of IIS sites.
FTP sites are created with New-WebFtpSite. This cmdlet is identical to New-WebSite except that it does not have an ApplicationPool or SSL parameter.
Virtual directories are web folders within a site that point to a folder that is outside of a site's normal directory structure. They are used to point a site directory to another folder on the server, including those that exist on separate disks.
Here is an example of how to use New-Item in the WebAdministration provider to create a virtual directory. The example creates a directory in the PowerShellBible website that is pointing to the D drive of the server.
cd IIS:SitesPowerShellBible New-Item Data -Type VirtualDirectory -PhysicalPath d:
Virtual directories can also be created with the New-WebVirtualDirectory cmdlet. The relevant parameters are:
Here is an example of how this cmdlet can be used.
$dir = ‘C:virtualdir1’ if (!(Test-Path $dir)) { md $dir } $params = @{ Name=‘VirtualDir1’ Site=‘PowerShellBible’ PhysicalPath = $dir } New-WebVirtualDirectory @params
Caution
Even though many of the parameters are optional in the WebAdministration cmdlets, omitting certain parameters may cause you problems. For example, creating a virtual directory without specifying a physical path will make IIS Manager think you have a corrupted configuration when you try to browse to the virtual directory. A good rule of thumb is to supply parameters for each item of information that you normally supply to the GUI when you perform the same function manually.
Creating a web application directory is a very simple process whether you use the provider or the cmdlet. The cmdlet methods within IIS are generally more robust and easier to read and modify in scripts, but it is really just personal preference.
The following example converts a directory in the PowerShellBible website into an application folder via the WebAdministration provider.
cd IIS:SitesPowerShellBible md App #Creates the App directory on the file system New-Item App -Type Application -PhysicalPath (Get-Item .App).fullname
Note
The function mkdir and its alias md is one of the only FileSystem functions that work in the WebAdministration provider. You cannot copy, delete, or write to files using the IIS: drive. If you need to access the files within the websites, you will need to run Get-Item to return the underlying object (as illustrated in the previous example) or use the FileSystem drives that you normally use to manage files on disk.
Application folders can also be created with the New-WebApplication cmdlet. The parameters of importance are:
The following shows an example of how to use this cmdlet:
$dir = ‘C:inetpubwwwrootPSBibleapp1’ if (!(Test-Path $dir)) { md $dir } $params = @{ Name=‘App1’ Site=‘PowerShellBible’ PhysicalPath = $dir ApplicationPool=‘AppPool1’ } New-WebApplication @params
Application pools encapsulate applications and sites into groups of worker processes. Whether you are creating these application pools via the provider or the cmdlet, only one line of code is required.
Creating application pools is very straightforward using New-Item.
New-Item IIS:AppPoolsPowerShellBible
This method is also very straightforward. The cmdlet takes a positional parameter that represents the name of the new application pool.
New-WebAppPool PowerShellBible
Setting up SSL is a multistep process:
The following example shows how to perform the above steps.
New-WebBinding -Name ‘PSBible’ -Protocol https -Port 443 -IPAddress 0.0.0.0 cd IIS:SslBindings $cert = Get-Item cert:LocalMachineMy734A6B9F621496813276A7134D64BFEFA5FF5C11 $cert |New-Item 0.0.0.0!443
Note
This example uses the IP address 0.0.0.0 for both the SSL and web binding. The 0.0.0.0 address is used to specify all IP addresses for this server.
Once items have been created, you can use the cmdlets that you use with any Windows PowerShell provider to get information and remove and modify the underlying objects: Get-Item, Remove-Item, Get-ItemProperty, Set-ItemProperty, New-ItemProperty, and Clear-ItemProperty. It's worth highlighting a few examples of how you can modify items in the provider.
In order to bind a site to an application pool, you can use Set-Item as follows:
$website = Get-Item IIS:SitesPSBible $website |Set-ItemProperty-Name ApplicationPool-Value PSBible
Here is an example of how you can remove all of the existing web bindings and then create a new one.
$website |Clear-ItemProperty -Name bindings $binding = @{protocol=‘http’; bindinginformation=‘*:80:www.wiley.com’} $website |Set-ItemProperty -Name bindings -Value $bindings
This example modifies the queue length for an application pool and then recycles that pool.
$pool = Get-Item IIS:AppPoolsPSBible $pool.queueLength = 3000 $pool.Recycle()
Here is an example of how you can grab one of the properties returned by Get-ItemProperty:
Get-ItemProperty .DefaultAppPool |Select -ExpandProperty processmodel
This example shows how you can use Set-ItemProperty to modify the properties of an application pool. The following two lines of code set a username and password for the application pool to run as:
$properties = @{userName=‘domainIIS_pool’; password=‘password’;identitytype=3} Set-ItemProperty IIS:AppPoolsPSBible -name processmodel -value $properties
The following line of code is a useful one that moves the location where log files are stored from a site to a new directory:
Set-ItemProperty IIS:SitesPSBible -Name LogFile.Directory -Value ‘d:Logs’
Finally, here is an example of using Remove-Item. The following code deletes a website:
Remove-Item IIS:SitesPSBible
In addition to using Remove-Item to delete IIS objects within the IIS: drive, each New-WebIISObject cmdlet also has a corresponding Remove-WebIISObject that can be used. For example, the cmdlet used to create websites is New-Website and the cmdlet used to delete a website is Remove-Website.
Not all configurations are exposed to the WebAdministration provider. Some settings require you to use the WebConfiguration cmdlets. These cmdlets work with the provider to expose more information about the IIS objects.
To understand how these cmdlets work, it's important to realize that these settings are maintained within XML files in a series of layers. For example, a website will get its configuration from a combination of the machine.config, the global web.config, the applicationHost.config, and finally through the web.config that belongs to the site. The cmdlets use XPath queries to find the locations you are looking to view and change.
To view these settings, you use Get-WebConfiguration and Get-WebConfigurationProperty.
Get-WebConfiguration uses the following important parameters:
The following is a snippet of XML that is taken from a machine.config:
<configProtectedData defaultProvider=“RsaProtectedConfigurationProvider”> <providers> <add name=“RsaProtectdConfigurationProvider” type=“System.Configuration.RsaProtectedConfigurationProvider,
System.Configuration,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”
description=“Uses; RsaCryptoServiceProvider to encrypt and decrypt” keyContainerName=“NetFrameworkConfigurationKey” cspProviderName=“” useMachineContainer=“true” useOAEP=“false”/> <add name=“DataProtectionConfigurationProvider” type=<System.Configuration.DpapiProtectedConfigurationProvider,
System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”
description=”Uses CryptProtectData and CryptUnProtectData Windows APIs to encrypt and decrypt” useMachineProtection=“true” keyEntropy=“”/> </providers> </configProtectedData>
To view the configProtectedData section for the PowerShellBible website, you would use the following code:
cd IIS:SitesPowerShellBible Get-WebConfiguration -Filter /configProtectedData |select *
defaultProvider : RsaProtectedConfigurationProvider providers : Microsoft.IIs.PowerShell.Framework.ConfigurationElement PSPath : MACHINE/WEBROOT/APPHOST/Default Web Site Location : ConfigurationPathType : Location ItemXPath : /configProtectedData IsLocked : True OverrideMode : Inherit OverrideModeEffective : Allow SectionPath : /configProtectedData Attributes : {defaultProvider} ChildElements : {providers} ElementTagName : configProtectedData Methods : Schema : Microsoft.IIs.PowerShell.Framework.ConfigurationElementSchema
According to the output, there are ChildElements that refer to the <providers> section of the XML. There is also an attribute called defaultProvider. To browse all of the <add> elements within <providers>, you could use a filter like this:
Get-WebConfiguration -Filter /configProtectedData/providers/*
Caution
Looking at the preceding filter, you would think that using a filter like /* would return all configurations. Unfortunately, it does not. Due to the way the XPath queries work, you will need to look at the /* and /*/* filters to see everything that is available.
To look at the defaultProvider attribute, you can inspect what is returned from Get-WebConfiguration using:
(Get-WebConfiguration /configProtectedData).defaultprovider
As an alternative, you can also use the Get-WebConfigurationProperty cmdlet. Get-WebConfigurationProperty has the same parameters as Get-WebConfiguration with the addition of a -Name parameter to specify the property you would like to view:
Get-WebConfigurationProperty /configProtectedData
-Name defaultprovider| Select Value
Sections are added to a configuration using Add-WebConfiguration and Add-WebConfigurationProperty. For example, if you wanted to add a new filename to the default files for the site, you could do the following.
$filter = ‘/system.webServer/defaultDocument/files’ Add-WebConfiguration $filter -AtIndex 0 -value @{value=“default.html”}
Caution
You must be mindful of case-sensitivity when working with these XML configuration files. The filter listed in this example is very specific.
Sections are modified using Set-WebConfiguration and Set-WebConfigurationProperty. The following example shows how you can use Set-WebConfiguration. The snippet redirects the PowerShellBible site to wiley.com.
cd IIS:SitesPowerShellBible Set-WebConfiguration system.webServer/httpRedirect -Value @{ enabled=$true destination=“http://wiley.com” exactDestination=$true httpResponseStatus=“Permanent” }
This final example shows how to use the Set-WebConfigurationProperty. Specifically, it shows you how to change the CGI timeout value for the PowerShellBible site.
cd IIS:Sites $filter = ‘/system.webserver/cgi’ Set-WebConfigurationProperty $filter -name timeout -Value ‘00:20:00’ ` -Location ‘PowerShellBible’
Caution
In the last example, if you run the Set-WebConfigurationProperty cmdlet from IIS:SitesPowerShellBible, you will receive the following error: “This configuration section cannot be used at this path. This happens when the section is locked at a parent level.” By executing this one level up, IIS:Sites, and specifying the -Location parameter, you are able to explicitly override this locked setting.
In addition to the WebConfiguration cmdlets, there are a few cmdlets that are designed to work with IIS 7 modules:
The cmdlets that refer to the global modules have a module type of Managed when looking at the modules with IIS Manager. The cmdlets are very straightforward in their usage. For example, the Get cmdlets will retrieve all of the modules, or you can specify a module name you would like to view information about. All the cmdlets take the name of the module by using the Name positional parameter. For example, the following will disable the AnonymousAuthenticationModule:
Disable-WebGlobalModule AnonymousAuthenticationModule
There are not many properties you can set when creating or modifying a module. Typically, the managed modules can point to a new type or precondition while the global modules can be pointed to a new DLL by using the Image property. The following example shows how you can modify a global and managed module:
Set-WebManagedModule UrlMappingsModule -Type PSBible.Mappings Set-WebGlobalModule IsapiModule -Image c:customisapi.dll
In addition to scripting deployments and changes to sites, Windows PowerShell can also be used to perform administrative tasks for IIS. This may be something as simple as controlling the state of IIS services or performing backups or restorations of configuration settings.
Application pools, websites, and the underlying Windows services of IIS can all be controlled via Windows PowerShell.
You have already seen that the provider exposes methods on application pools to allow things like stopping, starting, and recycling. You can also use the Stop() and Start() methods on the objects returned by Get-Item when using the cmdlet against items in the IIS:Sites container:
(Get-Item IIS:sitesPowerShellBible).Stop() (Get-Item IIS:sitesPowerShellBible).Start()
In addition to the preceding technique, there are six cmdlets to help you start and stop sites and application pools:
You can use the cmdlets by themselves:
Stop-WebSite PowerShellBible Start-WebSite PowerShellBible
Or you can pipe the provider objects into the cmdlets:
Get-ChildItem IIS:sites |Stop-WebSite Get-Item IIS:sitesPowerShellBible |Start-WebSite
The Start-WebItem and Stop-WebItem cmdlets can be used with either an application pool or a site.
Note
Actually, the AppPool and WebSite stop and start cmdlets are just wrappers to Start-WebItem and Stop-WebItem. When you call Start-WebSite, it looks for an item in IIS:sites with the name you are passing it, and then it pipes it into Start-WebItem. Similarly, the Start-AppPool cmdlet does the same thing, but it looks in IIS:AppPools.
While discussing starting and stopping items, it's also important to note that no WebAdministration cmdlets stop and start IIS because the cmdlets built into Windows PowerShell V2 already have cmdlets that do this for you. The only problem is that there is more than one service controlled via iisreset in IIS 7: W3WSVC, WAS, and IISADMIN (installed only if you are using components of IIS 6). You could write a set of functions that would handle this natively in Windows PowerShell, but there's really no reason to because IISReset.exe works fine from within Windows PowerShell — even within a remoting session.
Caution
There is a small issue with IISReset in Windows 7. If you run IISReset from a Windows PowerShell ISE window, it will work without problem, but if you run it from a regular Windows PowerShell window, it will return unsuccessfully without an error. The workaround is to use start iisreset instead.
The cmdlets and methods you can use to stop and start sites and application pools work regardless of the state the item is in prior to you calling the start or stop methods. In other words, if you call Start-WebSite on a site that is already running, it will not do anything, but it will not fail, either. Even though this logic is built into the cmdlets, you may still be interested in looking at the state for verification or reporting purposes within your script.
As with stopping and starting, you can do this in multiple ways with the WebAdminstration provider and cmdlets. You can use the cmdlets Get-WebSiteState, Get-WebAppPoolState, or Get-WebItemState the same way you use the stop and start cmdlets. You also have the ability to inspect the state property of a website or app pool item returned from the Get-Item cmdlet. For example, each of these will show you the same thing:
Get-WebAppPoolState PowerShellBible (Get-Item IIS:AppPoolsPowerShellBible).State $pool = Get-Item IIS:AppPoolsPowerShellBible $pool.state $pool = Get-Item IIS:AppPoolsPowerShellBible $pool | Get-WebAppPoolState $pool = Get-Item IIS:AppPoolsPowerShellBible $pool | Get-WebItemState
If you would like to retrieve the state of all of the app pools, you can use either of the following lines of code:
Get-ChildItem IIS:AppPools |Get-WebItemState Get-WebAppPoolState
If you would like to back up your entire IIS configuration, you can do so with Backup-WebConfiguration BackupName. This creates a folder in $env:windir System32inetsrvackup with the name you supplied to the cmdlet. The data in the backup directory can be restored with Restore-WebConfiguration BackupName. You should ensure that IIS is stopped prior to running the restoration or you will get errors.
Reading IIS log files can be a cumbersome task if it needs to be done manually. Though a lot of tools are available to help you make sense of IIS logs, it's important to note that Windows PowerShell truly excels at this type of data manipulation. There is a slight trick to the technique in order to get the header information, but once that is determined, the process is very simple.
To let ConvertFrom-Csv turn each line of the log file into a Windows PowerShell object, you must first determine the header of the log from the line that begins with #Fields:. Once that is obtained, you can pass a single space character to the -Delimiter property of the cmdlet. Listing 15-1 is a complete script that uses this technique to parse the log file into Windows PowerShell objects.
LISTING 15-1 Parsing an IIS Log File
$site = ‘IIS:sitesPowerShellBible’ #The logfile.directory configuration usually has an environment variable #Passing it to cmd will get the full path $log = cmd /c echo (Get-Item $site).logfile.directory #Get the log file path $log = Join-Path $log (”W3SVC” + (Get-Item $site).id) #Get the full path to the log file $log = Join-Path $log “u_ex$yesterday.log” $header = @() $logentries = get-content $log |foreach { #Read the log file and look for the first line that has Fields# if ($header.count -eq 0 -and $_ -match ‘#Fields: ([sS]+)’) { #Split the line by a single space to get the header properties #for the log file $header = $matches[1] -split ‘s’ } else { #Make sure the line does not begin with a comment symbol
if ($_ -notmatch ‘^#’) { #Convert the line into PowerShell objects from a #space delimited file $_ |ConvertFrom-Csv -Header $header -Delimiter ‘ ’ } } } #Display the entries to the screen $logentries
Once you have the log entries as Windows PowerShell objects, you can use the filtering, sorting, and formatting cmdlets you are familiar with. For example, if you wanted to filter for a list of 404 errors, you could use Where-Object or its alias where:
$logentries |where {$_.$(“sc-status”) -eq 404}
Sorting can be done by using the Sort-Object cmdlet or its alias sort:
$logentries |sort time-taken
You could just as easily use Export-CSV or ConvertTo-HTML. For example, the following creates an HTML report that shows the date/time along with the client's IP address and browser type. It uses Out-GridView so that you can more easily sort or filter the entries manually:
$logentries |Out-GridView $logentries |select date,time, ‘cs(User-Agent)’, c-ip | ConvertTo-Html | Out-File c: eport.html Start c: eport.html
As is the case with most things in Windows PowerShell, there are often multiple ways to accomplish tasks with the WebAdministration module and snap-in. The path you choose doesn't really matter. The important thing to realize is that you have everything you need to automate, manage, and report on your IIS servers directly within Windows PowerShell.
The next chapter looks at how you can use PowerShell to help manage System Center Operations Manager.