IIS: Manage multiple certificates on multiple Web servers

While it can be cumbersome to manage numerous security certificates, you can use Windows PowerShell to expedite the process.

Jason Helmick

Managing certificates in large-scale Web environments is a large-scale challenge. There might be hundreds of servers hosting hundreds of SSL Web sites, all with unique certificates. Installing and maintaining certificates on this scale is a time-consuming process with the GUI of IIS Manager.

Have no fear—there is a better way. Whether you have two or 200 Web servers, Windows PowerShell can turn days, weeks or months of your time into mere minutes. Throughout the process of deploying, installing and configuring certificates and SSL for your Web sites, there are a few tricks you can use to locate certificates that are about to expire so you can replace them.

The ground rules are simple: This works on IIS 7.5 and IIS 8, but you need Windows PowerShell remoting enabled. For this example, there are two Web servers configured in a load balance with their own cluster IP addresses. Each is running three new Web sites that need certificates and bindings for SSL. I’ve changed the cluster IP addresses in the code to internal addresses to obscure the actual Web sites, but you get the idea. I purchased my new certificates and have the .pfx files ready to deploy. Here’s the rundown of the existing Web sites:

  • Shop.Company.com – IP Address – Cert: shop.company.com.pfx
  • Update.Company.com – IP Address – Cert: update.company.com.pfx
  • Register.Company.com – IP Address – Cert: register.company.com.pfx

Let’s get started by deploying and installing the certificates on the load-balanced Web servers.

Deploy and install

You need two things to get started—a variable that contains a list of the Web server computer names and a Windows PowerShell remote session to each of those servers. My computer names came from a text file on my computer, but you can grab them from Active Directory if the Web servers are members of the domain:

PS> $servers = Get-Content c:\webservers.txt PS> $session = New-PsSession –ComputerName $servers

Copy the certificates to the remote servers so you can easily install them. Don’t worry about someone stealing your .pfx files. You’ll delete them before this section is complete. For this demonstration, my .pfx files are on my client computer c:\sites\certpfx:

PS> $servers | foreach-Object{ copy-item -Path c:\sites\certpfx\*.* -Destination "\\$_\c$"}

CertUtil.exe is a great tool for installing certificates over Windows PowerShell remoting sessions. In the following one-liners, each Web server will install the certificates. Remember, there are three certificates to install (one for each Web site), so I repeated the commands:

PS> Invoke-command -Session $session { certutil -p P@ssw0rd -importpfx c:\shop.company.com.pfx} PS> Invoke-command -Session $session { certutil -p P@ssw0rd -importpfx c:\update.company.com.pfx} PS> Invoke-command -Session $session { certutil -p P@ssw0rd -importpfx c:\register.company.com.pfx}

When you use CertUtil.exe, you’ll need to specify the password for the .pfx. Because I performed this in real time, I just typed the passwords. If you’re going to build a script, I’d recommend replacing the passwords with a variable that gets the password from a prompt, such as:

PS> $Cred = (Get-Credential).password

Make sure to delete the .pfx files from the remote Web servers to prevent any possible certificate theft:

PS> $servers | foreach-object {Remove-Item -Path "\\$_\c$\*.pfx"}

With the certificates installed on the remote servers, the next step is to create the HTTPS bindings for the Web sites.

Create Web site bindings

Each Web site on each server in the load balance needs an HTTPS binding. Use the New-WebBinding cmdlet from the WebAdministration module and the process will be a snap. The cmdlet parameters specify the site name, protocol, port and the cluster IP address for the site. The SSLFlags determine where the certificate you’ll use for the binding is located:

PS> Invoke-Command -session $session {Import-Module WebAdministration} PS> Invoke-command -Session $session { New-WebBinding -name shop -Protocol https -Port 443 -IPAddress -SslFlags 0} PS> Invoke-command -Session $session { New-WebBinding -name update -Protocol https -Port 443 -IPAddress -SslFlags 0} PS> Invoke-command -Session $session { New-WebBinding -name register -Protocol https -Port 443 -IPAddress -SslFlags 0}

The certificates you just installed are in Windows certificate storage, but here are the options:

  • 0 – Regular certificate in Windows certificate storage
  • 1 – Server Name Indication (SNI) certificate
  • 2 – Central certificate store
  • 3 – SNI certificate in central certificate store

There’s one last step to complete, which is often overlooked and forgotten: linking the certificates to the new Web site bindings.

Bind the certificates

This last step can be confusing. The graphical IIS Manager hides this part of the process, but you can’t leave it out if you want your sites to work with SSL. You have to link the certificate to the binding of the Web site.

This is a two-phase process. First, you have to get the thumbprint of the certificate for each Web site so it can create a proper SSL binding. Remember, I’m working with three Web sites in this example. I’ll need the thumbprint from each unique certificate. I chose to store the thumbprint in three different variables so I didn’t confuse them when I link them to the Web sites:

PS> Invoke-Command -session $session { $CertShop=Get-ChildItem -Path Cert:\LocalMachine\My | where-Object {$_.subject -like "*shop*"} | Select-Object -ExpandProperty Thumbprint} PS> Invoke-Command -session $session { $CertUpdate=Get-ChildItem -Path Cert:\LocalMachine\My | where-Object {$_.subject -like "*update*"} | Select-Object -ExpandProperty Thumbprint} PS> Invoke-Command -session $session { $CertRegister=Get-ChildItem -Path Cert:\LocalMachine\My | where-Object {$_.subject -like "*register*"} | Select-Object -ExpandProperty Thumbprint}

Now it’s time to use the thumbprint to grab the entire certificate and assign it as an SSL binding for each Web site. The following command uses Get-Item to grab the certificate, then New-Item to create the SSL binding; the IIS provider IIS:\SSLBindings creates the SSL bindings along with the binding information:

PS> Invoke-Command -Session $session { get-item -Path "cert:\localmachine\my\$certShop" | new-item -path IIS:\SslBindings\!443} PS> Invoke-Command -Session $session { get-item -Path "cert:\localmachine\my\$certUpdate" | new-item -path IIS:\SslBindings\!443} PS> Invoke-Command -Session $session { get-item -Path "cert:\localmachine\my\$certRegister" | new-item -path IIS:\SslBindings\!443}

In IIS binding information is normally displayed as IPAddress:Port:Hostname (*:80:*). However, Windows PowerShell interprets the colon (:) as a path indicator. When you’re using Windows PowerShell to set binding information, you use the exclamation point (!) instead.

The bindings are now complete and you can reach the Web sites using HTTPS, as demonstrated here:

PS> start iexplore https://shop.company.com PS> start iexplore https://update.company.com PS> start iexplore https://register.company.com

Check for expiration

There’s another useful solution that employs all the information you just learned. It’s a common business challenge for many of you: Are your certificates about to expire?

Part of managing certificates is determining if some are about to expire so you can replace them. You can apply the concepts and tactics used here to this problem. You just need one more one-liner. Again, you’ll still need a Windows PowerShell remoting session open for the Web servers you want to check for expiring certificates.

The following example scans all the certificates in the localmachine certificate store. It compares the certificate expiration property notafter to the current date in a custom column I named ExpireInDays. The filter (Where-Object) checks to see if any certificates have fewer than 90 days before they expire. The command lists the server names and certificates about to expire:

PS> Invoke-Command -Session $session { Get-ChildItem -Path Cert:\LocalMachine\My | Select-Object -Property PSComputerName, Subject, @{ n='ExpireInDays';e={($_.notafter - (Get-Date)).Days}} | Where-Object {$_.ExpireInDays -lt 90}}

Using all you’ve learned here, you can successfully and quickly replace all those certificates—even on a large scale—using Windows PowerShell.

Jason Helmick

Jason Helmick* is the director of Windows PowerShell technologies for Interface Technical Training, based in Phoenix, Ariz. He’s a speaker, author, teacher and inadvertent IIS administrator.*