Best practice for easy IIS certificate management

We have an internal Windows CA that we use to issue certificates for our internal DEV/TEST systems. Previously I’ve stored all those certificates in Octopus and assigned them during deployment (via Deploy an IIS Site). Of course, I now have 30 certificates that are getting ready to expire around the same time.

I could re-issue all them from the CA and do the same thing again - but I’m wondering if there is a way to leverage IIS’ ability to renew certs from an internal CA?

Anyone done this with Octopus?

Hi Blake,

This is a great question. I thought I’d highlight a couple of Octopus pieces that would be likely to play a part in a solution for this.

Replace Certificate API Endpoint
There is an API endpoint at /api/certificates/{certificate-id}/replace which is designed for when a certificate has been renewed. It archives the existing certificate and replaces it with the new one. Any Octopus variables which reference the old certificate are automatically now pointing at the new cert.

This can be invoked from the UI, but if you’re automating it then using the Octopus .NET client library is probably the easiest way.

Subscribing to Certificate Expiry Events
Octopus raises events 20-days-prior, 10-days-prior, and on expiry. You can configure subscriptions for these which can invoke a webhook.
This is handy if you want Octopus to be the source of truth for certificate expiry.

I hope that is some help. Unfortunately there is nothing out-of-the-box in Octopus relating to the IIS certificate rebind functionality. But if we can be of any assistance along the way, please don’t hesitate to reach out.

This would make a great blog post :slightly_smiling_face:

Regards,
Michael

What should be the body of “/api/certificates/{certificate-id}/replace” POST request?
I guess there should be at least file content (encoded how?) and password, but my octopus server under “/swaggerui” page doesn’t have any information about that.

The body of that POST should be a JSON object with a certificateData property and a password property.

The certificateData property should contain the base64-encoded certificate. The certificate can be any of the supported formats.
Note that even if the cert is in PEM format, it should still be base64 encoded.

So after beating my head on this for a bit I decided to create my own solution.

<#
Blake A. Duffey
1.8.2020
delete expired certs, search the store for an appropriate, valid cert, return that thumbprint
#>

$FQDN = $FQDN.ToLower()
$domain = (Get-WmiObject Win32_ComputerSystem).Domain

#delete any expired certs that match the FQDN
Get-ChildItem -Path cert:\LocalMachine\My -ExpiringInDays 0 | Where-Object ($_.Subject -like "CN=$FQDN") | Remove-Item

#get the latest matching cert for this FQDN 
$Cert = Get-ChildItem -Path cert:\LocalMachine\My, cert:\LocalMachine\WebHosting | Where-Object {$_.Subject -like "CN=$FQDN"} | Sort-Object $_.NotAfter | Select-Object -Last 1

#if there are none AND an our.domain.com host, request one from the CA
if ($null -eq $Cert -and $domain -like "our.domain.com") { 
    Get-Certificate -Template 'WebServerCertificate' -DnsName $FQDN -SubjectName "CN = $FQDN" -CertStoreLocation cert:\LocalMachine\My
    $Cert = Get-ChildItem cert:\LocalMachine\My | Where-Object {$_.Subject -like "CN=$FQDN"} | Sort-Object $_.NotAfter | Select-Object -Last 1
}

#return the thumbprint for Deploy to IIS step
Set-OctopusVariable -name "CertificateThumbprint" -value $Cert.Thumbprint 

#output values
$FQDN
$Cert.Thumbprint   
$Cert.NotAfter

Instead of storing the cert in Octopus, I see if there is an appropriate one in the cert store on the deployment target, If so, I use the thumbprint in the ‘Deploy to IIS’ step. If NOT, I request one from our internal CA, and then use the thumbprint.

In the Deploy to IIS Step, I simply reference the thumbprint variable from this step:

#{Octopus.Action[ Manage Certificate].Output.CertificateThumbprint}

(obviously this requires you to have an internal CA capable of issuing out the certs)

1 Like