ALBのリスナールールを自動で切り替えるLambda関数
SetRulePriorities APIを使用して、ALBのリスナールールを自動で切り替える関数です。
CloudFormationからSNS経由で通知を受け取り、CloudFormationによる更新中はメンテンナンス画面を表示し、更新が完了したらEC2のサイトを表示するというルールの切り替えを行います。
概要
- ランタイム:
Node 14.x
- アクセス権限:
AWSLambdaBasicExecutionRole
-
ELB v2 SetRulePriorities
(カスタムポリシーで選択する)
- 環境変数
-
LogicalResourceId
: 更新するCloudFormationスタック名 -
ForwardRuleArn
: ターゲットグループへの転送ルールのARN -
MaintenanceRuleArn
: メンテナンス用の固定レスポンスを返すルールのARN
-
ソース
const AWS = require('aws-sdk');
AWS.config.update({ region: 'ap-northeast-1' });
AWS.config.apiVersions = {
elbv2: '2015-12-01',
// other service API versions
};
const elbv2 = new AWS.ELBv2();
exports.handler = async (event) => {
console.log(JSON.stringify(event, null, 2));
const messages = event.Records[0].Sns.Message.split('\n');
//リソースの論理IDを取得
const logicalResourceId = messages
.find((message) => {
return message.includes('LogicalResourceId');
})
.replace(/LogicalResourceId=|\'/g, '');
console.log(logicalResourceId);
if (logicalResourceId !== process.env.LogicalResourceId) {
return;
}
//更新状況を取得
const resourceStatus = messages
.find((message) => {
return message.includes('ResourceStatus=');
})
.replace(/ResourceStatus=|\'/g, '');
console.log(resourceStatus);
let params = '';
if (resourceStatus === 'UPDATE_IN_PROGRESS') {
params = {
RulePriorities: [
/* required */
{
Priority: 1,
RuleArn: process.env.MaintenanceRuleArn,
},
{
Priority: 2,
RuleArn: process.env.ForwardRuleArn,
},
/* more items */
],
};
} else if (
resourceStatus === 'UPDATE_COMPLETE' ||
resourceStatus === 'UPDATE_ROLLBACK_COMPLETE'
) {
params = {
RulePriorities: [
/* required */
{
Priority: 1,
RuleArn: process.env.ForwardRuleArn,
},
{
Priority: 2,
RuleArn: process.env.MaintenanceRuleArn,
},
/* more items */
],
};
} else {
return;
}
try {
const result = await elbv2.setRulePriorities(params).promise();
console.log(JSON.stringify(result, null, 2));
} catch (err) {
console.log(err);
}
};
event
の中身
CloudFormationからSNS経由で飛んでくる情報はevent
に入っていますが、実際には以下のような情報が入っています。
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:ap-northeast-1:467964259659:Study_Topic_NotifyCloudFormationEvent:986df782-8a51-45e7-b523-9bd396c5ebf3",
"Sns": {
"Type": "Notification",
"MessageId": "d255cf45-5bc0-5286-af38-97b50ae294b0",
"TopicArn": "arn:aws:sns:ap-northeast-1:467964259659:Study_Topic_NotifyCloudFormationEvent",
"Subject": "AWS CloudFormation Notification",
"Message": "StackId='arn:aws:cloudformation:ap-northeast-1:467964259659:stack/StudyRelease/2fc47fe0-8ab6-11eb-b4b5-0e9ed3f1b3df'\nTimestamp='2021-04-14T02:09:38.195Z'\nEventId='77321280-9cc6-11eb-bae0-0a1871fe28bb'\nLogicalResourceId='StudyRelease'\nNamespace='467964259659'\nPhysicalResourceId='arn:aws:cloudformation:ap-northeast-1:467964259659:stack/StudyRelease/2fc47fe0-8ab6-11eb-b4b5-0e9ed3f1b3df'\nPrincipalId='AROAWZ5GX5VF6AF2NRDU6:tnet_admin_119503'\nResourceProperties='null'\nResourceStatus='UPDATE_ROLLBACK_COMPLETE'\nResourceStatusReason=''\nResourceType='AWS::CloudFormation::Stack'\nStackName='StudyRelease'\nClientRequestToken='Console-ExecuteChangeSet-6ed7afc4-c02a-bac3-48e8-dea1b25a96b9'\n",
"Timestamp": "2021-04-14T02:09:38.250Z",
"SignatureVersion": "1",
"Signature": "JrKCtXJEL6aNAZStjXVRfFYHEHYPM63zVPgAf929WKD+2AlB3YUuMc4ZtFg4ly2DDN+ilOzCcCAs5i+fUaGDF8dkSmQS34L7ikCmJPVVXrabam8yZ8CP9lcn2bbTUCYCb75ec9cdDqE3c0Snp6fCKB/ZgkMMCV7w4TTdMzNAHyldjseWu/1goxq91q6X9dOcSYVoV5GuNrI0kQOfRKJQOguWCYV8Ze7uXFJ5frmUrcD/xb3eRNnzCNjAe3pvTqNPVU6RgLO2fIfTq6Qlo+NSweQZ7niJvrEQ+X+uugnWAoFKz7tqO+mehsifFeS2K/oJDvz9XmOC0EAOWF3rDgukuw==",
"SigningCertUrl": "https://sns.ap-northeast-1.amazonaws.com/SimpleNotificationService-010a507c1833636cd94bdb98bd93083a.pem",
"UnsubscribeUrl": "https://sns.ap-northeast-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-northeast-1:467964259659:Study_Topic_NotifyCloudFormationEvent:986df782-8a51-45e7-b523-9bd396c5ebf3",
"MessageAttributes": {}
}
}
]
}
処理の最初の方で、Message
から必要な情報のみを取得しています。
CloudFormationからの通知内容の判定
CloudFormationからはスタック自体の更新情報の他にもリソースごとの更新情報も飛んできます。
この関数の処理の目的は、
「スタックの更新が開始されたらメンテナンス画面を表示し、スタックの更新が完了したらサイトを表示するようルールの順序を変更する」
ことなので、必要な情報はスタック名とスタックの更新状況の2つです。
まずはスタック名が含まれているかを判定します。
if (logicalResourceId !== process.env.LogicalResourceId) {
return;
}
続いて更新状況の判定です。
更新中(UPDATE_IN_PROGRESS
)ならメンテ画面を表示するルールを1
に設定します。
更新完了(UPDATE_COMPLETE
)またはロールバック完了(UPDATE_ROLLBACK_COMPLETE
)ならサイトを表示するルールを1
に設定します。
if (resourceStatus === 'UPDATE_IN_PROGRESS') {
params = {
RulePriorities: [
/* required */
{
Priority: 1,
RuleArn: process.env.MaintenanceRuleArn,
},
{
Priority: 2,
RuleArn: process.env.ForwardRuleArn,
},
/* more items */
],
};
} else if (
resourceStatus === 'UPDATE_COMPLETE' ||
resourceStatus === 'UPDATE_ROLLBACK_COMPLETE'
) {
params = {
RulePriorities: [
/* required */
{
Priority: 1,
RuleArn: process.env.ForwardRuleArn,
},
{
Priority: 2,
RuleArn: process.env.MaintenanceRuleArn,
},
/* more items */
],
};
} else {
return;
}
ALBへ設定変更リクエスト
リクエストにはSetRulePriorities APIを使用します。
パラメーターの詳細はドキュメントをご覧ください。
この関数では優先順位であるPriority
とALBリスナールールのARNであるRuleArn
を設定しています。
まとめ
今回はALBのリスナールールを自動で切り替える関数を紹介しました。
現状はCloudFormationの通知機能から発火させていますが、他にもEventBridgeのスケジュール実行や、フロントエンドからボタンでの切り替えなどもできると思うので、参考にして頂ければ幸いです。
Discussion