CloudFormationでCloudWatchイベントの実行間隔をパラメータで指定する時にrate式で1を設定するためのConditionsを書いてみた

2018.03.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、臼田です。

みなさんLambdaしていますか?

今日はLambdaを導入する為に用意したCloudFormationで、特定のパラメータを指定すると失敗した経緯があり、回避策を吐き出そうと思ってブログにしました。

タイトルで何を言っているかわからない

多分そう思われる方も多いかもしれません。順に説明します。

まず、CloudWatchイベントではルールのスケジュール式の書き方にcron式とrate式がありますが、CloudFormationでは

AWS::Events::Ruleにてどちらも同じくScheduleExpressionとして文字列で指定する必要があります。

ここで問題になるのが、rate式ではrate(5 minutes)のようにrate(数値 単位)のフォーマットを取りますが、値が1の場合には単位が単数形になります。

1分間隔の場合にはrate(1 minutes)ではなくrate(1 minute)となります。's'がなくなります。英語的には適切な書き方ですが、この仕組はプログラマブルに処理したい時にはやや困ります。

CloudFormationではrate式をただの文字列として指定する必要があるため、数値をパラメータで取る場合で1を取りうる場合にはConditionsで条件関数を利用して判定し、処理を分ける必要があります。

というわけで、書いてみました。

Conditionsでrate式を変えるテンプレートの書き方

今回はLambdaを実行するためのCloudWatchイベントをrate式で作成する事を想定します。

パラメータの指定は以下のようになっています。

Parameters:
  INTERVAL:
    Description: "Enter the interval of get log cycle.(minutes, Max 180)"
    Type: Number
    Default: 60
    MaxValue: 180
    MinValue: 1

1分から最大180分で取ります。cron式で登録しようと思うと、分単位なら0 - 59までしか取れないので、1時間以上の時間をパラメータから取ろうと思ったら基本こうなると思います。

Conditionsは以下のようになります。

Conditions: 
 IsSingular: !Equals [ !Ref INTERVAL, 1 ]
 IsPlural: !Not [ !Equals [ !Ref INTERVAL, 1 ] ]

INTERVAL1なら単数形にする必要があるので、その成否で取ります。

Conditionsの結果はtrueならリソースを作成する, falseならしないの判断しかできないので、2つのConditionsが必要になります。

実際のリソースでは下記のようにConditionsを設定します。(Lambdaのリソースは省略します)

Resources:
  ScheduledRule: 
    Type: "AWS::Events::Rule"
    Condition: IsPlural
    Properties: 
      Description: "ScheduledRule"
      ScheduleExpression: !Join [ "", [ "rate(", Ref: INTERVAL, " minutes)" ] ]
      State: "ENABLED"
      Targets: 
        - Arn: !GetAtt SampleLambda.Arn
          Id: "SampleLambda"
  PermissionForEventsToInvokeLambda: 
    Type: "AWS::Lambda::Permission"
    Condition: IsPlural
    Properties: 
      FunctionName: 
        Ref: "SampleLambda"
      Action: "lambda:InvokeFunction"
      Principal: "events.amazonaws.com"
      SourceArn: !GetAtt ScheduledRule.Arn
# If INTERVAL is 1, set rate to minute (not minute's'!).
  ScheduledRuleSingular: 
    Type: "AWS::Events::Rule"
    Condition: IsSingular
    Properties: 
      Description: "ScheduledRule"
      ScheduleExpression: !Join [ "", [ "rate(", Ref: INTERVAL, " minute)" ] ]
      State: "ENABLED"
      Targets: 
        - Arn: !GetAtt SampleLambda.Arn
          Id: "SampleLambda"
  PermissionForEventsToInvokeLambdaSingular: 
    Type: "AWS::Lambda::Permission"
    Condition: IsSingular
    Properties: 
      FunctionName: 
        Ref: "SampleLambda"
      Action: "lambda:InvokeFunction"
      Principal: "events.amazonaws.com"
      SourceArn: !GetAtt ScheduledRuleSingular.Arn

まず、作成するAWS::Events::Ruleリソースが2つになります。そして、必ずどちらかのConditionsを指定します。

そして、Lambdaを実行する権限のリソースがAWS::Events::Ruleを指しているので、こちらも同じようにConditionsを指定して2つ作成します。それぞれ指定するSourceArnも変わります。

以上でCloudFormationのパラメータでCloudWatchイベントで単数形、複数形両方の値に対応することができます。

おわりに

rate式を利用する時に単位が変わってしまうのはやや使いづらいですが、上記を利用して対応しましょう。

何方かのお役に立てれば幸いです。