Skip to content
Luca Dell'Oca Principal Cloud Architect @Veeam
Virtual To The Core Virtual To The Core

Virtualization blog, the italian way.

  • Media
  • About me
Virtual To The Core
Virtual To The Core

Virtualization blog, the italian way.

Automate Let’s Encrypt certificate renewals with AWS Route53 DNS records

Luca Dell'Oca, January 29, 2019January 28, 2019

I wrote already two articles about this topic. I know that managing SSL certificates can be a cumbersome task, so any option to automate the process is a great addition to any IT administrator toolbox. This is why Let’s Encrypt certificates are becoming so popular, not just because they are free but also because the automated platform that they use allow for some amazing automation solutions. In my first article Use Let’s Encrypt free certificates in Windows for Veeam Cloud Connect I explained the basics of Let’s Encrypt technology, and how to use its certificates on a Windows machine using ACMEsharp libraries with Powershell. Then, in the second article Improved Powershell script for Let’s Encrypt certificate renewals I optimized the script even more. But still, there was room for improvement and even more automation.

 

The limits of my previous powershell process

The limit of my actual script is in the DNS handler, that is the part where we can receive a new TXT resource record that we need to update into our DNS zone. Right now, the script use the “manual” option: once we receive in the output the new dns txt record we have to pause the script and go to our DNS server to update the Resource Record. As you may have noticed if you have used the script already, the RR name is always the same, but it has each time a new value. It changes each time a new challenge is started. Once the DNS zone has been updated, the script can be completed, but this manual step to update the DNS record is extremely annoying.

Time to find a way to automate this part too!

 

ACMEsharp and Route53

I’ve wasted a considerable amount of time to try and use ACMEsharp’s native “Challenge Handlers”, that are the types of challenge you can execute to complete a ownership challenge. I found initially some handlers listed among the available ones related to AWS, and that seemed promising, but in the end I was not able to make them work. Bugs, issues, really poor documentation ruined any effort I put into it.

So, I decided to switch complitely my approach, and instead of trying to find some pre-cooked solution, I chose this time to use native AWS technologies to complete this new version of the script. In the process, I also learned more about Powershell, string processing, and AWS Route53 automation!

 

Create a dedicated IAM user to update the DNS record

One of the most common security practice is the so-called least privilege: a user or a function should not have any additonal permission than the minimum required to complete a task. In AWS, my new procedure needs to just update one single Resource Record of a specified DNS Zone. Using the root account in AWS to do so would be a complete failure in security. Thanks to AWS IAM (Identity and Access Management) we can instead create a set of credentials with just the minimum required permissions.

For our use case, we only need: ChangeResourceRecordSets

And we also want to limit this account to the domain we want to work with, that is virtualtothecore.com in my example. In AWS terms, we need the Amazon Resource Names (ARNs) to identify the hosted DNS zone. The format is this:

arn:aws:route53:::hostedzone/zoneid

There’s no way (as of now) to limit the account to a specific resource record, so we need to accept the fact that this account can possibly change (and then damage) every record in the zone. There have been several requests to implement this feature (see here for example) but so far this is the highest granularity we can have.

So, the final rule in JSON format for our user is:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "route53:ChangeResourceRecordSets",
            "Resource": "arn:aws:route53:::hostedzone/ZQUSU6S6339VA"
        }
    ]
}

 

Automating the DNS record updates

As I said before, this time I decided to use native AWS tools instead of incomplete github projects. AWS has, as you can imagine, many tools to automate the interaction with their resources, and one is their Powershell modules. So, the first part of my new script goes and installs both ACMESharp and AWS. You can also note I found and adapted a nice function that checks if a module is already locally available, and in case it’s not it downloads and installs it:

 function Load-Module ($m) {

    # If module is imported say that and do nothing
    if (Get-Module | Where-Object {$_.Name -eq $m}) {
        write-host "Module $m is already imported."
    }
    else {
        # If module is not imported, but available on disk then import
        if (Get-Module -ListAvailable | Where-Object {$_.Name -eq $m}) {
            Import-Module $m -Verbose
        }
        else {
            # If module is not imported, not available on disk, but is in online gallery then install and import
            if (Find-Module -Name $m | Where-Object {$_.Name -eq $m}) {
                Install-Module -Name $m -Force -Verbose -Scope CurrentUser
                Import-Module $m -Verbose
            }
            else {
                # If module is not imported, not available and not in online gallery then abort
                write-host "Module $m not imported, not available and not in online gallery, exiting."
                EXIT 1
            }
        }
    }
}

Load-Module "ACMESharp"
Load-Module "AWSPowerShell"

 

I’ve also added some new checks for the Vault folder and its initialization, but those are not the topic of this post. You can see those sections in the complete script. The line where I injected the new code is after this one:

(Update-ACMEIdentifier $alias -ChallengeType dns-01).Challenges | Where-Object {$_.Type -eq "dns-01"} > challenge.txt

Before, I used some complex WinForms code to show the Challenge sent by Let’s Encrypt to the user. This time, I’m parsing the text file to extract the string I need:

 # Get the new TXT record from Let's Encrypt

$RRtext = Select-String challenge.txt -Pattern "RR Value" -CaseSensitive | Out-String -Stream
$separator = "["

$RRtext = $RRtext.split($separator)
$RRtext = $RRtext[2]
$RRtext = $RRtext.trimend("]")
$RRtext = """$RRtext"""

 

The text from Let’s Encrypt is always the same, and we need the string with the value of the resource record. This is clearly identified with the “RR Value” text in it. Then, with some string operations the code trims the un-needed text before and after, and adds the quotes around the text. The final result is our new TXT record in proper DNS format:

"j23gJf_rJzsmKxcVQ-UOhezeT96dzvXLA3UiBxSN5Kw"

Then, we need to update the record. for this operation, we first need to configure the credentials that will be used in the script to connect to AWS. In order to make the script more secure and to do not share the credentials, those are not stored in the script. Instead we can use this code to store them in a profile:

$access = "THISISNOTMYREALACCESSOKEY" #Access key ID
$secret = "THISISNOTMYREALSECRETKEY" #Secret access key
Set-AWSCredential -AccessKey $access -SecretKey $secret -StoreAs awsroute53

The credentials are store in a profile, and I just need to load it each time I need to use it:

Set-AWSCredential -ProfileName awsroute53

 

Time to update the TXT record:

$change = New-Object Amazon.Route53.Model.Change

$change.Action = "UPSERT"
$change.ResourceRecordSet = New-Object Amazon.Route53.Model.ResourceRecordSet
$change.ResourceRecordSet.Name = "_acme-challenge.cc.virtualtothecore.com."
$change.ResourceRecordSet.Type = "TXT"
$change.ResourceRecordSet.TTL = 300
$change.ResourceRecordSet.ResourceRecords = (New-Object Amazon.Route53.Model.ResourceRecord($RRtext))

$params = @{
    HostedZoneId="ROUTE53ZONEID"
      ChangeBatch_Comment="Updated TXT record for cc.virtualtothecore.com. with new Let'sEncrypt challenge"
      ChangeBatch_Change=$change
}

Edit-R53ResourceRecordSet @params

 

The remaining part of the script goes on as described in the previous posts, by completing the challenge, retrieving the certificate and installing it where it’s needed.

I also realized that I only posted the script here, and I’ve never published it via GitHub. So I took the change to fix also this and you can now read the code directly here:

https://github.com/dellock6/veeam-powershell/blob/master/letsencrypt-cloudconnect-aws.ps1


https://github.com/dellock6/veeam-powershell
4 forks.
11 stars.
0 open issues.

Recent commits:
  • Initial commit of the new json fileThe json file lists the minimum permissions required to use Veeam Direct Restore to AWS EC2, Luca Dell'Oca
  • V.3 with fully automated AWS Route53 integration, Luca Dell'Oca
  • V.2 for manual DNS challenges, Luca Dell'Oca
  • Updated readme and content, Luca Dell'Oca
  • Changed license to MIT license instead of GPL v3, Luca Dell'Oca

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to email a link to a friend (Opens in new window) Email
  • Click to share on Tumblr (Opens in new window) Tumblr
  • Click to share on Pinterest (Opens in new window) Pinterest
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to share on WhatsApp (Opens in new window) WhatsApp
  • Click to share on Pocket (Opens in new window) Pocket
Tech acmesharpautomationawscertificateschallengecloud connectletsencryptpowershellrecordsroute53TXTveeam

Post navigation

Previous post
Next post

Search

Sponsors

Latest Posts

  • Migrate WSL (Windows Subsystem for Linux) to a new computer
  • Pass keystrokes to a pfSense virtual machine to install it automatically
  • Automatically deploy pfSense with Terraform and Ansible
  • My Automated Lab project: #6 Create a S3 Bucket with Terraform
  • My Automated Lab project: #5 Deploy a Linux vSphere VM with Terraform and custom disks
©2025 Virtual To The Core | WordPress Theme by SuperbThemes
We use cookies to ensure that we give you the best experience on our website, and to collect anonymous data regarding navigations stats using 3rd party plugins; they all adhere to the EU Privacy Laws. If you continue to use this site we will assume that you are ok with it.OkNoPrivacy Policy