Here’s another PowerShell IIS script for your liking.
Let’s get one thing straight. I hate cryptography and certificates. Over my career, I’ve been the “certificate guy” on a few occasions. However, it was just another hat for a system administrator. I never got to the point to where I completely understood the technology and it seemed like every task I tried to accomplish around that area seemed to never work out. It’s definitely an unforgiving technology for sure.
Let me tell you a story about automating getting a certificate installed on an IIS server with PowerShell.
Table of Contents
From a client, request a new public Digicert certificate, get it installed on a remote Windows Server 2012 R2 Core Server running IIS 8.5 in a workgroup, set a SSL binding to a website and use the installed certificate for the binding. Simple, right? LOLz!
How it’s Done, Son!
In a nutshell, it takes X steps to make this happen.
- Generate a certificate signing request on the computer where the cert will be installed. In my case, I only had WinRM access so I had to execute certreq.exe on the remote server using
Invoke-Commandand send the content of the CSR to a local file.
- Get the CSR signed by a public CA. (No help here -a pretty cert just came back from security that I worked with). In my case, the security team gave me back a P7B collection of certs rolled up into a .CER file which included the cert I was looking for as well as the intermediate certs. This took a bit of tinkering to work.
- Complete the certificate signing request on the computer where the cert will be installed. This will get the certificate into the certificate store.
- Create a SSL IIS web binding on the server.
- Attach the certificate to the web binding.
Generating the CSR with this PowerShell IIS Script
Your first task will be to run certreq.exe with this PowerShell IIS script on the remote server to gather up a request file. To do this, certreq.exe requires an INF file as input. This file is used for all the various options your certificate will end up having. Without going into a ton of detail, this is a copy of the INF file that I was using.
[Version] Signature = "$Windows NT$" [NewRequest] Subject = "C=US,S=State,L=City,O=Company,OU=IT,CN=Name" Exportable = TRUE KeyLength = 2048 KeySpec = 1 KeyUsage = 0xa0 MachineKeySet = True ProviderName = "Microsoft RSA SChannel Cryptographic Provider" ProviderType = 12 Silent = True SMIME = False RequestType = PKCS10
You’ll see in the
New-CertificateSigningRequest function that I make it super-easy for you to customize this. Actually, if you use my functions you’ll never even see this file as it’s only needed temporarily to create the CSR (request file).
Next, you’ll need to get this INF file on the remote server and run certreq.exe with the following parameters like
certreq.exe -new "$InfFilePath" "$reqFilePath".
This will generate a CSR (request file) on the remote computer. You’ll then need to send this file to your security team. This will also create a certificate containing both the private key and the public key in the Certificate Issued Requests in the local machine context.
Completing the CSR
In my case, I got back a single CER file. I created a function in this PowerShell IIS script to simply import this directly into the Personal store in the local machine context to find that IIS couldn’t see it. The reason was that the certificate had to contain the private key as well. Simply importing the certificate into the Personal store would not work. I had to complete the certificate request use certreq.exe.
To do this, you will need to copy the certificate you receive from your security team onto the remote server and then execute certreq.exe like this
certreq.exe -accept -machine "C:\issuedcert.cer".
You’ll always need to ensure that the response certificate always goes into the local machine context by using the
-machine parameter. This should complete successfully according to everything I read but it definitely did not for me. For some reason, I was receiving an error that looked like this:
It turns out this means that the public key in the request file did not match what was returned by the security team. To test this, simply run
certutil.exe -dump requestfile.req and
certutil -dumpissuedcert.cer. Scroll down through the output until you see the public key area.
Copy out each of these private keys and compare in a text editor to ensure they’re the same. If not, get onto your security team for not signing your request right!
If those match, you’re good and you should not receive that error.
Create the SSL Binding
On the remote server run:
PS> Import-Module WebAdministration PS> New-WebBinding -Name $WebsiteName -IP * -Port 443 -Protocol https
Attach the Certificate to the SSL Binding
On the remote server, run:
PS> $certificate = Get-Item Cert:\localmachine\My\$Thumbprint PS> $certificate | New-Item "IIS:\SSLBindings\0.0.0.0!443"
Get the PowerShell IIS Script
If all goes well, you should be done! Now, if you need a little help doing this I’ve created three functions to make it happen a lot easier. Head on over to my Github repo to get the functions:
## Send the file that this outputs to your certificate people New-CertificateSigningRequest -SubjectHost 'somesubject' -FilePath 'C:\somefile.req' -ComputerName REMOTESERVER ## Import the cert receieved Import-CertificateSigningRequestResponse -FilePath C:\issuedcert.cer -ComputerName REMOTESERVER ##Create the binding and attach the cert. New-IISWebBinding -ComputerName 'zapp09rpr01' -WebsiteName GHI -Protocol https -Port 443 -Certificate $cert
Be sure to check out this other ATA blog posts on managing IIS with PowerShell