AWS CDK(cdk-remote-stack)でACMとCloudFrontのクロスリージョン参照を実装する
2023/10/05追記 AWS CDK公式の機能でクロスリージョン参照できるようになったので、こちらのブログも参考にしてください。 AWS CDK公式の機能でACMとCloudFrontのクロスリージョン参照を実装する | DevelopersIO
AWS CDKでクロスリージョン参照できなくて、悔しい思いをしたことはありませんか? 私はよくあります。
そんなときはcdk-remote-stackパッケージを利用すると解決できます! 詳しくはこちらの弊社ブログをご覧ください。
今回、次の図のようなCloudFront+S3の環境を東京(ap-northeast-1)リージョンで構築したかったのですが、 これにカスタムドメインをつけようと思うと、CloudFrontの仕様上バージニア(us-east-1)リージョンでACMを構築する必要があります。
通常、AWS CDKではクロスリージョン参照ができないため、 東京リージョンで作成するCloudFrontからバージニアリージョンのACMを参照しようとすると、 ARNをベタ書きするとか手作業でパラメータストアに保存して参照する必要がありました。
今回、cdk-remote-stackパッケージを使ってクロスリージョン参照して構築する機会があったのでその手順を共有します。
サンプルコード
今回cdkで構築する環境のサンプルコードは以下に保存しています。細部が気になる方はこちらを御覧ください。
前提条件
AWS CDKはv2を使用しています。
$ cdk --version 2.3.0 (build beaa5b2)
Route53のパブリックホストゾーンを事前に作成しています。
解説
主なファイル構成はこんな感じです。
- bin/
- cdk-cloudfront-cross-region-sample.ts
- lib/
- acm-for-cloudfront-stack.ts
- cloudfront-stack.ts
- remote-output-stack.ts
- s3/
- index.html
#!/usr/bin/env node import 'source-map-support/register'; import * as cdk from 'aws-cdk-lib'; import { AcmForCloudfrontStack } from "../lib/acm-for-cloudfront-stack"; import { RemoteOutputStack } from "../lib/remote-output-stack"; import { CloudfrontStack } from "../lib/cloudfront-stack"; const account = '<<YOUR_AWS_ACCOUNT_NO>>'; const domainName = '<<YOUR_PUBLIC_DOMAIN_NAME>>'; const hostName = 'cf-cross'; const envJP: cdk.Environment = { account, region: 'ap-northeast-1', }; const envUS: cdk.Environment = { account, region: 'us-east-1', }; const app = new cdk.App(); const acmForCloudfront = new AcmForCloudfrontStack(app, 'AcmForCloudfrontStack', { env: envUS, domainName, hostName, }); const remoteOutput = new RemoteOutputStack(app, 'RemoteOutput', { env: envJP, acm: acmForCloudfront, }); new CloudfrontStack(app, 'CloudFront', { env: envJP, domainName, hostName, acmArn: remoteOutput.acmArn, }); cloudfront.addDependency(acmForCloudfront);
自分で使用するときは、環境に合わせて <<YOUR_AWS_ACCOUNT_NO>>
と <<YOUR_PUBLIC_DOMAIN_NAME>>
を書き換えてください。
まず最初に24行目でバージニアリージョンのACMを作成しています。
次に30行目でcdk-remote-stackパッケージを利用して、東京リージョンでACMのARNを読み込める形にしています。
最後に35行目でバージニアリージョンのACMのARNを参照しつつ、東京リージョンでCloudFront+S3を作成しています。
import { Construct } from 'constructs' import { Stack, StackProps, CfnOutput, } from 'aws-cdk-lib' import * as route53 from 'aws-cdk-lib/aws-route53'; import * as certificatemanager from 'aws-cdk-lib/aws-certificatemanager'; interface AcmForCloudfrontStackProps extends StackProps { hostName: string domainName: string } export class AcmForCloudfrontStack extends Stack { constructor(scope: Construct, id: string, props: AcmForCloudfrontStackProps) { super(scope, id, props); const hostedZone = route53.HostedZone.fromLookup( this, 'HostedZone', { domainName: props.domainName, } ); const certificate = new certificatemanager.DnsValidatedCertificate( this, 'Certificate', { domainName: `${props.hostName}.${props.domainName}`, hostedZone: hostedZone, validation: certificatemanager.CertificateValidation.fromDns(hostedZone), } ); new CfnOutput(this, 'AcmArn', { value: certificate.certificateArn, }); } }
cdk-remote-stackでACMのARNを読み込む際に重要なのは、38行目のCfnOutputです。
このCfnOutputで出力した値を AcmArn
をキーにして参照します。
import { RemoteOutputs } from "cdk-remote-stack"; import { App, Stack, StackProps, } from 'aws-cdk-lib' import { AcmForCloudfrontStack } from "./acm-for-cloudfront-stack"; interface RemoteOutputStackProps extends StackProps { acm: AcmForCloudfrontStack; } export class RemoteOutputStack extends Stack { public readonly acmArn: string; constructor(scope: App, id: string, props: RemoteOutputStackProps) { super(scope, id, props); this.addDependency(props.acm) const outputs = new RemoteOutputs(this, "Outputs", { stack: props.acm, alwaysUpdate: false, }); this.acmArn = outputs.get("AcmArn"); } }
出力された AcmArn
を参照して acmArn
変数に格納しているのが26行目の outputs.get("AcmArn")
です。
ここまでできたら後はこの acmArn
を参照して、いつもどおりCloudFrontのcdkを書くだけです。
CloudFrontのcdkでの書き方は、こちらのブログをご覧ください。
実際のプログラムはこちらです。
このcdkをデプロイすると、S3を東京リージョンに配置しながらカスタムドメインのCloudFrontが構築できます。
終わりに
今までcdkではクロスリージョンの参照ができずに諦めて手作業でやっていたことが、cdkで完結できるようになりました。 いろいろなパターンで使えそうなので、クロスリージョン参照で困っている方はぜひ試してみてください。