CloudWatch Alarmでアラーム発生時にStep Functionsステートマシンを起動する

CloudWatch Alarmでアラーム発生時にStep Functionsステートマシンを起動する

2021.11.08

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

こんにちは、CX事業本部 IoT事業部の若槻です。

今回は、CloudWatch Alarmでアラーム発生時にStep Functionsステートマシンを起動する方法を確認してみました。

ステートマシンはどこから起動できるか

まず、なるべく簡潔な構成にするという前提で、アラーム発生時にステートマシンをどのようにして起動できるかを確認してみます。

CloudWatch Alarmの場合

まずCloudWatch Alarmからステートマシンを直接起動できるか。

CloudWatch Alarmのアクションの設定を見ると、ターゲットとしてSNS Topicしか指定できないようです。

ドキュメントを見る限りでもアラームアクションへのSNS Topic以外のサービスの指定については記述がありません。

よってCloudWatch Alarmからはステートマシンの直接起動はできないようです。

SNS Topicの場合

続いてCloudWatch Alarmのアクションの唯一の発行先となるSNS Topicからは起動できるのか。

Topicのサブスクリプションの設定を確認すると、サブスクライブするエンドポイントのプロトコルは以下が指定可能となっています。ステートマシンは一覧にありません。

  • HTTP/HTTPS
  • Email/Email-JSON
  • Amazon Kinesis Data Firehose
  • Amazon SQS
  • AWS Lambda
  • プラットフォームアプリケーションエンドポイント
  • SMS

このうち聞き慣れないものとしてプラットフォームアプリケーションエンドポイントがありますが、こちらはモバイルアプリケーションのエンドポイントに通知を送信する際に使用するものとのことです。

なのでステートマシンのArnを指定しようとしてもエラーとなります。

考えられる構成

よって現時点で考えられる、アラーム発生時にステートマシンを起動させる最も簡単な構成は以下となりそうです。(※)

※2022/5 追記:Amazon EventBridgeを使用した構成であればさらに簡略化できました。記事末尾に詳細記載しています。

実装

上記構成を実装してみます。

CDKコード

lib/aws-cdk-app-stack.ts
import * as cdk from '@aws-cdk/core';
import * as sfn from '@aws-cdk/aws-stepfunctions';
import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
import * as cloudwatchActions from '@aws-cdk/aws-cloudwatch-actions';
import * as sns from '@aws-cdk/aws-sns';
import * as lambda from '@aws-cdk/aws-lambda';
import * as lambdaNodejs from '@aws-cdk/aws-lambda-nodejs';
import { SnsEventSource } from '@aws-cdk/aws-lambda-event-sources';
import * as iam from '@aws-cdk/aws-iam';

export class AwsCdkAppStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    //SNS Topic
    const cloudwatchAlarmNotifyTopic = new sns.Topic(
      this,
      'cloudwatchAlarmNotifyTopic'
    );

    //CloudWatch Alarm
    const sampleAlarm = new cloudwatch.Alarm(this, 'sampleAlarm', {
      alarmName: 'sampleAlarm',
      metric: new cloudwatch.Metric({
        namespace: 'StateMachinePublish',
        metricName: 'temperature',
      }),
      evaluationPeriods: 1,
      threshold: 5,
      statistic: cloudwatch.Statistic.MAXIMUM,
      comparisonOperator:
        cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD,
      period: cdk.Duration.minutes(3),
    });

    //Alarmアクション追加
    sampleAlarm.addAlarmAction(
      new cloudwatchActions.SnsAction(cloudwatchAlarmNotifyTopic)
    );

    //ステートマシン
    const stateMachine = new sfn.StateMachine(this, 'sampleStateMachine', {
      stateMachineName: 'sampleStateMachine',
      definition: new sfn.Pass(this, 'pass', {}),
    });

    //Lambda関数
    const startStateMachineFunc = new lambdaNodejs.NodejsFunction(
      this,
      'startStateMachineFunc',
      {
        functionName: 'startStateMachineFunc',
        entry: 'src/lambda/startStateMachineHandler.ts',
        handler: 'handler',
        runtime: lambda.Runtime.NODEJS_14_X,
        environment: {
          STATE_MACHINE_ARN: stateMachine.stateMachineArn,
        },
      }
    );

    //ステートマシン起動権限付与
    startStateMachineFunc.addToRolePolicy(
      new iam.PolicyStatement({
        actions: ['states:StartExecution'],
        resources: [stateMachine.stateMachineArn],
      })
    );

    //Lambdaイベントソース追加
    startStateMachineFunc.addEventSource(
      new SnsEventSource(cloudwatchAlarmNotifyTopic)
    );
  }
}

Lambdaコード

src/lambda/startStateMachineHandler.ts
import * as AWS from 'aws-sdk';

const STATE_MACHINE_ARN = process.env.STATE_MACHINE_ARN!;

const sfn = new AWS.StepFunctions();

export const handler = async () => {
  await sfn
    .startExecution({
      stateMachineArn: STATE_MACHINE_ARN,
    })
    .promise();
};

動作

監視対象のメトリクスがしきい値を下回り、CloudWatch Alarmがアラーム状態となりました。

するとLambda実行を介してステートマシンがちゃんと起動しました。

まとめ

  • CloudWatch Alarmのアラーム発生時にステートマシンを起動したい場合、現在取りうる最も簡潔な構成は、CloudWatch Alarm -> SNS topic -> Lambda関数 -> ステートマシンとなる。
  • せめてSNS topicからステートマシンを直接起動できるようになると嬉しい。

2022/5 追記:Amazon EventBridgeを使用した方が良い場合もある

Amazon EventBridgeのRuleを使用するとさらに構成を簡略化することができました。

<div class="mxgraph" style="max-width:100%;border:1px solid transparent;" data-mxgraph="{"highlight":"#0000ff","nav":true,"resize":true,"toolbar":"zoom layers tags lightbox","edit":"_blank","xml":"<mxfile scale=&quot;1.5&quot; border=&quot;0&quot;><diagram id=&quot;wlil6eIq3ImAZShQtSz2&quot; name=&quot;Page-1&quot;><mxGraphModel dx=&quot;904&quot; dy=&quot;1075&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;850&quot; pageHeight=&quot;1100&quot; math=&quot;0&quot; shadow=&quot;0&quot;><root><mxCell id=&quot;0&quot;/><mxCell id=&quot;1&quot; parent=&quot;0&quot;/><mxCell id=&quot;2&quot; value=&quot;AWS Cloud&quot; style=&quot;sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_aws_cloud;strokeColor=#858B94;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#858B94;dashed=0;&quot; parent=&quot;1&quot; vertex=&quot;1&quot;><mxGeometry x=&quot;110&quot; y=&quot;146&quot; width=&quot;610&quot; height=&quot;250&quot; as=&quot;geometry&quot;/></mxCell><mxCell id=&quot;7&quot; value=&quot;&quot; style=&quot;edgeStyle=none;html=1;&quot; parent=&quot;1&quot; source=&quot;3&quot; target=&quot;5&quot; edge=&quot;1&quot;><mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;/></mxCell><mxCell id=&quot;3&quot; value=&quot;&quot; style=&quot;sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;fillColor=#B0084D;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.alarm;&quot; parent=&quot;1&quot; vertex=&quot;1&quot;><mxGeometry x=&quot;170&quot; y=&quot;216&quot; width=&quot;78&quot; height=&quot;78&quot; as=&quot;geometry&quot;/></mxCell><mxCell id=&quot;4&quot; value=&quot;CloudWatch Alarm&quot; style=&quot;text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;&quot; parent=&quot;1&quot; vertex=&quot;1&quot;><mxGeometry x=&quot;154&quot; y=&quot;306&quot; width=&quot;110&quot; height=&quot;20&quot; as=&quot;geometry&quot;/></mxCell><mxCell id=&quot;12&quot; value=&quot;&quot; style=&quot;edgeStyle=none;html=1;&quot; parent=&quot;1&quot; source=&quot;5&quot; target=&quot;9&quot; edge=&quot;1&quot;><mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;/></mxCell><mxCell id=&quot;5&quot; value=&quot;&quot; style=&quot;sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#FF4F8B;gradientDirection=north;fillColor=#BC1356;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.eventbridge;&quot; parent=&quot;1&quot; vertex=&quot;1&quot;><mxGeometry x=&quot;360&quot; y=&quot;216&quot; width=&quot;78&quot; height=&quot;78&quot; as=&quot;geometry&quot;/></mxCell><mxCell id=&quot;8&quot; value=&quot;EventBridge Rule&quot; style=&quot;text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;&quot; parent=&quot;1&quot; vertex=&quot;1&quot;><mxGeometry x=&quot;344&quot; y=&quot;306&quot; width=&quot;110&quot; height=&quot;20&quot; as=&quot;geometry&quot;/></mxCell><mxCell id=&quot;9&quot; value=&quot;&quot; style=&quot;sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#FF4F8B;gradientDirection=north;fillColor=#BC1356;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.step_functions;&quot; parent=&quot;1&quot; vertex=&quot;1&quot;><mxGeometry x=&quot;550&quot; y=&quot;216&quot; width=&quot;78&quot; height=&quot;78&quot; as=&quot;geometry&quot;/></mxCell><mxCell id=&quot;11&quot; value=&quot;Step Functions&lt;br&gt;State Machine&quot; style=&quot;text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;&quot; parent=&quot;1&quot; vertex=&quot;1&quot;><mxGeometry x=&quot;544&quot; y=&quot;301&quot; width=&quot;90&quot; height=&quot;30&quot; as=&quot;geometry&quot;/></mxCell></root></mxGraphModel></diagram></mxfile>"}"></div>
<script type="text/javascript" src="https://viewer.diagrams.net/js/viewer-static.min.js"></script>

ただし注意点として、EventBridge Ruleからステートマシンを起動する場合は任意の実行名を指定できません。指定したい場合は本記事で紹介した方法の方が良いかと思います。

参考

以上

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事