Cloud FormationでIoT Analyticsのワークフローの設定を行う方法
AWS IoT Analytics(Analyticsと略す)の設定をCloud Formationで行う際にいろいろ躓いたポイントがあった為、備忘録として書いています。(2019/04/21現在)
この記事にたどり着いた方であれば各サービスの詳細は理解されていると思いますので、サービスの説明は行いません。
結論
この記事を必要としている人はすぐにでも結論が欲しいと思いますので、先に結論のYAMLデータを記載します。
AWSTemplateFormatVersion: 2010-09-09
Resources:
AnalyticsRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: iot.amazonaws.com
Action: 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/CloudWatchFullAccess'
- 'arn:aws:iam::aws:policy/CloudWatchLogsFullAccess'
MaxSessionDuration: 3600
Path: /
RoleName: AnalyticsRole
Metadata:
'AWS::CloudFormation::Designer':
id: 3a915d63-b53f-4014-8158-28ea71c94949
MyLambdaRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/CloudWatchFullAccess'
- 'arn:aws:iam::aws:policy/CloudWatchLogsFullAccess'
- 'arn:aws:iam::aws:policy/AWSIoTAnalyticsFullAccess'
- 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
- 'arn:aws:iam::aws:policy/AWSIoTFullAccess'
- 'arn:aws:iam::aws:policy/service-role/AWSLambdaRole'
MaxSessionDuration: 3600
Path: /
RoleName: MyLambdaRole
Metadata:
'AWS::CloudFormation::Designer':
id: 99ea8723-ae9a-4478-b6ef-f0ef1df73a36
MyChanel:
Type: 'AWS::IoTAnalytics::Channel'
Properties:
ChannelName: my_chanel
RetentionPeriod:
Unlimited: true
Metadata:
'AWS::CloudFormation::Designer':
id: accd8536-0d98-49ec-9cc3-43f6df5b3f79
MyDatastore:
Type: 'AWS::IoTAnalytics::Datastore'
Properties:
DatastoreName: my_datastore
RetentionPeriod:
Unlimited: true
Metadata:
'AWS::CloudFormation::Designer':
id: 18e1feef-36e9-48d9-886a-4b651891b3d9
MyFunction:
Type: 'AWS::Lambda::Function'
Properties:
Code:
ZipFile: |-
exports.handler = async (event) => {
console.log(event);
return event;
}
FunctionName: MyFunction
Handler: index.handler
MemorySize: 128
Role: !GetAtt
- MyLambdaRole
- Arn
Runtime: nodejs8.10
Timeout: 3
Metadata:
'AWS::CloudFormation::Designer':
id: cdf7bcde-8fb6-4c04-bee9-14dac73f45ac
MyPipeline:
Type: 'AWS::IoTAnalytics::Pipeline'
Properties:
PipelineName: my_pipeline
PipelineActivities:
- Channel:
ChannelName: !Ref MyChanel
Name: MyChanel
Next: MyFunction
- Lambda:
Name: MyFunction
Next: MyDatastore
BatchSize: 1
LambdaName: !Select
- '6'
- !Split
- ':'
- !GetAtt
- MyFunction
- Arn
- Datastore:
DatastoreName: !Ref MyDatastore
Name: MyDatastore
Metadata:
'AWS::CloudFormation::Designer':
id: 31475aa9-b3ab-48d5-a1e7-9d9a5a627a05
MyFunctionPermission:
Type: 'AWS::Lambda::Permission'
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !GetAtt
- MyFunction
- Arn
Principal: iotanalytics.amazonaws.com
Metadata:
'AWS::CloudFormation::Designer':
id: 53ad796c-fbed-45ff-bb41-ab8286537cc4
MyDataset:
Type: 'AWS::IoTAnalytics::Dataset'
Properties:
DatasetName: my_dataset
Actions:
- ActionName: MyDataset
QueryAction:
SqlQuery: !Join
- ' '
- - SELECT * FROM
- !Ref MyDatastore
RetentionPeriod:
Unlimited: true
NumberOfDays: 90
Triggers:
- Schedule:
ScheduleExpression: cron(0/15 * * * ? *)
Metadata:
'AWS::CloudFormation::Designer':
id: 5f53a313-5d81-42ef-b47d-655c6396191d
MyTopicRule:
Type: 'AWS::IoT::TopicRule'
Properties:
RuleName: MyTopicRule
TopicRulePayload:
Description: ''
RuleDisabled: false
AwsIotSqlVersion: 2016-03-23
Sql: SELECT * FROM my/topic
Actions:
- IotAnalytics:
ChannelName: !Ref MyChanel
RoleArn: !GetAtt
- AnalyticsRole
- Arn
Metadata:
'AWS::CloudFormation::Designer':
id: a2fe4fdf-6c0c-455a-adb6-6c983e675d2f
Metadata:
'AWS::CloudFormation::Designer':
18e1feef-36e9-48d9-886a-4b651891b3d9:
size:
width: 60
height: 60
position:
x: -210
'y': 360
z: 1
embeds: []
5f53a313-5d81-42ef-b47d-655c6396191d:
size:
width: 60
height: 60
position:
x: -210
'y': 260
z: 1
embeds: []
accd8536-0d98-49ec-9cc3-43f6df5b3f79:
size:
width: 60
height: 60
position:
x: -110
'y': 260
z: 1
embeds: []
99ea8723-ae9a-4478-b6ef-f0ef1df73a36:
size:
width: 60
height: 60
position:
x: -10
'y': 460
z: 1
embeds: []
cdf7bcde-8fb6-4c04-bee9-14dac73f45ac:
size:
width: 60
height: 60
position:
x: -110
'y': 460
z: 1
embeds: []
53ad796c-fbed-45ff-bb41-ab8286537cc4:
size:
width: 60
height: 60
position:
x: -210
'y': 460
z: 1
embeds: []
isassociatedwith:
- cdf7bcde-8fb6-4c04-bee9-14dac73f45ac
31475aa9-b3ab-48d5-a1e7-9d9a5a627a05:
size:
width: 60
height: 60
position:
x: -110
'y': 360
z: 1
embeds: []
3a915d63-b53f-4014-8158-28ea71c94949:
size:
width: 60
height: 60
position:
x: -10
'y': 360
z: 1
embeds: []
a2fe4fdf-6c0c-455a-adb6-6c983e675d2f:
size:
width: 60
height: 60
position:
x: -10
'y': 260
z: 1
embeds: []
ここまでのコードで次図の環境を構築することができます。
見える形で表示されると、なんかやる気でますよね。
作業手順
普通にAWS GUI上で作成を行った場合の手順は次の通りです。
- IAM Roleを作成
- IoT AnalyticsのChannelを作成
- IoT AnalyticsのDatastoreを作成
- IoT AnalyticsのPipeline用のLambdaを作成 ※1
- IoT AnalyticsのPipelineを作成 ※2
- IoT AnalyticsのPipelineがLambdaにPermissionを設定 ※1
- IoT AnalyticsのDatasetを作成
- IoTのルールエンジンを作成
※1 Topicでデータを受けた後そのままDataStoreに格納する場合は不要
※2 Lambdaを利用しない、修正する必要がある
躓いたポイント
Cloud Formationで躓いたポイントは3つありました。
WebのActivityとCloud FormationのActivityの不一致
最初にActivityをWeb上で設定するときの画面を見てみます。
Web上では、ActivityはLambdaの設定しかありません。
実際の設定を覗いてもActivityは1つだけになってます。
しかしYAMLデータを見ると、Pipelineで登録する3つの作業はActivityとして登録されています。
これが1個目の躓いたポイントでした。
公式リファレンスのActivityにはChannelとDatastoreは登場しているので、比較的納得のできる内容ではありましたが。。。
MyPipeline:
Type: 'AWS::IoTAnalytics::Pipeline'
Properties:
PipelineName: my_pipeline
PipelineActivities:
- Channel:
ChannelName: !Ref MyChanel
Name: MyChanel
Next: MyFunction
- Lambda:
Name: MyFunction
Next: MyDatastore
BatchSize: 1
LambdaName: !Select
- '6'
- !Split
- ':'
- !GetAtt
- MyFunction
- Arn
- Datastore:
DatastoreName: !Ref MyDatastore
Name: MyDatastore
PipelineのActivityには必須ではないパラメータは実質必須
次に、Channel、Lambda、Datastoreの共通パラメータを見てみましょう。
代表してChannelのAWS公式日本語リファレンス【Pipeline.Properties.PipelineActivities.Channel】をみてみましょう。
Name "チャネル" アクティビティの名前。 必須: いいえ タイプ: 文字列 更新に伴う要件: 中断はありません Next パイプライン内の次のアクティビティ。 必須: いいえ タイプ: 文字列 更新に伴う要件: 中断はありません
2019/04/21現在
配列で格納されているならば順番に呼び出されるだろうと勘違いをしていたため、この2つのプロパティに何の意味があるのかがわかりませんでした。
しかも2つとも必須ではないとされていたため余計に勘違いが加速してましたよね。。。
何なのかということですが、ただActivityを呼び出す順番の制御をしているだけだったのです。
せめて、「Name
とNext
はActivityの呼び出す順番を制御するために利用している。次のActivityがない場合Next
は記述しなくてもよい」ぐらいは書いてくれよ!!!一人逆ギレしていました。
Analytics Datasetの必須ではないパラメータは実は必須
2つ目の躓きポイントで慣れたので躓いたわけじゃないです、少しリファレンスと違うので書いてます。
まずは、DatasetのAWS公式日本語リファレンス【Dataset.Properties.RetentionPeriod】を見てみましょう。
NumberOfDays メッセージデータが保持される日数。"unlimited" パラメータは false であることが必要です。 必須: いいえ タイプ: 整数 更新に伴う要件: 中断はありません Unlimited true の場合、メッセージデータは無期限に保持されます。 必須: いいえ タイプ: ブール値 更新に伴う要件: 中断はありません
2019/04/21現在
これはDatasetに登録されたデータの保存期限を決めるパラメータ群です。
無期限で保存したかったのでUnlimited: true
にしてNumberOfDays
は記述しませんでした。
そうしたらCloudFormationに「NumberOfDays
は必須です」と突き返されてしまいました。。。
これには推測できていて、
このデフォルト値は90日期限となっています。
仮に無期限に設定したとしても、データとしてはデフォルトの期限情報が残っています。
なので、NumberOfDays
パラメータは必須パラメータとして登録されているのだと思います。
最後に
1つ目の躓いたポイントは私の学習不足だったり、読む力といったものが影響して勝手に躓いたものでしたので、何も言えないです。しかし2つ目の躓いたポイントは避けようがない。。。
皆さんもリファレンスの落とし穴に嵌らないようにお気を付けください。
実は他のリファレンスと合わせて読むと解決するのではないかと思ったりしています。
私ではそれを見つけることができなかったので、リファレンスを見つけられた方がいましたら教えていただけると助かります。
実はCloud Formationを利用して4日目なので、説明が足りなかったり、ミスがありましたらコメントにてご指摘ください。
(ネストされたスタックとやらをつかうとCloud Formationの共通部品を括りだすことができるらしい???次はこれを調べてみて、記事にできればと思ってます。)