Skip to content

Instantly share code, notes, and snippets.

@andrewodri
Last active January 8, 2024 08:00
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save andrewodri/1d3c25b01f2b7b307f4b7b538ef36fff to your computer and use it in GitHub Desktop.
Save andrewodri/1d3c25b01f2b7b307f4b7b538ef36fff to your computer and use it in GitHub Desktop.
Create and Validate an ACM Certificate

This script performs the following actions:

  1. Creates a TLS certificate in ACM
  2. Upserts a validation CNAME record in Route 53
  3. Waits for the validation CNAME record to complete/update
  4. Waits for the certificate to validate and issue
  5. Outputs a description of the certificate

This obviously assumes that your domain's DNS is hosted on Route 53. It also uses the AWS credentials and region for the environment it is executed in.

It has no dependencies on any non-standard libraries (like jq, awk, etc.); just bash and the AWS CLI.

#!/usr/bin/env bash
################################################################################
# USAGE:
# ./create-acm-certificate.sh [domain]
#
# DESCRIPTION:
# This utility create and validates a certificate in ACM, as well as the
# required DNS records for validation.
#
# The following requirements need to be met in order to use this utility:
# - The domain's DNS must be hosted by Route 53.
# - The account and region defined in `~/.aws/config` and defined in your
# `AWS_PROFILE` environment variable (or lack thereof) will be assumed.
################################################################################
################################################################################
# Requests a certificate based on the provided domain name from ACM.
################################################################################
ACM_CERTIFICATE_ARN=$(aws acm request-certificate \
--domain-name "$1" \
--subject-alternative-names "*.$1" \
--validation-method DNS \
--query CertificateArn \
--output text)
echo "[ACM] Certificate ARN: $ACM_CERTIFICATE_ARN"
################################################################################
# The following commands extract the name and value of the required CNAME record
# that needs to be created to confirm ownership of the domain the certificate
# will be associated with.
################################################################################
VALIDATION_NAME="$(aws acm describe-certificate \
--certificate-arn "$ACM_CERTIFICATE_ARN" \
--query "Certificate.DomainValidationOptions[?DomainName=='$1'].ResourceRecord.Name" \
--output text)"
VALIDATION_VALUE="$(aws acm describe-certificate \
--certificate-arn "$ACM_CERTIFICATE_ARN" \
--query "Certificate.DomainValidationOptions[?DomainName=='$1'].ResourceRecord.Value" \
--output text)"
echo "[ACM] Certificate validation record: $VALIDATION_NAME CNAME $VALIDATION_VALUE"
################################################################################
# Request the hosted zone from Route 53 that is associated with the domain that
# the validation CNAME record will be associated with.
################################################################################
R53_HOSTED_ZONE_ID="$(aws route53 list-hosted-zones-by-name \
--dns-name "$1" \
--query "HostedZones[?Name=='$1.'].Id" \
--output text)"
R53_HOSTED_ZONE=${R53_HOSTED_ZONE_ID##*/}
echo "[Route 53] Hosted Zone ID: $R53_HOSTED_ZONE"
################################################################################
# Create the change batch needed to upset the validation record, then run the
# command to apply the change batch.
################################################################################
R53_CHANGE_BATCH=$(cat <<EOM
{
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "$VALIDATION_NAME",
"Type": "CNAME",
"TTL": 300,
"ResourceRecords": [
{
"Value": "$VALIDATION_VALUE"
}
]
}
}
]
}
EOM
)
R53_CHANGE_BATCH_REQUEST_ID="$(aws route53 change-resource-record-sets \
--hosted-zone-id "$R53_HOSTED_ZONE" \
--change-batch "$R53_CHANGE_BATCH" \
--query "ChangeInfo.Id" \
--output text)"
################################################################################
# Wait 1) for the validation record to be created, and 2) for the certificate
# to validate the domain and issue the certificate.
################################################################################
echo "[Route 53] Waiting for validation records to be created..."
aws route53 wait resource-record-sets-changed --id "$R53_CHANGE_BATCH_REQUEST_ID"
echo "[ACM] Waiting for certificate to validate..."
aws acm wait certificate-validated --certificate-arn "$ACM_CERTIFICATE_ARN"
ACM_CERTIFICATE_STATUS="$(aws acm describe-certificate \
--certificate-arn "$ACM_CERTIFICATE_ARN"
--query "Certificate.Status"
--output text)"
ACM_CERTIFICATE="$(aws acm describe-certificate \
--certificate-arn "$ACM_CERTIFICATE_ARN"
--output json)"
################################################################################
# Output the certificate description from ACM, and highlight the status of the
# certificate.
################################################################################
if [ "$ACM_CERTIFICATE_STATUS" = "ISSUED" ]; then
GREP_GREEN="1;32"
echo "$ACM_CERTIFICATE" | GREP_COLOR="$GREP_GREEN" grep --color -E "\"Status\": \"${ACM_CERTIFICATE_STATUS}\"|$"
else
GREP_RED="1;31"
echo "$ACM_CERTIFICATE" | GREP_COLOR="$GREP_RED" grep --color -E "\"Status\": \"${ACM_CERTIFICATE_STATUS}\"|$"
fi
@luafanti
Copy link

luafanti commented Jan 8, 2024

Thx for the useful gist. I found one gap - missed backslashes

ACM_CERTIFICATE_STATUS="$(aws acm describe-certificate \
--certificate-arn "$ACM_CERTIFICATE_ARN" \
--query "Certificate.Status" \
--output text)"

ACM_CERTIFICATE="$(aws acm describe-certificate \
--certificate-arn "$ACM_CERTIFICATE_ARN" \
--output json)"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment