概要
平日の営業時間は処理を実行したいが、土日祝日は処理を動かしたくないケースがあるかと思います。
そこで平日のみLambdaを実行し、土日祝日はLambdaを実行しないようにする構成をCloudFormationで作成します。
今回の構成を作成する前に
まずは、月~金に処理を実行し、土日は処理を実行しない場合の構成をCloudFormationで作成してみます。
構成
構成としては以下のようなシンプルな形になります。
リソースの作成に使用するファイル
リソースの作成には以下のテンプレートを使用します。
AWSTemplateFormatVersion: '2010-09-09'
Description: "Periodic Execution Function Template"
Transform: AWS::Serverless-2016-10-31
Resources:
EventBridgeRule:
Type: AWS::Events::Rule
Properties:
Description: "lambda invoke every 10 minutes Monday through Friday rule."
Name: lambda-invoke-every-10minutes-rule
ScheduleExpression: 'cron(0/10 * ? * MON-FRI *)'
State: ENABLED
Targets:
- Arn: !GetAtt EventBridgeTriggerFunction.Arn
Id: lambda
EventBridgeTriggerFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: "output-current-time-function"
Description: "output current time"
Runtime: nodejs14.x
CodeUri: app/src
Handler: index.handler
MemorySize: 256
Timeout: 10
Role: !GetAtt EventBridgeTriggerFunctionRole.Arn
EventBridgeTriggerFunctionRole:
Type: AWS::IAM::Role
Properties:
RoleName: "output-current-time-function-role"
Description: "Role For Lambda"
Path: "/service/"
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: "log-output-policy"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*"
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/output-current-time-function:*"
LambdaInvokePermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt EventBridgeTriggerFunction.Arn
Principal: events.amazonaws.com
SourceArn: !GetAtt EventBridgeRule.Arn
Lambdaの処理では、現在日時を出力します。
exports.handler = async function (event, context) {
// 現在時刻を日本時間で出力
console.log(new Date(Date.now() + ((new Date().getTimezoneOffset() + (9 * 60)) * 60 * 1000)));
return;
}
リソースの作成
今回はローカル環境からAWS SAM CLIコマンドを使用して、スタックの作成を行います。
AWS SAM CLIコマンドのインストール方法は、こちらをご参照ください。
sam deploy \
--stack-name event-bridge-trigger-function-stack \
--capabilities CAPABILITY_NAMED_IAM \
--s3-bucket ${テンプレートアップロード先バケット名} \
--s3-prefix event-bridge-trigger-function-stack \
--no-fail-on-empty-changeset
作成したリソースは以下になります。
実行結果の確認
CloudWatchLogsを確認するとLambdaが10分毎に起動し、現在日時を出力していることがわかります。
土日祝日に実行されないようにしたい場合
先ほどの構成ですと、指定した日付や指定した曜日にLambdaを実行することは出来ますが、祝日に実行されたくない場合などに対応できません。
そこで、Systems Manager Change Calendarを使用し、祝日にLambdaが起動しないようにしていきます。
構成
構成としては以下になります。
Systems Manager Change Calendarとは
オリジナルのカレンダーイベントを作成できるサービスで、イベントの変化や現在の状態確認を通じて以下のようなことを実現できます。
・09:00にオープン、18:00にクローズするようなカレンダーを作成し、09:00になったらDBを動かし、18:00になったらDBを止めるようなLambdaを実行
・APIでカレンダーイベントの状態を取得して、オープンであれば営業時間内、クローズであれば営業時間外をレスポンス
カレンダーの設定はコンソール画面で自由に設定したり、GoogleカレンダーなどのサードパーティーのiCalendar (.ics) ファイルをインポートして、カレンダーに設定できます。
※詳しい内容を知りたい方は、AWS公式サイトなどをご参照ください。
カレンダーを作成してみる
Googleカレンダーからエクスポートしたicsファイルを利用して、カレンダーを作成してみます。
今回は日本時間のカレンダーを作成するため、icsファイルのタイムゾーンを以下のように変更します。
X-WR-TIMEZONE:UTC
↓
X-WR-TIMEZONE:Asia/Tokyo
カレンダー名を入力し、icsファイルをインポートします。
カレンダータイプは、祝日のみ実行しないようにするためデフォルトで開くを選択します。
リソースの作成に使用するファイル
リソースの作成には以下のテンプレートを使用します。
AWSTemplateFormatVersion: '2010-09-09'
Description: "Periodic Execution Function Template"
Transform: AWS::Serverless-2016-10-31
Resources:
OutputCurrentTimeFunctionRole:
Type: AWS::IAM::Role
Properties:
RoleName: "output-current-time-function-role"
Description: "Role For Lambda"
Path: "/service/"
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: "log-output-policy"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*"
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/output-current-time-function:*"
OutputCurrentTimeFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: "output-current-time-function"
Description: "output current time"
Runtime: nodejs14.x
CodeUri: app/src
Handler: index.handler
MemorySize: 256
Timeout: 10
Role: !GetAtt OutputCurrentTimeFunctionRole.Arn
EventBridgeRole:
Type: AWS::IAM::Role
Properties:
RoleName: "state-machine-exec-every-10minutes-rule-role"
Description: "Role For Lambda"
Path: "/service/"
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: "state-machine-exec-policy"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- states:StartExecution
Resource:
- !Ref StateMachine
EventBridgeRule:
Type: AWS::Events::Rule
Properties:
Description: "state machine exec every 10 minutes Monday through Friday rule."
Name: state-machine-exec-every-10minutes-rule
ScheduleExpression: 'cron(0/10 * ? * MON-FRI *)'
State: ENABLED
Targets:
- Arn: !Ref StateMachine
Id: lambda
Input: '{}'
RoleArn: !GetAtt EventBridgeRole.Arn
StateMachineLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /sfn/lambda-invoke-excluding-holiday-state-machine
StateMachineRole:
Type: AWS::IAM::Role
Properties:
RoleName: "lambda-invoke-excluding-holiday-state-machine-role"
Description: "Role For StateMachine"
Path: "/service/"
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- states.ap-northeast-1.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: "ssm-read-policy"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ssm:GetCalendarState
Resource: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:document/*"
- PolicyName: "lambda-invoke-policy"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource: !GetAtt OutputCurrentTimeFunction.Arn
- PolicyName: "log-policy"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogDelivery
- logs:GetLogDelivery
- logs:UpdateLogDelivery
- logs:DeleteLogDelivery
- logs:ListLogDeliveries
- logs:PutResourcePolicy
- logs:DescribeResourcePolicies
- logs:DescribeLogGroups
Resource: "*"
StateMachine:
Type: AWS::StepFunctions::StateMachine
Properties:
StateMachineName: lambda-invoke-excluding-holiday-state-machine
RoleArn: !GetAtt StateMachineRole.Arn
DefinitionString:
!Sub
- |-
{
"Comment": "Lambda Invoke Excluding Holiday State Machine",
"StartAt": "GetCalendarState",
"States": {
"GetCalendarState": {
"Type": "Task",
"Parameters": {
"CalendarNames": [
"${ssmCalendarArn}"
]
},
"Resource": "arn:aws:states:::aws-sdk:ssm:getCalendarState",
"Next": "CalendarStateChoice"
},
"CalendarStateChoice": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.State",
"StringEquals": "OPEN",
"Next": "LambdaInvoke"
},
{
"Variable": "$.State",
"StringEquals": "CLOSED",
"Next": "LambdaInvokeSkip"
}
],
"Default": "LambdaInvokeSkip"
},
"LambdaInvoke": {
"Type": "Task",
"Resource": "${lambdaArn}",
"Next": "LambdaInvokeSuccess"
},
"LambdaInvokeSuccess": {
"Type": "Succeed"
},
"LambdaInvokeSkip": {
"Type": "Pass",
"End": true
}
}
}
- {
ssmCalendarArn: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:document/JapaneseHoliday",
lambdaArn: !GetAtt OutputCurrentTimeFunction.Arn
}
LoggingConfiguration:
Destinations:
- CloudWatchLogsLogGroup:
LogGroupArn: !GetAtt StateMachineLogGroup.Arn
IncludeExecutionData: true
Level: ALL
リソースの作成
スタックの作成を行います。
sam deploy \
--stack-name invoke-lambda-excluding-holidays-function-stack \
--capabilities CAPABILITY_NAMED_IAM \
--s3-bucket ${テンプレートアップロード先バケット名} \
--s3-prefix invoke-lambda-excluding-holidays-function-stack \
--no-fail-on-empty-changeset
作成したリソースは以下になります。
・ステートマシン
ステートマシンでは、カレンダーの状態を取得(GetCalendarState)し、カレンダーの状態を確認(CalendarStateChoice)します。
状態がOPENであればLambdaを実行し、CLOSEDであればステートマシンを終了します。
実行結果を確認してみた
2022/06/03に実行されたステートマシンの結果を確認すると、カレンダーはOPEN状態のため、Lambdaが実行されていることがわかります。
次に、CLOSEDの動作確認のため、icsファイルにテスト祝日の日を追加し、再インポートします。
BEGIN:VEVENT
DTSTART;VALUE=DATE:20220603
DTEND;VALUE=DATE:20220604
DTSTAMP:20220603T082139Z
UID:20231009_irbln7n3car55hoi8mu58b019s@google.com
CLASS:PUBLIC
CREATED:20220126T031610Z
DESCRIPTION:祝日
LAST-MODIFIED:20220126T031610Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:テスト祝日の日
TRANSP:TRANSPARENT
ステートマシンの結果を確認すると、カレンダーはCLOSED状態のため、Lambda実行がスキップされていることがわかります。
おわりに
今回はSystems Manager Change Calendarを使用して、平日のみ起動するLambdaを作成しました。
EventBridgeのcron設定による定期実行だけでは実現できないパターンに対応できるので、実行制御の幅が広がりそうです。