Exchange ACME PowerShell Script

This is the PowerShell script I use to automatically update a Let’s Encrypt SSL Certificate on Exchange 2016 running on Windows Server 2016 using Posh-ACME.  Let’s Encrypt certificates are valid for 3 months, but I set the script to run once a month, so that if there is some type of temporary problem it gets two more tries before the expiration. Note that his example script modifies DNS hosted by GoDaddy to verify ownership of the domain. If you host your DNS at GoDaddy you will need to include your `GDKey` and `GDSecret` and otherwise adjust the script to your environemnt. If your DNS is hosted elsewhere or if you would like to use HTTP verification you will need to modify the script according to the instructions for the Posh-ACME plugins.

# Use TLS 1.2.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

# Install the Posh-ACME module. Typically the module only needs to be installed once on a server.
Install-Module -Name Posh-ACME

# Update the Posh-ACME module. The install command above will not update a currently installed module.
Update-Module -Name Posh-ACME

# Use the production server.
Set-PAServer LE_PROD

# Define the variables.
$contact = 'user@example.com'
$pluginArguments =@{GDKey='xxxxxxxxxxxxxxxx';GDSecret='xxxxxxxxxxxxxxxx'}
$pfxPassword = 'SuperSecretPassword'
$certificatePath = 'C:\Users\administrator\AppData\Local\Posh-ACME\acme-v02.api.letsencrypt.org\xxxxxxxxx\mail.example.com\cert.pfx'
$certFriendlyName = "mail.example.com_$($(get-date -format yyyy-MM-dd--HH-mm))"

# Generate the certificate.
New-PACertificate 'mail.example.com','autodiscover.example.com','mail.domain.com','autodiscover.domain.com' -AcceptTOS -Contact $contact -DnsPlugin GoDaddy,GoDaddy,GoDaddy,GoDaddy -PluginArgs $pluginArguments -DnsAlias 'mail.example.acme.example.com','autodiscover.example.acme.example.com','mail.domain.acme.example.com','autodiscover.domain.acme.example.com' -PfxPass $pfxPassword -force

# Invoke the Exchange Management PowerShell snapin.
Invoke-Expression "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn;"

# Import the certificate into Exchange and set it to run all the necessary services.
Import-ExchangeCertificate -FileName $certificatePath -FriendlyName $certFriendlyName -Password (ConvertTo-SecureString -String $pfxPassword -AsPlainText -Force) | Enable-ExchangeCertificate -Services POP,IMAP,SMTP,IIS -Force

# Restart IIS.
iisreset

# Disable "Require SSL" for the Default Web Site.  This allows IIS to redirect HTTP requests to HTTPS if configured (optional).
Set-WebConfiguration -Location "Default Web Site" -Filter 'system.webserver/security/access' -Value None

3 comments

  1. I received the following email:

    Thanks for posting the script for posh-acme on exchange, it works great! I made a slightly modified version I wanted to share. Cool website and thanks
    again

    #Set-PAServer LE_STAGE # testing, dry run.
    Set-PAServer LE_PROD # production use

    # Domain
    Set-PAOrder example.com

    # Check renew.
    $cert = Submit-Renewal

    # Do something.
    if ($cert) {

    $certFriendlyName = “example.com_$($(get-date -format yyyy-MM-dd–HH-mm))”
    $certificatePath = “C:\certs\example.com\$certFriendlyName.pfx”
    $pfxPassword = ‘pfxpass’

    # COPY CURRENT CERT TO PATH. %appdata%\Posh-ACME\
    $cert = Get-PACertificate Copy-Item -Path $cert.PfxFile -Destination $certificatePath

    # Invoke the Exchange Management PowerShell snapin.
    Invoke-Expression “Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn;”

    # Import the certificate into Exchange and set it to run all the necessary services.
    Import-ExchangeCertificate -FileName $certificatePath -FriendlyName $certFriendlyName -Password (ConvertTo-SecureString -String $pfxPassword -AsPlainText -Force) | Enable-ExchangeCertificate -Services POP,IMAP,SMTP,IIS -Force

    # Restart IIS
    iisreset

    # Disable “Require SSL” for the Default Web Site. This allows IIS to redirect HTTP requests to HTTPS if configured (optional).
    Set-WebConfiguration -Location “Default Web Site” -Filter ‘system.webserver/security/access’ -Value None

    #Send email
    Send-MailMessage -From ‘acme@example.com’ -To ‘admin@example.com’ -Subject ‘CERT REWNEW: example.com’ -Body “Renewed Cert: $certFriendlyName” -SmtpServer 127.0.0.1

    }

    exit

  2. Wanted to say that it’s not good security practice to have the password for the pfx file in clear text in your script. You should at least use some encryption to secure it.

    1. 1 – Do you know of any way to do this that is compatible with Posh-ACME?

      2 – If you follow the logic of the script, the entire purpose of the password is just to be compatible with the script, which wants to create a password protected PFX file. We don’t care at all about the PFX file, we just want the certificate imported into IIS. So, we just need to create a dummy password on the PFX file and then use it again to import the certificate into IIS.

      3 – If it makes you feel better, you can modify the script to delete the PFX file after it is imported. However, this is simply security theatre (of which I am not a fan), because anyone who has access to read the PFX file and the Posh-ACME file on the server also has sufficient permissions to export the certificate and key out of IIS. They also have sufficient privileges to just modify the Posh-ACME script and run it again to generate a new PFX file with a password they know.

      3 – As an extension of point 2, this is how Certbot works on Linux, which is that it doesn’t produce encrypted output, but rather produces unencrypted certificates and keys that are stored in a directory that only the correct users have access to. Anyone with access to that directory could also run Certbot again with different options, which would negate any purpose of encrypting the output.

Leave a Reply to Soren Stoutner Cancel reply

Your email address will not be published.