AwsCustomResourceを使ってAWS CDKでのデプロイ時に別リージョンの値を参照する
はじめに
AWS CDKを触っていて、「ap-northeast-1
からus-east-1
にあるリソースのARNや値を取得したいな」と思うことはありませんか? 言い換えるならば「AWS CDKの利用時に2つ以上のリージョンにデプロイが必要で、リージョン間の値の相互参照をしたい」というような状況です。
通常、AWS CDKは1回のデプロイでは単一のリージョンへしかできず、デプロイ時に別リージョンの値を取得・更新はできないです。
具体的なユースケースでパッと思いつくのは、Lambda@Edgeをus-east-1
に設定した上で、
CloudFrontをap-northeast-1
にデプロイするときなどです。
(※:一応、この場合はCDKのaws_cloudfront.experimental
を利用すると、 1回のデプロイでus-east-1
と別の1リージョンに同時にデプロイできはします)。
この「AWS CDKの利用時に2つ以上のリージョンにデプロイが必要で、リージョン間の値の相互参照をしたい」という時に有効な方法があります。
それは、AwsCustomResource
を利用する方法です。
やりたいこと・この記事の対象読者・利用時のイメージ図
やりたいこと
やりたいことは、「AWS CDKの利用時に2つ以上のリージョンにデプロイが必要で、リージョン間の値の相互参照をすること」です。
対象読者
以下の2パターンくらいを実行したい人におすすめです。
- 上記の「やりたいこと」をしたい人
- AWS CDKを触ってる人
利用時のイメージ図
「Lambda@Edgeをus-east-1
に設定した上で、 CloudFrontをap-northeast-1
にデプロイする」時のユースケースで考えてみます。
CloudFrontへ設定するLambda@Edgeが別リージョンにある場合、ARNなどの値は参照できません。そのため、ap-northeast-1
のStackに直接パラメータを付与したりなどの処理が必要です。
AwsCustomResource
を利用するとSSM
を介してap-northeast-1
から擬似的にus-east-1
の値を参照できます。
この図の場合、デプロイは2回行っています。
-
us-east-1
のデプロイ-
AWS Lambda
・Lambda@Edge
をデプロイ -
AwsCustomResource
が動いて、ap-northeast-1
のSSM
にパラメータを設定
-
-
ap-northeast-1
のデプロイ-
ap-northeast-1
のSSM
からパラメータを読み取る -
Amazon CloudFront
をデプロイ
-
2回のデプロイのうち、us-east-1
へデプロイするときにAwsCustomResource
を動かしています。
これによって、us-east-1
にあるARNやその他の任意の値をap-northeast-1
から読み込めるようになります。
コード
コードは以下の3つの内容を載せます。
この3つの内容で「やりたいこと」が実現できます。
-
AwsCustomResource
を利用したクラスの作成 -
us-east-1
にあるStackでの利用方法 -
ap-northeast-1
にあるStackでの参照方法
AwsCustomResource
を利用したクラスの作成
1. このクラスでは、SSM
のパラメータを作成・更新する処理を担っています。
クラスとして作成しなくても良いのですが、見通しやすくするために作成しています。
コードの内容はここを参考にコードをTypeScript
に変更してあります。
import {
Stack,
custom_resources,
} from 'aws-cdk-lib';
import {Construct} from 'constructs';
export interface IXRegionParam {
region?: string
}
export interface IPutSsmParam {
parameterName: string
parameterValue: string
parameterDataType: string
idName: string
}
export class XRegionParam extends Construct {
private stack: Stack
public region: string
constructor(scope: Construct, id: string, props: IXRegionParam) {
super(scope, id)
this.stack = Stack.of(this)
this.region = props.region ? props.region : this.stack.region
}
putSsmParameter(props: IPutSsmParam) {
const resultParams = new custom_resources.AwsCustomResource(this, props.idName, {
policy: custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
resources: custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE
}),
onUpdate: {
service: "SSM",
action: "putParameter",
parameters: {
Name: props.parameterName,
Value: props.parameterValue,
DataType: props.parameterDataType,
Type: "String", // "String"は定数値
Overwrite: true
},
region: this.region,
physicalResourceId: custom_resources.PhysicalResourceId.of(props.idName)
}},
)
}
}
us-east-1
にあるStackでの利用方法
2. us-east-1
からap-northeast-1
に値を設定する際に、1で作成したXRegionParam
を利用します。
region
のパラメータにリージョンを入れると、そのリージョンのSSM
の値を作成・更新します。
import {
Stack,
StackProps,
aws_ssm,
} from 'aws-cdk-lib';
import {XRegionParam} from './XRegionParam';
export class UsEast1Stack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
/** ~~ 前略 ~~ */
new XRegionParam(this, "x-region-param", {
region: "ap-northeast-1"
}).putSsmParameter({
parameterName: `/cdk/EdgeFunctionArn/${id}/[パラメータの名前]`,
parameterValue: "[パラメータの値]",
parameterDataType: "text", // "text"は定数値
idName: `x-region-param-id-${id}`
})
/** ~~ 後略 ~~ */
}
}
上記のコードで、ap-northeast-1
のSSM
へ値を作成・更新をしています。
ap-northeast-1
にあるStackでの参照方法
3. 設定したリージョンからは、XRegionParam / AwsCustomResource
ではなくCDKのSSMのリソースをそのまま利用できます。
ただ、parameterName
は設定した値と同一にする必要があります。
import {
Stack,
StackProps,
aws_ssm,
} from 'aws-cdk-lib';
export class ApNorthast1Stack extends Stack {
constructor(scope: App, id: string, props?: StackProps) {
super(scope, id, props);
/** ~~ 前略 ~~ */
const someParam = aws_ssm.StringParameter.fromStringParameterAttributes(this, 'ssmParam', {
parameterName: `/cdk/EdgeFunctionArn/[UsEast1Stackで設定したidの値]/[パラメータの名前]`,
}).stringValue;
/** ~~ 後略 ~~ */
}
}
上記のコードで、SSM
から値を取得しています。
デプロイ!
上記の3つをそれぞれ反映した上で、us-east-1
/ ap-northeast-1
のStackをそれぞれ順番にデプロイすれば、「やりたいこと」はできています。
注意点・その他
AwsCustomResource
を使って呼ぶ方法
1. 他のメソッドを今回はSSM
のputParameter
という関数を利用して、SSM
の値を更新しました。
パラメータはAWS SDK for JavaScriptやboto3などが参考になります。
もちろん他のメソッドを呼び出すことができます。
その際は、上記のドキュメントから呼び出したい関数を、
XRegionParams
の以下のservice
/ action
/ parameter
の引数に合うように設定すれば
任意の関数を呼び出すことができます。
onUpdate: {
service: "SSM",
+ action: "getParameter",
- action: "putParameter",
parameters: {
Name: props.parameterName,
- Value: props.parameterValue,
- DataType: props.parameterDataType,
- Type: "String", // "String"は定数値
- Overwrite: true
},
ただし、あまり複雑な処理をしてしまうとStackの更新がしづらくなるので、
注意して利用した方がいいです。
us-east-1
からap-northeast-1
に設定する理由
2. 値を理由は「ap-northeast-1
のStackからus-east-1
の値が更新されたことを検知できず、継続的に適切な更新ができないから」です。
もちろんgetParameter
(参考)を利用すれば、ap-northest-1
からus-east-1
のSSM
へのリソースへアクセス可能です。加えてap-northeast-1
のStackからus-east-1
のSSM
の値も利用できます。
ただ、以下のような状態の時に問題が発生します。
-
us-east-1
のStackは更新してデプロイ済み -
us-east-1
のStackを更新したので、ap-northeast-1
のStackも更新したい -
ap-northeast-1
そのもののStackの内容は更新されていない
上記のような場合、CDKでap-northeast-1
の内容をデプロイする際には(no change)
と見なされて、us-east-1
への値の再参照をしてくれず、適切にap-northeast-1
のStackを更新できないという状況に陥ります。
もちろん、「ap-northeast-1
へのデプロイが一度しかない」というような状況だったら問題ないのですが、 それだったら、SSM
を介して値をやりとりする理由もあまりないと思います。
上記の状況を考えて、今回紹介したようなus-east-1
からap-northeast-1
のSSM
の値を更新する、という方法を採っています。
この方法だと、ap-northeast-1
のStackの更新はなくても、更新されたSSM
の内容を読み取って意図した更新処理ができます。
$ cdk destroy
でSSM
が削除されない
3. この方法で別リージョンにSSM
の値を設定した後で、
Stackを削除した場合($ cdk destory {stack}
)には
別リージョンのSSM
は削除されません。
そのため、手動で削除することを忘れないようにしてください。
おわりに
CDKのAwsCustomResource
を使って、リージョン間の値の受け渡しができました。
この方法は、参考記事にも書かれているように多用すると見通しが悪くなる可能性もあります。使う場所や理由を考えて快適なAWS CDKライフをお送りください。
Discussion