Skip to content

Instantly share code, notes, and snippets.

@sbingner
Created May 4, 2019 09:16
Show Gist options
  • Save sbingner/49f0e0bd03edb588873a724e60c005a7 to your computer and use it in GitHub Desktop.
Save sbingner/49f0e0bd03edb588873a724e60c005a7 to your computer and use it in GitHub Desktop.
letsEncrypt cert updates for Exchange
import-module ACMESharp
#
# Script parameters
#
$domain = "mail.yourdomain.com"
$sans = @("autodiscover.yourdomain.com")
$exchangeServers = @("EX01", "EX02", "EX03");
$iissitename = "Default Web Site"
$certname = "mail.yourdomain.com-$(get-date -format yyyy-MM-dd--HH-mm)"
#
# Environmental variables
#
$PSEmailServer = "127.0.0.1"
$LocalEmailAddress = "administrator@yourdomain.com"
$OwnerEmailAddress = "you@yourdomain.com"
$pfxfile = "c:\Certificates\$certname.pfx"
#
# Script setup - should be no need to change things below this point
#
$ErrorActionPreference = "Stop"
$EmailLog = @()
#
# Utility functions
#
function Write-Log {
$msg = "$(get-date -Format HH:mm:ss) $($args[0])"
Write-Host $msg
$script:EmailLog += $msg
}
function Register-FQDN
{
$domain = $args[0]
$certname = $args[1]
Write-Log "Generating a new identifier for $domain"
New-ACMEIdentifier -Dns $domain -Alias $certname
Write-Log "Completing a challenge via http"
Complete-ACMEChallenge $certname -ChallengeType http-01 -Handler iis -HandlerParameters @{ WebSiteRef = $iissitename }
Write-Log "Submitting the challenge"
Submit-ACMEChallenge $certname -ChallengeType http-01
# Check the status of the identifier every 6 seconds until we have an answer; fail after one minute
$i = 0
do {
$identinfo = (Update-ACMEIdentifier $certname -ChallengeType http-01).Challenges | Where-Object {$_.Status -eq "valid"}
if($identinfo -eq $null) {
Start-Sleep 6
$i++
}
} until($identinfo -ne $null -or $i -gt 10)
if($identinfo -eq $null) {
Write-Log "We did not receive a completed identifier after 60 seconds"
$Body = $EmailLog | out-string
Send-MailMessage -From $LocalEmailAddress -To $OwnerEmailAddress -Subject "Attempting to renew Let's Encrypt certificate for $domain" -Body $Body
Exit
}
}
Try {
if (1) {
Register-FQDN $domain $certname
ForEach ($san in $sans) {
Register-FQDN $san "$san.$certname"
}
# We now have a new identifier... so, let's create a certificate
Write-Log "Attempting to renew Let's Encrypt certificate for $domain"
# Generate a certificate
Write-Log "Generating certificate for $domain"
New-ACMECertificate $certname -AlternativeIdentifierRefs $sans -Generate -Alias $certname
# Submit the certificate
Submit-ACMECertificate $certname
# Check the status of the certificate every 6 seconds until we have an answer; fail after a minute
$i = 0
do {
$certinfo = Update-AcmeCertificate $certname
if($certinfo.SerialNumber -ne "") {
Start-Sleep 6
$i++
}
} until($certinfo.SerialNumber -ne "" -or $i -gt 10)
if($i -gt 10) {
Write-Log "We did not receive a completed certificate after 60 seconds"
$Body = $EmailLog | out-string
Send-MailMessage -From $LocalEmailAddress -To $OwnerEmailAddress -Subject "Attempting to renew Let's Encrypt certificate for $domain" -Body $Body
Exit
}
# Export Certificate to PFX file
Get-ACMECertificate $certname -ExportPkcs12 $pfxfile
}
$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$pfx.Import($pfxfile)
$certThumbprint = $pfx.Thumbprint
$exchver = gcm exsetup | %{$_.fileversioninfo.ProductVersion.Split("{.}")}
switch ($exchver[0])
{
8 {$exchsnapin = "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin;"}
14 {$exchsnapin = "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010;"}
15 {$exchsnapin = "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn;"}
default {$exchsnapin = "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010;"}
}
Invoke-Expression $exchsnapin
foreach ($server in $exchangeServers) {
$ps = New-PSSession -ComputerName $server
Write-Log "Updating certs for $server"
Import-ExchangeCertificate -Server $server -FileName $pfxfile -FriendlyName $certname | Enable-ExchangeCertificate -Server $server -DoNotRequireSsl -Services "IIS,IMAP,SMTP,POP" -Force
Write-Log "Remove old certificates"
$certs = Invoke-Command -Session $ps { Get-ChildItem -Path Cert:\LocalMachine\My\ -DNSName $using:domain }
foreach ($oldcert in $certs) {
Write-Log "Testing Cert: $($oldcert.Thumbprint)"
if ($oldcert.Subject -eq "CN=$domain" -And $oldcert.Issuer.Contains("Let's Encrypt") -And $oldcert.Thumbprint -ne $certThumbprint) {
Write-Log "Removing Cert $($oldcert.FriendlyName) ($($oldcert.Thumbprint))"
Remove-ExchangeCertificate -Thumbprint $oldcert.Thumbprint -Server $server -Confirm:$false
}
}
iisreset $server
}
# Finished
Write-Log "Finished"
$Body = $EmailLog | out-string
Send-MailMessage -From $LocalEmailAddress -To $OwnerEmailAddress -Subject "Let's Encrypt certificate renewed for $domain" -Body $Body
} Catch {
Write-Host $_.Exception
$ErrorMessage = $_.Exception | format-list -force | out-string
$EmailLog += "Let's Encrypt certificate renewal for $domain failed with exception`n$ErrorMessage`r`n`r`n"
$Body = $EmailLog | Out-String
Send-MailMessage -From $LocalEmailAddress -To $OwnerEmailAddress -Subject "Let's Encrypt certificate renewal for $domain failed with exception" -Body $Body
Exit
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment