If you are not taking advantage of Automatic Certificate Management, you are officially a dinosaur, desperately clinging to the good old days, when certificate issuance required blood tests, DNA samples, and voice identification.

If you have tried getting a certificate from Digicert recently, you've noticed they now insist on speaking to the organization owning the domain name, through publicly verifiable contact information. Try doing that for a lab network! Thankfully, using the ACME tools now offered by Lets Encrypt and other CAs, you can bypass all these shenanigans and get your certificates with minimal fuss and without the burdensome human interaction we IT professionals despise so much.

Another benefit of using the ACME tools is that for those junior techs who struggle with the process of getting certificates issued and installed, you can rely on automation to do the job even when certificates expire and you are on a beach somewhere.

Most of the organizations I manage are small; only one or two use Remote Desktop Services. This means for our small band of indomitable IT engineers, there is a mad scramble once or twice a year, usually while I am on vacation, to reissue an expired certificate for a Remote Desktop server that has been forgotten, with a sincere promise that next year will be different.

To that end, having previously written and implemented automatic certificate renewal for our Fortinet routers, the few RDS Servers we manage were next on the list.

Part of the Posh-ACME PowerShell module allows you to use automated DNS challenges to verify domain ownership. Having to manually complete the challenge would negate the automated nature of an automated process. To complete these challenges automatically, your domain must be hosted with a service that has API-based access to create and manage DNS records.

Amazon's Route 53 is one such service; however, I have been reluctant to move all our client domains to this service, as it is frankly a lot of work. However, if we simply delegate a portion of the domain name space to Route 53, we can take advantage of the API and keep the domain where it was originally.

To do so, have a look at my post here. Just to recap, you add a new zone to Route53, and then add the DNS records for that zone to your original zone. Finally, add a CNAME for your DNS Challenge.

The result is that any Posh-ACME request for a certificate for that FQDN is now automatically processed and issued. You can then use the normal RDS PowerShell tools to install your new certificate. Wrap this all up in a PowerShell script and a scheduled task, and you no longer get calls about expired certificates for your RDS Deployments.

Non expiring certificate

Non expiring certificate

My full script is below. But first, here's an interesting point to keep in mind. One of our clients is running a Windows Server 2012 as their RDS host. Yes, it is old now, but it is not due for renewal. In any event, the real issue is that when you install PowerShell 5.1 (or WMF 5.1) on Server 2012, a new 'feature' is enabled which breaks many of the PowerShell RDS cmdlets. However, if you enter a remote PowerShell session to the same server, from a newer server on the same network, the RDS cmdlets run perfectly well.

Expiring certificate

Expiring certificate

The script uses the Posh-ACME and AWSPowerShell modules. PowerShell remoting must be enabled on your RDS Server. The script also makes use of SMTP for notifications. If you require SMTP authentication or non-standard ports, you will need to make changes to accommodate them.

Subscribe to 4sysops newsletter!

<#
.Synopsis
   Script to Automated Certificate Renewal for Remote Desktop Server deployment.
.DESCRIPTION
   Script to Automated Certificate Renewal for Remote Desktop Server deployment.
   Using Lets Encrypt (Posh-ACME, AWSPowerShell) we can automate the issuance of certificates for our Remote Desktop deployments, to save admin time.
.EXAMPLE
  .\LE-RDP.ps1 -rdsServer RDS-Server.internal.domain 
               -LEServer le_stage 
               -domain public.domain.com 
               -challengeDomain challenge.acme.domain.com 
               -contact admin@domain.com 
               -smtpServer smtp.domain.com
               -from "RDS Server <server@domain.com>"
               -r53secret (Get-Content savedkey.txt)
               -r53key awsIAMKey

  
This example requests a certificate for public.domain.com from the Lets Encrypt staging CA, using the challenge domain challenge.acme.domain.com. It uses the SMTP server smtp.domain.com for notifications and the Route 53 IAM Credentials r53secret and awsIAMkey. The r53secret is your IAM Secret Access Key. It has been saved as a secure string in the savedkey.txt file and is converted back to a secure string. The r53key is your IAM access key.

#>

param(
    [string]$rdsServer,
    [string]$LEServer,
    [string]$domain,
    [string]$challengeDomain,
    [string]$contact,
    [string]$smtpServer,
    [string]$from,
    [string]$r53Secret,
    [string]$r53Key
)
$session = New-PSSession $rdsServer
$cdomain = "cn",$domain -join "="
$date = [datetime]::Now
$expires = $date.addHours(48)
$cert = Invoke-Command -Session $session -ScriptBlock {$cdomain = $args[0];get-childitem cert:\localmachine\my | where { $_.Subject -eq $cdomain} } -ArgumentList $cdomain
$thumbprint = $cert.Thumbprint
if(($cert.NotAfter) -le $expires)
{
    # If Certificate due to expire in 48 hours, request new certificate and install to RDS.
    Write-Output "Certificate Requires Replacement"
    # Get the Certificate
    Set-PAServer $LESERVER
    if(($LESERVER) -eq "LE_STAGE")
    {
        $certName = "ACME-STAGE"
    }
    if(($LESERVER) -eq "LE_PROD")
    {
        $certName = "ACME-PROD"
    }
    $SecurePassword = $r53Secret | ConvertTo-SecureString
    $r53Params = @{R53AccessKey=$r53Key; R53SecretKey=$SecurePassword}
    try{
        $certificate = New-PACertificate $domain -AcceptTOS -Contact $contact -DnsPlugin Route53 -DnsAlias $challengeDomain -PluginArgs $r53Params -Verbose -force -ErrorAction Stop
        Start-Sleep 60 # Pause for 1 minute
        $password = "poshacme"
        $pwd = ConvertTo-SecureString -String $password -AsPlainText -Force
        $roles = @(
        "RDRedirector",
        "RDPublishing",
        "RDWebAccess",
        "RDGateway"
        )
        Write-Output "Add Certificate to RDS"
        # Install Certificate
        foreach ($role in $roles)
        {
            Set-RDCertificate -role $role -importPath $certificate.pfxFile -password $pwd -ConnectionBroker $rdsServer -Force
        }
        Write-Output "Remove old certificate"
        Invoke-Command -Session $session -ScriptBlock {$thumbprint = $args[0]; $oldCert = Get-Childitem Cert:\LocalMachine\my | where {$_.Thumbprint -eq $thumbprint} | Remove-Item -Force -Confirm:$false} -ArgumentList $thumbprint
        $subject = "Certificate Renewal - $domain"
        $body = "Certificate renewed and installed - $domain"
    }
    catch{
        $_.exception.message
        $subject = "Error - Certificate Renewal $domain"
        $body = "Review Certificate Renewal for $domain"
    }
    Send-MailMessage -SmtpServer $smtpServer -from $from -to $contact -Subject $subject -body $body
}
else
{
    Write-Output "Certificate does not need replacing"
    $expiry = $cert.NotAfter
    Write-Output "Expires : $expiry"
}
Get-PsSession | Remove-PSSession
avatar
0 Comments

Leave a reply

Please enclose code in pre tags

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2021

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account