AWS IoT EventsをCloudFormationで作ってみた

AWS IoT EventsをCloudFormationで作ってみました。
2021.02.17

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

はじめに

いままで手動ポチポチでIoT Eventsを作っていました。 動作を試す実験だったので十分でしたが、案件で採用する可能性が高くなったため、IoT EventsをCloudFormationで作ってみました。

おすすめの方

  • AWS IoT EventsをCloudFormationで作成したい方

仕様を決める

デバイス

  • 水位情報をクラウドに送信する
  • デバイスが送信する水位情報は下記とする
    • 水位(メートル): waterLevel
    • 時刻(ミリ秒): timestamp
{
    "waterLevel": 10,
    "timestamp": 1601258079088
}

トピック

  • デバイスは下記トピックにデータを送信する
    • sample/<デバイスID>/waterLevel

クラウド(IoT Events)

  • しきい値は30とする
  • しきい値以上になった場合、SNSトピックを発行してメール送信する(水位が異常です)
  • しきい値を下回った場合、SNSトピックを発行してメール送信する(復旧しました)

IoT EventsをCloudFormationで作成する

CloudFormationテンプレート

下記を定義しています。

  • メール通知用のSNSトピック
  • IoTルール
  • IoTルール用のIAMロール
  • IoT Eventsの入力情報
  • IoT Eventsの探知器モデル
  • IoT Eventsの探知器モデル用のIAMロール

iot_events.yml

AWSTemplateFormatVersion: 2010-09-09
Description: IoT Events Sample

Resources:
  WaterLevelNotifyTopic:
    Type: AWS::SNS::Topic
    Properties:
      Subscription:
        - Endpoint: sample@example.com
          Protocol: email

  waterLevelTopicRule:
    Type: AWS::IoT::TopicRule
    Properties:
      RuleName: water_level_topic_rule
      TopicRulePayload:
        AwsIotSqlVersion: "2016-03-23"
        RuleDisabled: false
        Sql: >-
          SELECT
            topic(2) as deviceId, * as payload
          FROM
            'sample/+/waterLevel'
        Actions:
          - IotEvents:
              InputName: !Ref WaterLevelEventsInput
              RoleArn: !GetAtt WaterLevelTopicRuleRole.Arn

  WaterLevelTopicRuleRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: water-level-topic-rule-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: iot.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AWSIoTEventsFullAccess

  WaterLevelEventsInput:
    Type: AWS::IoTEvents::Input
    Properties:
      InputName: WaterLevelInputData
      InputDefinition:
        Attributes:
          - JsonPath: deviceId
          - JsonPath: payload.waterLevel
          - JsonPath: payload.timestamp

  WaterLevelDetectorModel:
    Type: AWS::IoTEvents::DetectorModel
    Properties:
      DetectorModelName: water-level-model
      Key: deviceId
      EvaluationMethod: BATCH
      RoleArn: !GetAtt WaterLevelEventsDetectorModelRole.Arn
      DetectorModelDefinition:
        InitialStateName: Normal
        States:
          - StateName: Normal
            OnInput:
              TransitionEvents:
                - EventName: to_warning
                  NextState: Warning
                  Condition: $input.WaterLevelInputData.payload.waterLevel >= 30
          - StateName: Warning
            OnEnter:
              Events:
                - EventName: send-mail-to-warning
                  Actions:
                    - Sns:
                        TargetArn: !Ref WaterLevelNotifyTopic
                        Payload:
                          Type: STRING
                          ContentExpression: "'水位の上昇を検知しました。対象デバイスID: ${$input.WaterLevelInputData.deviceId}, 水位: ${$input.WaterLevelInputData.payload.waterLevel}'"
            OnInput:
              TransitionEvents:
                - EventName: to_normal
                  NextState: Normal
                  Condition: $input.WaterLevelInputData.payload.waterLevel < 30
            OnExit:
              Events:
                - EventName: send-mail-to-normal
                  Actions:
                    - Sns:
                        TargetArn: !Ref WaterLevelNotifyTopic
                        Payload:
                          Type: STRING
                          ContentExpression: "'水位が復旧しました。対象デバイスID: ${$input.WaterLevelInputData.deviceId}, 水位: ${$input.WaterLevelInputData.payload.waterLevel}'"


  WaterLevelEventsDetectorModelRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: water-level-detector-model-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: iotevents.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AWSIoTEventsFullAccess
        - arn:aws:iam::aws:policy/AmazonSNSFullAccess

デプロイ

aws cloudformation deploy \
    --template-file iot_events.yml \
    --stack-name IoT-Events-Sample-Stack \
    --capabilities CAPABILITY_NAMED_IAM \
    --no-fail-on-empty-changeset

デプロイされた探知器モデルを確認する

予定通りにできました。

IoT Eventsの探知器モデルの様子

動作確認

waterLevel=10を送信すると、Normal状態になる

  • トピック: sample/d0001/waterLevel
{
    "waterLevel": 10,
    "timestamp": 1601258079088
}

Normal状態になりました。

Normal状態になった

waterLevel=30を送信すると、Warning状態になってメールが来る

  • トピック: sample/d0001/waterLevel
{
    "waterLevel": 30,
    "timestamp": 1601258079088
}

Warning状態になりました。

Warning状態になった

水位の上昇を知らせるメールも来ています。

メールが来た

waterLevel=29を送信すると、Normal状態になって復旧メールが来る

  • トピック: sample/d0001/waterLevel
{
    "waterLevel": 29,
    "timestamp": 1601258079088
}

Normal状態になりました。

Normal状態になった

復旧メールも来ています。

メールが来た

さいごに

いろんなところでハマりました。最初からCloudFormationで作るのは大変なので、手動で作成してからエクスポートを行い、その内容を参考にすると便利でした。どなたかの参考になれば幸いです。

参考