📝

CloudFront の設定をエクスポートしてほぼ同じ設定で復元してみた

に公開

CloudFront の設定をエクスポートしてコピー(ほぼ同じ設定で復元)してみた記録です。

後述するやり方で調整すれば CLI から同じようなディストリビューションが新規に作成できます。

検証する際はテスト環境で行うことをお勧めいたします。

前提

AWS CLI の設定や CloudFront の設定は完了している前提でこの記事の内容はコンパクトにまとめています。

状況

以前コンソールから設定した CloudFront のディストリビューションがありました。
コンソールから設定しているので特にコード化されていない状態でした。

AWS CLI でバックアップ

AWS CLI を使用すると JSON 形式で設定が吐き出せるというのでやってみました。

awscli

aws cloudfront get-distribution-config --id <ExistingDistID> > dist-config.json

実行すると以下のような JSON がレスポンスで返ってきます。

cloudfront-setting

{
  "ETag": "E2QWRUHEXAMPLE",
  "DistributionConfig": {
    "CallerReference": "cli‑example",
    "Aliases": {
      "Quantity": 1,
      "Items": [
        "api.example.com"
      ]
    },
    "DefaultRootObject": "index.html",
    "Origins": {
      "Quantity": 1,
      "Items": [
        {
          "Id": "myS3Origin",
          "DomainName": "my-bucket.s3.amazonaws.com",
          "OriginPath": "",
          "CustomHeaders": {
            "Quantity": 0
          },
          "S3OriginConfig": {
            "OriginAccessIdentity": "origin-access-identity/cloudfront/E74FTE3AEXAMPLE"
          }
        }
      ]
    },
    "DefaultCacheBehavior": {
      "TargetOriginId": "myS3Origin",
      "ViewerProtocolPolicy": "redirect-to-https",
      "AllowedMethods": {
        "Quantity": 2,
        "Items": [
          "GET",
          "HEAD"
        ],
        "CachedMethods": {
          "Quantity": 2,
          "Items": [
            "GET",
            "HEAD"
          ]
        }
      },
      "ForwardedValues": {
        "QueryString": false,
        "Cookies": {
          "Forward": "none"
        },
        "Headers": {
          "Quantity": 0
        },
        "QueryStringCacheKeys": {
          "Quantity": 0
        }
      },
      "MinTTL": 0,
      "DefaultTTL": 86400,
      "MaxTTL": 31536000
    },
    "CacheBehaviors": {
      "Quantity": 0,
      "Items": []
    },
    "Comment": "My CloudFront distribution",
    "Logging": {
      "Enabled": true,
      "IncludeCookies": false,
      "Bucket": "my-log-bucket.s3.amazonaws.com",
      "Prefix": "logs/"
    },
    "PriceClass": "PriceClass_All",
    "Enabled": true,
    "ViewerCertificate": {
      "CloudFrontDefaultCertificate": false,
      "ACMCertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/abcd‑efgh‑1234",
      "SSLSupportMethod": "sni-only",
      "MinimumProtocolVersion": "TLSv1.2_2019"
    },
    "Restrictions": {
      "GeoRestriction": {
        "RestrictionType": "none",
        "Quantity": 0,
        "Items": []
      }
    },
    "WebACLId": "arn:aws:wafv2:us-east-1:123456789012:global/webacl/MyWebACL/abcd1234",
    "HttpVersion": "http2",
    "IsIPV6Enabled": true
  }
}

以下主な項目です。コンソールの設定が JSON になっているので不明な項目はコンソールを確認してみると良いと思います。

項目 説明
ETag 現在の設定バージョンを示すタグ
DistributionConfig ディストリビューションの設定本体
CallerReference 一意性チェック用の文字列
Aliases CNAME(Alternate Domain Names)情報
Origins 起点オリジン(S3 や HTTP サーバーなど)情報
DefaultCacheBehavior デフォルトのキャッシュ動作、プロトコルポリシー、HTTP メソッド、TTL 設定など
CacheBehaviors パスパターンごとの追加キャッシュ動作(なければ空)
Logging アクセスログ設定(有効・ログ先バケット・プレフィックスなど)
ViewerCertificate SSL/TLS 設定(ACM 証明書 ARN、SNI 方法、最小プロトコルなど)
Restrictions ジオ制限設定
WebACLId このディストリビューションに紐付けられている WAF Web ACL の ARN
HttpVersion サポートする HTTP バージョン(例:http2)
IsIPV6Enabled IPv6 の有効/無効フラグ

似た環境を JSON から立ち上げる

前述の手順で取得した JSON からほぼ同じ環境を立ち上げてみたいと思います。ここで注意しなければならない点がいくつかあります。

代替ドメインは一意に設定が必須

以下のように CloudFront の代替ドメインは1意になっていなければなりません。つまり、稼働中のディストリビューションをコピーする場合などは注意が必要になります。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/CNAMEs.html?utm_source=chatgpt.com#alternate-domain-names-restrictions

JSON はそのまま使えない

エクスポートした JSON は create-distribution コマンドでそのまま使うことはできません。

似たディストリビューションを作成

まずはコマンドを確認してみます。


$ aws cloudfront create-distribution --distribution-config file://dist-config-modified.json

上記コマンドではエクスポートした json ファイルを指定しています。しかしこのままだと後述するエラーが出ますのでいくつか修正をしましょう。

ETag と DistributionConfig(キー部分)を削除

以下の部分を削除します。

export

{
  "ETag": "...",
  "DistributionConfig": { ...本体... }
}

この { ...本体... }部分を残し、最上位の階層とした JSON にすることでこのエラーは解消できます。

Adjustment

{ ...本体... }

CallerReference を変更する

JSON の中に CallerReference という項目があります。これはディストリビューションを1意に指定するものなのでここを独自の値に変更します。

値は日時スタンプなどを含めてわかりやすく、かつ機密情報は入れないように作成するのがよいとのことでした。以下のような値に変更します。
projA-oac-migration-2024-03-06T12-34-56Z-7d32f8


"CallerReference": "projA-oac-migration-2024-03-06T12-34-56Z-7d32f8"

代替ドメイン部分を空に設定

この Items に代替ドメインが格納されています。注意の部分でも書いたようにこの値は1意でなければならず今回は稼働中のディストリビューションがあるという状況を想定している為ここを空にして新しいものを立ち上げます。


"Aliases": { "Quantity": 1, "Items": ["xxxxxxxxx.com"] }

以下のように修正します。


"Aliases": { "Quantity": 0 }

ディストリビューションのステータスを無効にして立ち上げる

JSON の中に DistributionConfig.Enabled という項目があります。これがディストリビューションのステータスを決定している部分になります。この項目を false に設定します。

ここまで JSON を修正できたら前述のコマンドを実行できます。


$ aws cloudfront create-distribution --distribution-config file://dist-config-modified.json

コンソールから確認すると新しいディストリビューションが登録されていると思います。私のケースでは数分のダウンタイムを許容し、ここから既存のディストリビューション設定の代替ドメインを削除しディストリビューションを停止、新しいものに代替ドメインを設定してディストリビューションを起動、Route53 からのルーティングを新しいものに向けるという流れで移行しました。

エラー

代替ドメインについてのエラーには以下のようなものがあります。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/troubleshooting-distributions.html#troubleshoot-incorrectly-configured-DNS-record-error

以下は私が出会ったエラーです。

JSON をそのまま指定した

get-distribution-config でエクスポートした JSON をそのまま指定して create-distribution を実行したら以下のエラーになりました。

error

Parameter validation failed:
Missing required parameter in DistributionConfig: "CallerReference"
Missing required parameter in DistributionConfig: "Origins"
Missing required parameter in DistributionConfig: "DefaultCacheBehavior"
Missing required parameter in DistributionConfig: "Comment"
Missing required parameter in DistributionConfig: "Enabled"
Unknown parameter in DistributionConfig: "ETag", must be one of: CallerReference, Aliases, DefaultRootObject, Origins, OriginGroups, DefaultCacheBehavior, CacheBehaviors, CustomErrorResponses, Comment, Logging, PriceClass, Enabled, ViewerCertificate, Restrictions, WebACLId, HttpVersion, IsIPV6Enabled, ContinuousDeploymentPolicyId, Staging
Unknown parameter in DistributionConfig: "DistributionConfig", must be one of: CallerReference, Aliases, DefaultRootObject, Origins, OriginGroups, DefaultCacheBehavior, CacheBehaviors, CustomErrorResponses, Comment, Logging, PriceClass, Enabled, ViewerCertificate, Restrictions, WebACLId, HttpVersion, IsIPV6Enabled, ContinuousDeploymentPolicyId, Staging

エラーを読むとパラメータが足らないと言われていますが、実は余分な要素が含まれていることが原因です。解消方法としては以下の部分を削除します。

export

{
  "ETag": "...",
  "DistributionConfig": { ...本体... }
}

この { ...本体... }部分を残し、最上位の階層とした JSON にすることでこのエラーは解消できます。

fixed

{ ...本体... }

CallerReference がコピー元と同じため

JSON の中に CallerReference という項目があります。これはディストリビューションを1意に指定するものなのでここを独自の値に変更しないと以下のエラーがでます。

error

An error occurred (DistributionAlreadyExists) when calling the CreateDistribution operation: The caller reference that you are using to create a distribution is associated with another distribution. Already exists:

値は日時スタンプなどを含めてわかりやすく、かつ機密情報は入れないように作成するのがよいとのことでした。

fixed

"CallerReference": "projA-oac-migration-2024-03-06T12-34-56Z-7d32f8"

items パメータ指定方法間違い

error

An error occurred (InvalidArgument) when calling the CreateDistribution operation: The parameter CNAME contains one or more parameters that are too small.

これは JSON の指定を以下のように指定していた為でした。

mistake

"Aliases": { "Quantity": 1, "Items": [""] }

以下のように修正したら直りました。

fixed

"Aliases": { "Quantity": 0 }

稼働中の代替ドメインを移動する

CloudFront の代替ドメインは同じものを設定できない仕様になっています。

公式では以下のようなやり方で稼働中ディストリビューションの代替ドメインを移動する方法が用意されていました。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/alternate-domain-names-move.html

AWS アカウントが別でも代替ドメインを移動できるようです。

今回は同じ AWS アカウント間でしたので以下のコマンドで移動することができました。


aws cloudfront update-domain-association \
  --domain "www.example.com" \
  --target-resource DistributionId="$NEW_ID" \
  --if-match "$ETAG"

上記コマンドには ETAG という情報が必要なので以下のコマンドを実行して取得します。この ETAG はコンソールから確認できない値なので AWS CLI で取得するほかありません。

新しいディストリビューションの ID を<ExistingDistID>に当てはめて実行します。


aws cloudfront get-distribution-config --id <ExistingDistID> > dist-config.json

ダウンロードした JSON の先頭に ETAG があるのでメモして update-domain-association を実行します。

ここで usage のエラーがでたら AWS CLI のバージョンが古いので以下のページを参考にアップデートしてください。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html

コマンド実行が完了するまでには 1 分もかかりませんでした。最終変更も変化がなかったのでうまくいけばダウンタイムもかなり少なくすみそうです。(厳密に検証してはおりません)

update-domain-association の実行でエラー

update-domain-association の実行時に usage のエラーがでたら AWS CLI 自体のバージョンが古いかもしれません。
以下のページにしたがってアップデートすると解消できました。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html

あとがき

Terraform などの IaC 以前につくられたリソースはいずれかのタイミングで IaC 化するとその後の管理コストが劇的に減らせる実感がありました。とはいえ CLI で色々できるのは本当にすごいですね。クラウドの凄さも実感できる体験でした。

Discussion