この記事の想定読者
- AWSの基本知識がある
- CloudFormationを使った事がある
- cdkも使った事がある
L1とL2(とL3)
スタックをコードとして管理したい時、CloudFormationではyamlかjson形式で記述します。cdkでは、TypeScriptなどのメジャーな言語で記載する事が出来ます。cdkは、内部的には一旦CloudFormationに変換します。
そのcdkでは3種類の記述方法があります。下に紹介した公式ページに詳細はお願いしますが、L1はCloudFormationと同レベルの細かい設定が必要ですが、L2は必須の情報だけ設定すればあとはcdkがよしなにデフォルト設定してくれます。
L3では標準的な構成に関してさらにサービスをまとめてくれます。例えばApplicationLoadBalancedFargateService
ではALBとFargateのスタックをまとめて対応してくれます。
L2の難点
コード上の設定箇所が少ないのでありがたいL2ですが、細かい部分を指定したい場合に、L2だと対応していない時があるという難点があります。事前にわかっていれば最初からL1で作成するなどの対応が可能ですが、既にL2でスタックを作成している場合にL1に切り替えるとリソースが作り直されてしまう可能性があります(※未検証。後述しますがL2からL1へのキャストも出来るぐらいなので、スタック名を同じにすれば条件によっては大丈夫かもしれません)。
今回の具体例
Cognitoで、ユーザーを作成した場合の招待メッセージや、認証コードを送信する際のメッセージのフォーマットを指定したいけど、L2のドキュメントを探してもではその属性が存在しませんでした。
L1のドキュメントでは「verificationMessageTemplate」、「adminCreateUserConfig」の2プロパティになります。
対応ポイント
後述、参考にさせて頂いたページの追記版になってしまいますが、以下の通りです。
L2オブジェクトをL1のクラスにキャストする
L2オブジェクトにあるnode.defaultChild
属性で、L1のクラスにキャスト出来ます。
import * as cognito from "@aws-cdk/aws-cognito";
// ・・中略・・
// L2クラスのオブジェクト作成
const userPool = new cognito.UserPool(this, "UserPool", {
// ・・中略・・
// L1クラスへキャスト
const cfnUserPool = userPool.node.defaultChild as cognito.CfnUserPool;
// ・・後略・・
addPropertyOverrideメソッドを使う
L1形式ではクラス名はCfn****となっていますが、表題のメソッドが存在します。このメソッドによりプロパティの上書きが出来ます。
プロパティ名および値はCloudFormationのものを使用
上記関数で指定する時、プロパティ名は値はCloudFormationを意識したものになります。
自分は躓いてしまった所なのですが、L1の設定名であるadminCreateUserConfig
では駄目で、CloudFormationの設定名であるAdminCreateUserConfig
を使う必要があります(アッパーキャメルケースを使う)。
また、指定する値に関してです。プリミティブ型なら特に気にかけませんが、オブジェクト型の場合、L1のプロパティオブジェクトでなくjson形式で指定する必要があります。
L1クラスの関数なので当たり前と言えば当たり前です(L1形式ならaddPropertyOverrideを使わずに直接指定できる)。
cfnUserPool.addPropertyOverride('AdminCreateUserConfig', {
InviteMessageTemplate: {
EmailSubject: '仮パスワードをお送りします',
EmailMessage: 'ユーザーIDは{username}です。一時パスワードは「{####}」です。 ',
SMSMessage: 'ユーザーIDは{username}です。一時パスワードは「{####}」です。 '
}
});
cfnUserPool.addPropertyOverride('VerificationMessageTemplate', {
DefaultEmailOption: 'CONFIRM_WITH_CODE',
EmailSubject: '認証コードをお送りします',
EmailMessage: '認証コード「{####}」を入力してください。',
SmsMessage: '認証コード「{####}」を入力してください。',
});
const adminCreateUserConfig: cognito.CfnUserPool.AdminCreateUserConfigProperty = {
allowAdminCreateUserOnly: true,
inviteMessageTemplate: {
emailSubject: "仮パスワードをお送りします",
emailMessage: "ユーザーIDは{username}です。一時パスワードは「{####}」です。 ",
smsMessage: "ユーザーIDは{username}です。一時パスワードは「{####}」です。 "
}
};
const verificationMessageTemplateProperty: cognito.CfnUserPool.VerificationMessageTemplateProperty = {
defaultEmailOption: 'CONFIRM_WITH_CODE',
emailSubject: '認証コードをお送りします',
emailMessage: '認証コード「{####}」を入力してください。',
smsMessage: '認証コード「{####}」を入力してください。'
};
cfnUserPool.addPropertyOverride('adminCreateUserConfig', adminCreateUserConfig);
cfnUserPool.addPropertyOverride('verificationMessageTemplate', verificationMessageTemplateProperty);
感想
今までL2の難点を考えて、細かい設定する時困るからL1で書こうかなとか思う時があったのですが、この関数の存在を知ったので、安心してL2を使う事が出来ます。L3だと複数種類のスタックで構成される事からnode.defaultChild
でなく別の方法があると思います(※未調査。機会があったら調査して別記事にします)
結構常識的なナレッジかもしれませんが、自分が知らなかった事もあり、きっと誰かの役に立つだろうと信じての投稿でした。
参考にさせて頂いたページ