Automating ACM Certificate Validation: A Python Script That Clicks the AWS Console Button For You
Hook
Every developer who's provisioned an ACM certificate knows the drill: request certificate, copy CNAME name, copy CNAME value, switch to Route 53, find hosted zone, create record, paste values, wait. What if those eight steps were just one command?
Context
AWS Certificate Manager (ACM) makes SSL/TLS certificates free and auto-renewing, but there's a mandatory validation step that proves you control the domain. DNS validation is the preferred method—you create a specific CNAME record, AWS checks it exists, and your certificate is issued. The AWS Console has a convenient 'Create record in Route 53' button that does this automatically, but automation workflows, CI/CD pipelines, and infrastructure-as-code scenarios can't click buttons.
This is where create-and-validate-acm-certificate enters. It's a focused Python utility that orchestrates the dance between ACM certificate requests and Route 53 DNS record creation. Originally built by Dylan Burger for a specific use case, it does exactly what the Console button does—but scriptably. The tool requests an ACM certificate, extracts the validation CNAME records from the pending certificate, identifies the correct Route 53 hosted zone, creates the validation records, and optionally polls until AWS validates the certificate. It's 300 lines of Python that eliminate manual context-switching and copy-paste errors.
Technical Insight
Under the hood, create-and-validate-acm-certificate is a boto3 orchestration script with a clever design choice: it defaults to us-east-1. This isn't arbitrary—CloudFront distributions can only use ACM certificates from us-east-1, a quirk that catches developers off guard. The tool saves you from creating certificates in the wrong region.
The core workflow involves three AWS service calls. First, it requests a certificate with DNS validation:
import boto3
acm = boto3.client('acm', region_name='us-east-1')
response = acm.request_certificate(
DomainName='example.com',
SubjectAlternativeNames=['*.example.com'],
ValidationMethod='DNS'
)
certificate_arn = response['CertificateArn']
Second, it polls the certificate details to extract validation records. ACM doesn't immediately populate these, so the script waits:
import time
while True:
cert_details = acm.describe_certificate(CertificateArn=certificate_arn)
validation_options = cert_details['Certificate'].get('DomainValidationOptions', [])
if all('ResourceRecord' in option for option in validation_options):
break
time.sleep(2)
for option in validation_options:
record = option['ResourceRecord']
cname_name = record['Name']
cname_value = record['Value']
Third, it determines the Route 53 hosted zone using tldextract to parse domain components, then creates the CNAME record:
import tldextract
route53 = boto3.client('route53')
extracted = tldextract.extract('example.com')
base_domain = f"{extracted.domain}.{extracted.suffix}"
# Find hosted zone ID for base domain
zones = route53.list_hosted_zones_by_name(DNSName=base_domain)
hosted_zone_id = zones['HostedZones'][0]['Id']
# Create validation record
route53.change_resource_record_sets(
HostedZoneId=hosted_zone_id,
ChangeBatch={
'Changes': [{
'Action': 'UPSERT',
'ResourceRecordSet': {
'Name': cname_name,
'Type': 'CNAME',
'TTL': 300,
'ResourceRecords': [{'Value': cname_value}]
}
}]
}
)
The UPSERT action is particularly clever—it creates the record if it doesn't exist or updates it if it does, making the script idempotent for most scenarios.
The optional polling mechanism waits for certificate validation with exponential backoff. ACM validation typically takes 5-30 minutes depending on DNS propagation:
max_wait_seconds = 900 # 15 minutes
start_time = time.time()
while time.time() - start_time < max_wait_seconds:
cert = acm.describe_certificate(CertificateArn=certificate_arn)
status = cert['Certificate']['Status']
if status == 'ISSUED':
print(f"Certificate validated: {certificate_arn}")
break
elif status == 'FAILED':
raise Exception("Certificate validation failed")
time.sleep(30)
The architecture is deliberately simple—no classes, no complex state management, just sequential AWS API calls. This makes the code easy to audit and modify. You can use it as a CLI tool, import functions into your own Python scripts, or extract the logic into Lambda functions for event-driven certificate provisioning. The tldextract dependency handles edge cases like co.uk domains that would trip up naive string splitting.
Gotcha
The tool makes two significant assumptions that limit its applicability. First, it assumes the Route 53 hosted zone already exists for your domain. If you're requesting a certificate for newdomain.com but haven't created the hosted zone yet, the script fails with a cryptic boto3 error about missing zones. There's no zone creation logic, no helpful error message suggesting you create one first, and no handling of scenarios where the hosted zone lives in a different AWS account.
Second, as Dylan notes in the README, this was 'created for a single use case' and lacks production-grade error handling. If Route 53 record creation fails partway through a multi-domain certificate (say, you're validating example.com and api.example.com), there's no automatic rollback. The certificate request sits in pending validation with some records created and others missing. You'll need to manually clean up Route 53 or re-run the script. Cross-account validation scenarios—where your ACM certificate is in one account but Route 53 hosted zone is in another—aren't supported. The script also doesn't handle private certificates, certificate renewal workflows, or certificate deletion. It's truly single-purpose: create certificate, validate via Route 53, done.
Verdict
Use if: You manage both ACM certificates and Route 53 DNS in the same AWS account and need scriptable certificate provisioning for CI/CD pipelines or automation workflows. It's especially valuable for CloudFront deployments where us-east-1 certificates are mandatory, or when you're creating multiple certificates and want to avoid the AWS Console's repetitive clicking. The code is simple enough to audit in 10 minutes and modify for your specific needs. Skip if: You need cross-account certificate validation, prefer declarative infrastructure tools like Terraform or CloudFormation that manage certificate and DNS records with proper state tracking and dependency graphs, or require production-grade error handling with rollback capabilities. For one-off certificate creation, the AWS Console button is genuinely simpler. If you're provisioning infrastructure outside Route 53 or need Let's Encrypt certificates instead of ACM, look at certbot with DNS plugins or dedicated IaC modules.