この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
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
bin/cdk-cloudfront-cross-region-sample.ts
#!/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を作成しています。
lib/acm-for-cloudfront-stack.ts
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
をキーにして参照します。
lib/remote-output-stack.ts
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で完結できるようになりました。 いろいろなパターンで使えそうなので、クロスリージョン参照で困っている方はぜひ試してみてください。