スタックに更新があったことをトリガーに処理を行いたいな
こんにちは、のんピ(@non____97)です。
皆さんはCloudFormationのスタックに更新があったことをトリガーに何か処理を行いたいと思ったことはありますか? 私はあります。
例えば、スタック内の特定リソースが更新されたら、Lambda関数で更新されたリソースの情報を使って関連リソースの設定内容を更新したい場面があります。
今回、CloudFormationの各種イベントがAmazon EventBridgeに送信されるようになったとアナウンスされました。
スタック間の連携や、ドリフト検出通知に使えそうですね。
早速試してみたので紹介します。
対象のイベント
今回のアップデートでサポートされたCloudFormationのイベントを確認します。
サポート対象のイベントは以下の3つです。
- リソースのステータス
- スタックのステータス
- ドリフト検出ステータス
スタックのステータスだけでなく、リソースやドリフト検出のステータスを検知できるのはありがたいですね。
EventBridgeのサンドボックスを確認すると、早速追加されたイベントのサンプルイベントが追加されていました。
それぞれのサンプルイベントは以下の通りです。
CloudFormation Resource Status Changeのサンプルイベント
{
"version": "0",
"source": "aws.cloudformation",
"account": "123456789012",
"id": "12345678-1234-1234-1234-111122223333",
"region": "us-east-1",
"detail-type": "CloudFormation Resource Status Change",
"time": "2022-04-31T17:00:00Z",
"resources": ["arn:aws:cloudformation:us-east-1:123456789012:stack/teststack"],
"detail": {
"stack-id": "arn:aws:cloudformation:us-west-1:123456789012:stack/teststack",
"logical-resource-id": "my-s3-bucket",
"physical-resource-id": "arn:aws:s3:us-east-1:123456789012:bucket:my-s3-bucket",
"status-details": {
"status": "CREATE_COMPLETE",
"status-reason": ""
},
"resource-type": "AWS::S3::Bucket"
}
}
CloudFormation Stack Status Changeのサンプルイベント
{
"version": "0",
"source": "aws.cloudformation",
"account": "123456789012",
"id": "12345678-1234-1234-1234-111122223333",
"region": "us-east-1",
"detail-type": "CloudFormation Stack Status Change",
"time": "2022-04-31T17:00:00Z",
"resources": ["arn:aws:cloudformation:us-east-1:123456789012:stack/teststack"],
"detail": {
"stack-id": "arn:aws:cloudformation:us-west-1:123456789012:stack/teststack",
"status-details": {
"status": "CREATE_COMPLETE",
"status-reason": ""
}
}
}
CloudFormation Drift Detection Status Changeのサンプルイベント
{
"version": "0",
"source": "aws.cloudformation",
"account": "123456789012",
"id": "12345678-1234-1234-1234-111122223333",
"region": "us-east-1",
"detail-type": "CloudFormation Drift Detection Status Change",
"time": "2022-04-31T17:00:00Z",
"resources": ["arn:aws:cloudformation:us-east-1:123456789012:stack/teststack"],
"detail": {
"stack-id": "arn:aws:cloudformation:us-west-1:123456789012:stack/teststack",
"stack-drift-detection-id": "12345678-1234-123a-ab12-1234example",
"status-details": {
"stack-drift-status": "DRIFTED",
"detection-status": "DETECTION_COMPLETE"
},
"drift-detection-details": {
"drifted-stack-resource-count": 1
}
}
}
各ステータスがどの状態になっているのかはもちろん、リソースの論理IDや物理ID、ドリフトしているリソースの数なども通知されるのはありがたいです。Lambda関数でメッセージを整形してSlackに通知するのが捗りそうです。
スタックとリソースのイベントは以下の通りです。
ステータス | 説明 |
---|---|
CREATE_COMPLETE | 1つ以上のスタックまたはリソースの作成が正常に完了 |
CREATE_IN_PROGRESS | 1つ以上のスタックまたはリソースを作成中 |
CREATE_FAILED | 1つ以上のスタックまたはリソースの作成に失敗 |
DELETE_COMPLETE | 1つ以上のスタックまたはリソースの削除を正常に完了 |
DELETE_FAILED | 1つ以上のスタックまたはリソースの削除に失敗 |
DELETE_IN_PROGRESS | 1つ以上のスタックまたはリソースの削除中 |
REVIEW_IN_PROGRESS | テンプレートやリソースがないにもかかわらず、予想されるStackIdを持つ1つまたは複数のスタックが作成中 |
ROLLBACK_COMPLETE | スタックの作成に失敗またはスタックの作成の明示的なキャンセル後に1つ以上のスタックを正常に削除 |
ROLLBACK_FAILED | スタックの作成に失敗またはスタックの作成の明示的なキャンセル後に1つ以上のスタックの削除に失敗 |
ROLLBACK_IN_PROGRESS | スタックの作成に失敗またはスタックの作成の明示的なキャンセル後に1つ以上のスタックを削除中 |
UPDATE_COMPLETE | 1つ以上のスタックまたはリソース更新が正常に完了 |
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS | スタックの正常な更新後に1つ以上のスタックの古いリソースを削除中 |
UPDATE_FAILED | 1つ以上のスタックまたはリソースの更新に失敗 |
UPDATE_IN_PROGRESS | 1つ以上のスタックまたはリソースを更新中 |
UPDATE_ROLLBACK_COMPLETE | スタックの更新の失敗後、1つ以上のスタックを前の動作状態へのロールバックを正常に完了 |
UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS | スタックの更新の失敗後、1つ以上のスタックの新しいリソースを削除中 |
UPDATE_ROLLBACK_FAILED | スタックの更新の失敗後、1つ以上のスタックを前の動作ステートに戻すことに失敗 |
UPDATE_ROLLBACK_IN_PROGRESS | スタックの更新の失敗後、1つ以上のスタックを前の動作状態へロールバック中 |
IMPORT_IN_PROGRESS | インポートオペレーションが進行中 |
IMPORT_COMPLETE | resource import をサポートするスタック内のすべてのリソースについてインポートが正常に完了 |
IMPORT_ROLLBACK_IN_PROGRESS | インポートの以前のテンプレート構成にロールバック中 |
IMPORT_ROLLBACK_FAILED | スタック内の少なくとも1つのリソースでインポートロールバックに失敗 |
IMPORT_ROLLBACK_COMPLETE | インポートが以前のテンプレート構成に正常にロールバック完了 |
参考 : スタックの情報とリストの取得 - AWS CloudFormation
ドリフト検出ステータスは以下の3種類があります。
- ドリフト検出オペレーションステータス
- ドリフトステータス
- リソースドリフトステータス
その内、EventBridgeに送信されるイベントに含まれるステータスは、ドリフト検出オペレーションステータスとドリフトステータスです。
それぞれのステータスは以下の通りです。
ドリフト検出オペレーションステータス
ステータス | 説明 |
---|---|
DETECTION_COMPLETE | スタックドリフト検出オペレーションがドリフト検出をサポートするスタック内のすべてのリソースに対して正常に完了 |
DETECTION_FAILED | スタックドリフト検出オペレーションがスタック内の少なくとも1つのリソースに対して失敗 |
DETECTION_IN_PROGRESS | スタックドリフト検出オペレーションを実行中 |
ドリフトステータス
ステータス | 説明 |
---|---|
DRIFTED | スタックがテンプレートと異なる |
NOT_CHECKED | スタック、スタックセット、またはスタックインスタンスがテンプレートと異なるかどうかをチェックしていない |
IN_SYNC | サポートされている各リソースの現在の設定がテンプレートと一致している |
参考 : スタックとリソースに対するアンマネージド型設定変更の検出 - AWS CloudFormation
なお、全てのリソースのドリフトを検出できるわけではありません。ドリフト検出オペレーションをサポートするリソースは以下AWS公式ドキュメントをご覧ください。
やってみた
検証の構成
検証の構成は以下の通りです。
各イベントをトリガーにEメール通知とAWS Chat経由でのSlack通知を行います。
- リソースのステータス変更
- スタックのステータス変更
- ドリフト検出ステータス変更
こちらのリソースをAWS CDKでデプロイ後、再度スタックを更新してどのようなメッセージが送信されるか確認します。
リソースのデプロイ
それでは、AWS CDKで各種リソースをデプロイします。使用したコードは以下リポジトリに保存しています。
デプロイする際は.env
ファイルに以下情報を入力しておきます。
AWS_CHATBOT_WORKSPACE_ID=<AWS ChatbotのワークスペースのID>
SLACK_CHANNEL_ID=<通知先のSlackチャンネルのID>
SLACK_CHANNEL_NAME=<通知先のSlackチャンネルの名前>
EMAIL_ADDRESS=<通知先のメールアドレス>
通知確認
デプロイ後、AWS Chatbotのログ保存期間を1週間から2週間に修正して再デプロイします。
./lib/cfn-events-stack.ts
new chatbot.SlackChannelConfiguration(this, "Slack Channel", {
slackChannelConfigurationName: props.slackChannelName,
slackWorkspaceId: props.awsChatbotWorkspaceID,
slackChannelId: props.slackChannelID,
logRetention: logs.RetentionDays.TWO_WEEKS,
loggingLevel: chatbot.LoggingLevel.INFO,
notificationTopics: [topic],
});
再デプロイすると以下のようなSlack通知が来ていました。
スタックやリソースに対して何かした更新があったことは分かりますが、どのようなステータスになったのかは表示されませんでした。
スタックにSNS通知を設定することで、スタックに何か更新があったらSNS経由でメールやSlackに通知することができます。
こちらの方が詳細な情報を通知することができるので、手軽に更新を通知したい場合は、スタックにSNS通知を設定した方が良さそうですね。
メールも5件来ていました。メッセージ本文のJSONは以下の通りです。
{
"version": "0",
"id": "d27886c4-36b8-e9e7-b1e2-f8d8959c1d0f",
"detail-type": "CloudFormation Stack Status Change",
"source": "aws.cloudformation",
"account": "<AWSアカウントID>",
"time": "2022-07-22T09:19:06Z",
"region": "us-east-1",
"resources": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/CfnEventsStack/52856e20-0960-11ed-bde0-0aa886aa4ef7"
],
"detail": {
"stack-id": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/CfnEventsStack/52856e20-0960-11ed-bde0-0aa886aa4ef7",
"status-details": {
"status": "UPDATE_IN_PROGRESS",
"status-reason": "User Initiated"
}
}
}
{
"version": "0",
"id": "db3e571f-190b-9e8d-a9ff-7bdf178680fa",
"detail-type": "CloudFormation Resource Status Change",
"source": "aws.cloudformation",
"account": "<AWSアカウントID>",
"time": "2022-07-22T09:19:12Z",
"region": "us-east-1",
"resources": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/CfnEventsStack/52856e20-0960-11ed-bde0-0aa886aa4ef7"
],
"detail": {
"stack-id": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/CfnEventsStack/52856e20-0960-11ed-bde0-0aa886aa4ef7",
"logical-resource-id": "SlackChannelLogRetention46A4D367",
"physical-resource-id": "/aws/chatbot/times-non-97",
"resource-type": "Custom::LogRetention",
"status-details": {
"status": "UPDATE_IN_PROGRESS",
"status-reason": ""
}
}
}
{
"version": "0",
"id": "aa9828bd-03ac-77ec-cf36-4ebec1ebe09e",
"detail-type": "CloudFormation Resource Status Change",
"source": "aws.cloudformation",
"account": "<AWSアカウントID>",
"time": "2022-07-22T09:19:16Z",
"region": "us-east-1",
"resources": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/CfnEventsStack/52856e20-0960-11ed-bde0-0aa886aa4ef7"
],
"detail": {
"stack-id": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/CfnEventsStack/52856e20-0960-11ed-bde0-0aa886aa4ef7",
"logical-resource-id": "SlackChannelLogRetention46A4D367",
"physical-resource-id": "/aws/chatbot/times-non-97",
"resource-type": "Custom::LogRetention",
"status-details": {
"status": "UPDATE_COMPLETE",
"status-reason": ""
}
}
}
{
"version": "0",
"id": "323fa81d-7dc2-24a2-685b-b4036c115570",
"detail-type": "CloudFormation Stack Status Change",
"source": "aws.cloudformation",
"account": "<AWSアカウントID>",
"time": "2022-07-22T09:19:18Z",
"region": "us-east-1",
"resources": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/CfnEventsStack/52856e20-0960-11ed-bde0-0aa886aa4ef7"
],
"detail": {
"stack-id": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/CfnEventsStack/52856e20-0960-11ed-bde0-0aa886aa4ef7",
"status-details": {
"status": "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS",
"status-reason": ""
}
}
}
{
"version": "0",
"id": "9246974f-baf3-cf9b-15b6-c39211042d12",
"detail-type": "CloudFormation Stack Status Change",
"source": "aws.cloudformation",
"account": "<AWSアカウントID>",
"time": "2022-07-22T09:19:18Z",
"region": "us-east-1",
"resources": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/CfnEventsStack/52856e20-0960-11ed-bde0-0aa886aa4ef7"
],
"detail": {
"stack-id": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/CfnEventsStack/52856e20-0960-11ed-bde0-0aa886aa4ef7",
"status-details": {
"status": "UPDATE_COMPLETE",
"status-reason": ""
}
}
}
EventBridgeに送信されたイベントをそのまま確認できました。通知内容をEventBridgeのインプットトランスフォーマーで整形すれば、かなり観やすくなりそうです。
次にドリフト検出のイベントを確認してみます。
スタックアクション
-ドリフトを検出
をクリックします。
しばらくすると、SNSトピックについてのドリフトが検出されました。
内容を確認すると、SNSトピックにAWS Chatbotのエンドポイントのサブスクリプションが追加ことによるドリフトのようです。
通知内容を確認します。
Slackに通知されたメッセージは1件のみでした。
メールも1件のみでした。
{
"version": "0",
"id": "8b3095e7-e4a6-936c-e86d-611c8644720a",
"detail-type": "CloudFormation Drift Detection Status Change",
"source": "aws.cloudformation",
"account": "<AWSアカウントID>",
"time": "2022-07-22T09:41:07Z",
"region": "us-east-1",
"resources": [
"arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/CfnEventsStack/52856e20-0960-11ed-bde0-0aa886aa4ef7"
],
"detail": {
"stack-id": "arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/CfnEventsStack/52856e20-0960-11ed-bde0-0aa886aa4ef7",
"stack-drift-detection-id": "69909b70-09a2-11ed-90e8-123de96e8c03",
"status-details": {
"stack-drift-status": "",
"detection-status": "DETECTION_IN_PROGRESS"
},
"drift-detection-details": {
"drifted-stack-resource-count": -1
}
}
}
DETECTION_COMPLETE
やDRIFTED
の通知も来ると思ったのですが、来ませんでした。
AMIの作成や登録解除などのイベントがAmazon EventBridgeに送信されるようになったアップデート当日も一部イベントは検知できなかったので、一過性のものだと信じます。
CloudFormationのイベントを駆使するアーキテクチャの実装ができるぞ
CloudFormationの各種イベントがAmazon EventBridgeに送信されるようになったアップデートを紹介しました。
今まで同様の仕組みを採用する場合は、スタックやリソースのステータスをポーリングする必要がありました。今回のアップデートでその手間がなくなりました。
CloudFormationのイベントを使って色々なアーキテクチャが考えられそうですね。
なお、EventBridgeルール経由でSlackに通知する際は、このままだと情報が足りないと思うので、Lambda関数かEventBridgeのインプットトランスフォーマーでメッセージを整形することをおすすめします。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!