AWS CDK v2 (Go) で L1 Constructs を使う
2022-07-23最近 Go 言語で AWS CDK を使っているのですが、 L1 Constructs (low-level constructs) での記述に関する情報が見つけられず苦戦したので、メモとして残しておきます。
目次
前提
今回は、先日リリースされた CloudFormation Stack のイベントを SNS 経由でメール通知する構成を AWS CDK v2 (Go) で構築してみます。
作成する主なリソースは下記のとおりです。
- SNS::Topic
- SNS::TopicPolicy
- Events::Rule
また、通知されるのは 特定のプレフィックスを持つ Stack のみ を対象にしたいと思います。
L1 Constructs が必要な理由
今回の構成で L1 Constructs が必要になる理由は、 EventPattern で Resources を指定する際に、 prefix
演算子を使用したいからです。
EventPattern ではワイルドカード (*
) による値のマッチができず1、今回の要件である 特定のプレフィックスを持つ Stack のみ を対象としたい場合は prefix
演算子を使用する必要があります。2 この prefix
演算子を使用するか否かで、 L1 Constructs による実装が必要か否かが決まります。
具体的にどのような違いになるのか見ていきます。
リソース指定しない、または固定値で指定する場合
まず、 EventPattern として指定する JSON のスキーマおよび記述方法は下記のようになります。
{
"detail-type": [
"CloudFormation Resource Status Change",
"CloudFormation Stack Status Change",
"CloudFormation Drift Detection Status Change"
],
"resources": [
"arn:aws:cloudformation:ap-northeast-1:000000000000:stack/TestStack_Red",
"arn:aws:cloudformation:ap-northeast-1:000000000000:stack/TestStack_Green",
"arn:aws:cloudformation:ap-northeast-1:000000000000:stack/TestStack_Blue"
]
}
一方、 AWS CDK (Go) の L2 Constructs で EventPattern の定義に使用する awsevents.EventPattern
構造体の定義は下記のとおりです。(上記の JSON と対応する部分のみ。全体は
pkg.go.dev
を参照。)
type EventPattern struct {
DetailType *[]*string `field:"optional" json:"detailType" yaml:"detailType"`
Resources *[]*string `field:"optional" json:"resources" yaml:"resources"`
}
Resources
は *string
のスライスとして定義されているので、設定したい JSON のスキーマと一致しています。なので、固定のリソースを対象とする、もしくはリソースを指定しない場合は L2 Constructs で定義することができます。 (対象のリソースを指定しない場合は EventPattern.Resources
を省略すればよいだけです)
特定のプレフィックスを持つ Stack を対象リソースとする場合
特定のプレフィックスを持つ Stack を対象リソースとする場合、 EventPattern の JSON は prefix
演算子を使用して下記のような記述になります。
{
"detail-type": [
"CloudFormation Resource Status Change",
"CloudFormation Stack Status Change",
"CloudFormation Drift Detection Status Change"
],
"resources": [
{
"prefix": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/TestStack"
}
]
}
prefix
演算子を使わない場合とは異なり、 resources
配列の中身が文字列ではなくオブジェクトになっています。なので、 L2 Constructs ではこの JSON を表現することができず、 L1 Constructs を使用する必要が出てきます。
L1 Constructs を使用したリソースの定義方法
ではここで本題の、 AWS CDK (Go) で L1 Constructs を使用してリソースを定義する方法について見ていきます。
結論として、 *map[string]interface{}
型で定義します。
先ほどの prefix
演算子を使用した JSON を表現するには、下記のように EventPattern を定義します。
eventPattern := &map[string]interface{}{
"detail-type": &[]*string{
jsii.String("CloudFormation Resource Status Change"),
jsii.String("CloudFormation Stack Status Change"),
jsii.String("CloudFormation Drift Detection Status Change"),
},
"resources": &[]interface{}{
&map[string]*string{
"prefix": jsii.String("arn:aws:cloudformation:ap-northeast-1:000000000000:stack/TestStack"),
},
},
}
L1 Constructs だと awsevents.CfnRuleProps.EventPattern
が interface{}
型になるので、 *map[string]interface{}
型のスライスとして実装することで単純な文字列や prefix
などの演算子を使用したオブジェクト型での指定が可能になります。
これを awsevents.CfnRule
の定義と合わせると、下記のようになります。
awsevents.NewCfnRule(scope, jsii.String("CloudFormationEventsRule"), &awsevents.CfnRuleProps{
Name: jsii.String(util.ToKebabCase("cloud-formation-events-rule")),
EventBusName: jsii.String("default"),
State: jsii.String("ENABLED"),
EventPattern: &map[string]interface{}{
"detail-type": &[]*string{
jsii.String("CloudFormation Resource Status Change"),
jsii.String("CloudFormation Stack Status Change"),
jsii.String("CloudFormation Drift Detection Status Change"),
},
"region": &[]*string{
region,
},
"resources": &[]interface{}{
&map[string]*string{
"prefix": jsii.String("arn:aws:cloudformation:ap-northeast-1:000000000000:stack/TestStack"),
},
},
},
Targets: &[]interface{}{
&map[string]*string{
"arn": jsii.String("arn:aws:sns:ap-northeast-1:000000000000:TestTopic"),
"id": jsii.String("Target0"),
},
},
})
まとめ
AWS CDK v2 で Go 言語を使用した場合の L1 Constructs によるリソースの定義方法について書きました。
v2 で Go がサポートされたとはいえ TypeScript での利用が大多数のようで、なかなか Go を利用した場合の情報がなく大変だったんですが、完全に理解した気がします。
今回の内容を使ったその他のリソースも合わせた実装例は下記リポジトリに置いていますので、参考になれば幸いです。
aws-cdk-go-examples/cloudformation-events-to-slack at main · michimani/aws-cdk-go-examples
-
他にも数値の範囲によるマッチなどが可能 Amazon EventBridge のイベントパターン - Amazon EventBridge ↩︎
comments powered by Disqus