SSL: automatic renewal

Windows Server and IIS

A Windows Task Scheduler task fetches the fresh certificate once a day, links it to the key in the Windows store and binds it to the IIS site.

Renews by itself

Use this for a site on IIS (Windows Server 2016 and newer) if the certificate is already installed on the server, meaning the private key is in the Windows certificate store. This is the case when the CSR was created in IIS ("Create Certificate Request") or a PFX was imported at some point.

How it works: a PowerShell script fetches the new certificate from the link, puts it into the computer store and links it to the key already there (certutil -repairstore). If the server has no key for the new certificate, the script changes nothing. Then it binds the new certificate to all of the site's HTTPS bindings in IIS.

What you need

  • The certificate link from your account: https://api.zrazu.com/ssl-pull/zsp_YOUR_TOKEN.
  • Sign-in to the server with administrator rights.
  • The site name in IIS (as in IIS Manager → Sites), for example Default Web Site.

Step 1. First installation (if the certificate is not installed yet)

If the CSR was created in IIS: IIS Manager → server → Server Certificates → Complete Certificate Request → the cert.pem file from https://api.zrazu.com/ssl-pull/zsp_YOUR_TOKEN/cert.pem. If you have the key as a file, build a PFX in your account (certificate card → "Files" → PFX) and import it: Server Certificates → Import. Then Sites → site → Bindings → https → SSL certificate.

Step 2. The script

Open PowerShell as administrator and create the file:

New-Item -ItemType Directory -Force -Path "$env:ProgramFiles\ZRAZU" | Out-Null
notepad "$env:ProgramFiles\ZRAZU\zrazu-ssl.ps1"

Paste the text below into Notepad, put your IIS site name in the $Site line, and save.

# ZRAZU: updates the SSL certificate in IIS using the personal link. Run by Task Scheduler once a day.
$ErrorActionPreference = "Stop"
$Url = "https://api.zrazu.com/ssl-pull/zsp_YOUR_TOKEN"
$Site = "Default Web Site"
$Dir = Join-Path $env:ProgramFiles "ZRAZU"
$CertFile = Join-Path $Dir "cert.cer"
$ChainFile = Join-Path $Dir "chain.cer"
Invoke-WebRequest -Uri "$Url/cert.pem" -OutFile $CertFile -UseBasicParsing
Invoke-WebRequest -Uri "$Url/chain.pem" -OutFile $ChainFile -UseBasicParsing
$New = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($CertFile)
# Already imported — do not import again, but check the binding: yesterday the import may have worked and the binding not.
$Have = Test-Path "Cert:\LocalMachine\My\$($New.Thumbprint)"
if (-not $Have) {
  Import-Certificate -FilePath $ChainFile -CertStoreLocation Cert:\LocalMachine\CA | Out-Null
  Import-Certificate -FilePath $CertFile -CertStoreLocation Cert:\LocalMachine\My | Out-Null
  certutil -repairstore my $New.Thumbprint | Out-Null
}
$Mine = Get-Item "Cert:\LocalMachine\My\$($New.Thumbprint)"
if (-not $Mine.HasPrivateKey) {
  if (-not $Have) { Remove-Item $Mine.PSPath }
  throw "ZRAZU: this server has no key for this certificate — certificate not replaced"
}
Import-Module WebAdministration
foreach ($Binding in Get-WebBinding -Name $Site -Protocol https) {
  if ($Binding.certificateHash -ne $New.Thumbprint) { $Binding.AddSslCertificate($New.Thumbprint, "My") }
}
$Until = $New.NotAfter.ToString('yyyy-MM-dd')
if ($Have) { "ZRAZU: certificate unchanged, valid until $Until" } else { "ZRAZU: new certificate installed, valid until $Until" }

Run it once by hand:

powershell -NoProfile -ExecutionPolicy Bypass -File "$env:ProgramFiles\ZRAZU\zrazu-ssl.ps1"

Step 3. Schedule

schtasks /Create /F /SC DAILY /ST 04:17 /RU SYSTEM /TN "ZRAZU SSL" /TR "powershell -NoProfile -ExecutionPolicy Bypass -File \"C:\Program Files\ZRAZU\zrazu-ssl.ps1\""

The task will appear in Task Scheduler → Task Scheduler Library → ZRAZU SSL and will run every day at 04:17 as the system.

Check

  1. A manual run prints "new certificate installed" or "certificate unchanged".
  2. IIS Manager → Sites → site → Bindings → https — the certificate with the new date is selected.
  3. Site: https://zrazu.com/tools/ssl/example.com/.
  4. The result of the daily runs: Task Scheduler → ZRAZU SSL → History (the Last Run Result column: 0x0 means success).

If it did not work

  • "this server has no key for this certificate" — the certificate was issued for a key that this server does not have (reissued with a new key, or the CSR was created elsewhere). Import a PFX with the new key (step 1).
  • running scripts is disabled — run it exactly as in steps 2–3: with -ExecutionPolicy Bypass.
  • (404) Not Found — the link is disabled or replaced: create a new one in the certificate card and replace $Url.
  • A site on IIS 8 / Windows Server 2012 — update the server: this Windows version no longer receives security updates.

This page in Markdown