2
Help us understand the problem. What are the problem?

posted at

updated at

平日に起動するLambda関数を作成してみた

概要

平日の営業時間は処理を実行したいが、土日祝日は処理を動かしたくないケースがあるかと思います。
そこで平日のみLambdaを実行し、土日祝日はLambdaを実行しないようにする構成をCloudFormationで作成します。

今回の構成を作成する前に

まずは、月~金に処理を実行し、土日は処理を実行しない場合の構成をCloudFormationで作成してみます。

構成

構成としては以下のようなシンプルな形になります。

構成図-1.PNG

リソースの作成に使用するファイル

リソースの作成には以下のテンプレートを使用します。

template.yaml
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の処理では、現在日時を出力します。

app/src/index.js
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

作成したリソースは以下になります。

スタック2.PNG

実行結果の確認

CloudWatchLogsを確認するとLambdaが10分毎に起動し、現在日時を出力していることがわかります。
Lambdaログ.PNG
Lambdaログ2.PNG

土日祝日に実行されないようにしたい場合

先ほどの構成ですと、指定した日付や指定した曜日にLambdaを実行することは出来ますが、祝日に実行されたくない場合などに対応できません。
そこで、Systems Manager Change Calendarを使用し、祝日にLambdaが起動しないようにしていきます。

構成

構成としては以下になります。

構成図-2.PNG

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

カレンダーを作成ボタンをクリックします。
SSMカレンダー作成-1.PNG

カレンダー名を入力し、icsファイルをインポートします。
カレンダータイプは、祝日のみ実行しないようにするためデフォルトで開くを選択します。
SSMカレンダー作成-2.PNG

作成したカレンダーは以下になります。
SSMカレンダー-1.PNG

リソースの作成に使用するファイル

リソースの作成には以下のテンプレートを使用します。

template.yaml
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

作成したリソースは以下になります。

・スタック
スタック1.PNG

・EventBridge
EventBridge1.PNG

・ステートマシン

ステートマシンでは、カレンダーの状態を取得(GetCalendarState)し、カレンダーの状態を確認(CalendarStateChoice)します。
状態がOPENであればLambdaを実行し、CLOSEDであればステートマシンを終了します。
ステートマシン-1.PNG

・Lambda
Lambda1.PNG

実行結果を確認してみた

2022/06/03に実行されたステートマシンの結果を確認すると、カレンダーはOPEN状態のため、Lambdaが実行されていることがわかります。

ステートマシン-2.PNG

次に、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

SSMカレンダー-2.PNG

ステートマシンの結果を確認すると、カレンダーはCLOSED状態のため、Lambda実行がスキップされていることがわかります。
ステートマシン3.PNG

おわりに

今回はSystems Manager Change Calendarを使用して、平日のみ起動するLambdaを作成しました。
EventBridgeのcron設定による定期実行だけでは実現できないパターンに対応できるので、実行制御の幅が広がりそうです。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
2
Help us understand the problem. What are the problem?