🧚‍♀️

ACMのリクエストとDNS検証をシェルで自動化するには途中でウェイトが必要

2022/07/17に公開

こんにちは。深緑です。
先日、AWSにてWEBサイトのアカウント発行を自動化したのですが、
httpsの部分・・・即ちACMのリクエストとDNS検証に非常に苦労したので共有させていただきます。

なお、本記事のスクリプトはこちらのサイトを参考にさせていただいています。
ACM での SSL 証明書発行を AWS CLI でスクリプト化する - michimani.net
こちらの記事でもACMのリクエスト後にウェイトを入れていますが、
私が組んでみたところACMのリクエスト時に別名ドメインを増やすとより多くのウェイトが必要かつバラツキが発生することがわかったので、それに対応するためアレンジを入れたのが本記事のスクリプトになります。
上記記事の執筆者の方に感謝いたします。

ACMのリクエストとDNS検証を行うシェル

最終的に出来上がったシェルはこの通りです。

create_acm.sh
#!/bin/bash

domain="admin.example.net"
alternative_names=("subdomain1.example.net" "subdomain2.example.net")
hosted_zone_id="Route53のホストゾーンID"

# ACMをリクエスト
acm_arn=$( \
aws acm request-certificate \
--region ap-northeast-1 \
--domain-name ${domain} \
--validation-method DNS \
--subject-alternative-names $(IFS=" "; echo "${alternative_names[*]}") \
--output text)

# ACMのDNS検証レコードを作成
len=$((${#alternative_names[@]} + 1)) # ドメイン+代替ドメインの数
for i in $( seq 0 $(($len - 1)) )
do
    # ACMの検証用レコードのNameとValueの取得
    # ACMのリクエスト直後は検証用レコードが取れないので何回かリトライする
    validation_record_name=""
    validation_record_value=""
    for j in $(seq 180)
    do
        sleep 3
        validation_record_name=$( \
            aws acm describe-certificate \
            --region ap-northeast-1 \
            --certificate-arn ${acm_arn} \
            --query "Certificate.DomainValidationOptions[${i}].ResourceRecord.Name" \
            --output text)

        # NameとValueがまだできてない場合「None」が返ってくるので、返却値が4文字を超えていることをループを抜ける条件にする
        if [ 4 -ge "${#validation_record_name}" ]; then
            continue
        fi

        validation_record_value=$( \
            aws acm describe-certificate \
            --region ap-northeast-1 \
            --certificate-arn ${acm_arn} \
            --query "Certificate.DomainValidationOptions[${i}].ResourceRecord.Value" \
            --output text)

        if [ 4 -lt "${#validation_record_name}" ] && [ 4 -lt "${#validation_record_value}" ]; then
            break
        fi   

    done

    if [ 4 -ge "${#validation_record_name}" ] || [ 4 -ge "${#validation_record_value}" ]; then
        echo "ACMの検証用レコードが取得できませんでした"
        exit 1
    fi      

    aws route53 change-resource-record-sets \
    --region ap-northeast-1 \
    --hosted-zone-id ${hosted_zone_id} \
    --change-batch \
    "{
        \"Comment\": \"ACMのDNS検証レコードを作成\",
        \"Changes\": [
            {
                \"Action\": \"UPSERT\",
                \"ResourceRecordSet\": {
                    \"Name\": \"${validation_record_name}\",
                    \"Type\": \"CNAME\",
                    \"TTL\": 300,
                    \"ResourceRecords\": [
                        {
                            \"Value\": \"${validation_record_value}\"
                        }
                    ]
                }
            }
        ]
    }"

done

exit 0

解説

本スクリプトは、ACMの検証も自動化するためにDNS検証を使用しています。
DNSにはRoute53を使用する想定で書いています。
DNS検証は、作成したACMをaws acm describe-certificateしてCertificate.DomainValidationOptions[].ResourceRecordのNameとValueを用いて、
Route53にCNAMEを作ることで実現できます。
ACMの画面における「Route53でレコードを作成」のボタンの動きです。

Certificate.DomainValidationOptions[].ResourceRecordの中身は、
aws acm request-certificateが正常終了しても一定時間(バラツキあり)は空っぽです。
従って、aws acm request-certificate の直後に aws acm describe-certificate してRoute53のCNAME作成しようとしても空振りします。
これを回避するためにループとsleepを用いてウェイトをかけている次第です。

Certificate.DomainValidationOptions[].ResourceRecord.Name
Certificate.DomainValidationOptions[].ResourceRecord .Value
は、まだできてない場合は「None」が返ってくるので、返却値が4文字を超えているかどうかをウェイトの終了条件としています。

if [ 4 -ge "${#validation_record_name}" ]; then
	continue
fi

この部分は「None」との文字列比較でもいいような気はしているのですが、
なんか空白値が返ってくることがあるんじゃないかと心配になり文字数の比較としました。
本来取れる値は4文字はあり得ないですからね。

参考にしたサイト

ACM での SSL 証明書発行を AWS CLI でスクリプト化する - michimani.net

本記事がお役に立てれば幸いです。

Discussion