構文
以下が公式ドキュメントに記載されている構文
Resources:
ProvisioningTemplate: # 任意の名前
Type: AWS::IoT::ProvisioningTemplate
Properties:
Description: String # 説明
Enabled: Boolean # フリートプロビジョニングテンプレート有効/無効
PreProvisioningHook: # プロビジョニング時にフックするLambda
ProvisioningHook
ProvisioningRoleArn: String # [必須] デバイスをプロビジョニングする権限を持ったロールARN
Tags: # フリートプロビジョニングテンプレートの管理に使用できるメタデータ
Tags
TemplateBody: String # [必須] フリートプロビジョニングテンプレートのJSON形式のコンテンツ(JSON)
TemplateName: String # テンプレート名
以下、実際に値を入れたもの
Resources:
ProvisioningTemplate:
Type: AWS::IoT::ProvisioningTemplate
Properties:
Enabled: true
ProvisioningRoleArn: !GetAtt ProvisioningRole.Arn
TemplateName: !Sub '${AWS::StackName}_Template'
TemplateBody: |
{
"Parameters": {
"SerialNumber": {
"Type": "String"
},
"AWS::IoT::Certificate::Id": {
"Type": "String"
}
},
"Resources": {
"certificate": {
"Properties": {
"CertificateId": {
"Ref": "AWS::IoT::Certificate::Id"
},
"Status": "Active"
},
"Type": "AWS::IoT::Certificate"
},
"policy": {
"Properties": {
"PolicyName": "xxxxxxxxxx5D8828xxxxxxxxxx52CExxxxxxxxxx"
},
"Type": "AWS::IoT::Policy"
},
"thing": {
"OverrideSettings": {
"AttributePayload": "MERGE",
"ThingGroups": "DO_NOTHING",
"ThingTypeName": "REPLACE"
},
"Properties": {
"AttributePayload": {},
"ThingGroups": [],
"ThingName": {
"Fn::Join": [
"",
[
"fleet-provisioning-test-",
{
"Ref": "SerialNumber"
}
]
]
},
"ThingTypeName": "fleet-provisioning-test"
},
"Type": "AWS::IoT::Thing"
}
}
}
TemplateBody
のJSONはマネージメントコンソールでフリートプロビジョニングテンプレートを作成し、生成されたテンプレートのJSONをコピペしたもの
プロビジョニングロールの定義
ProvisioningRoleArn
で参照しているロールは以下のように定義
ProvisioningRole:
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/service-role/AWSIoTThingsRegistration
ManagedPolicyArns
でAWS管理ポリシーのARNを指定
マネージメントコンソールでフリートプロビジョニングテンプレートを作成した場合に、自動で設定されるAWS管理ポリシーのARNを設定
Principal
で権限を与える対象としてiotを指定
プロビジョニング前にフックするLambdaの設定
公式ドキュメントより以下のような構文になる
Resources:
ProvisioningTemplate:
Type: AWS::IoT::ProvisioningTemplate
Properties:
PreProvisioningHook: # プロビジョニング時にフックするLambda
TargetArn: String # フックするLambdaのARN
ProvisioningRoleArn: String
TemplateBody: String
以下、実際に値を入れたもの
Resources:
ProvisioningTemplate:
Type: AWS::IoT::ProvisioningTemplate
Properties:
Enabled: true
PreProvisioningHook: # 追加
TargetArn: !GetAtt FleetProvisioningHook.Arn
ProvisioningRoleArn: !GetAtt ProvisioningRole.Arn
TemplateName: !Sub '${AWS::StackName}_Template'
TemplateBody: |
...
FleetProvisioningHook: # Lambda関数定義
Type: AWS::Serverless::Function
...
上記に加えて、定義したLambda関数を実行できる権限をIoTに与える必要があるため以下を追加
LambdaAddPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt FleetProvisioningHook.Arn # LambdaのARN
Principal: iot.amazonaws.com # iotからの実行を許可
プロビジョニングテンプレート(JSON)の内容変更
クレーム証明書の設定
(2020/11/05 追記)
下記"SerialNumber"
、"AWS::IoT::Certificate::Id"
はデバイスから受信する値のため"Default"
は設定不要
(2020/11/05 追記終了)
以下のようにJSON内"AWS::IoT::Certificate::Id"の
"Default"`に証明書IDを設定
"Parameters": {
"SerialNumber": {
"Type": "String"
},
"AWS::IoT::Certificate::Id": {
"Type": "String",
"Default": "xxxxxxxxxx8d192xxxxxxfb22xxxxxxxxx5b8f6d148cdaafb5506xxxxxxxxxx"
}
},
プロビジョニングしたデバイスに付与するポリシーの設定
公式ドキュメント(CloudFormationのポリシー定義方法について)
1.既にAWSに作成済ポリシーを使用
以下のようにポリシーのARNを指定
"policy": {
"Properties": {
"PolicyName": "xxxxxxxxxx5D8828xxxxxxxxxx52CExxxxxxxxxx"
},
"Type": "AWS::IoT::Policy"
},
2.新規でポリシーを作成
以下のようにPolicyDocument
でポリシーを定義する
"policy": {
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:*",
"Resource": [
"arn:aws:iot:ap-northeast-1:36xxxxxxxxxx:client/*",
"arn:aws:iot:ap-northeast-1:36xxxxxxxxxx:topic/*",
"arn:aws:iot:ap-northeast-1:36xxxxxxxxxx:topicfilter/*"
]
}
]
}
},
"Type": "AWS::IoT::Policy"
},
リージョン、アカウントIDに変数を使用してみる(失敗)
前述のポリシー定義ではリージョン(ap-northeast-1
)、アカウントID(36xxxxxxxxxx
)が変わった場合にテンプレートの変更が必要となるため
以下のように設定してみたが、PolicyDocumentの構文異常でデバイス登録時に失敗
"policy": {
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:*",
"Resource": [
{"Fn::Sub" : "arn:aws:iot:${AWS::Region}:${AWS::AccountId}:client/*"},
{"Fn::Sub" : "arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topic/*"},
{"Fn::Sub" : "arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topicfilter/*"}
]
}
]
}
},
以下のようにParameters
でリージョン、アカウントIDを定義した場合もParameters
には文字列以外設定できないためエラー
"Parameters": {
"AccountID" : {
"Type": "String",
"Default": {"Fn::Sub" : "${AWS::AccountId}"}
},
"Region" : {
"Type": "String",
"Default": {"Fn::Sub" : "${AWS::Region}"}
}
},
"Resources": {
"policy": {
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:*",
"Resource": [
{"Fn::Sub" : "arn:aws:iot:${Region}:${AccountID}:client/*"},
{"Fn::Sub" : "arn:aws:iot:${Region}:${AccountID}:topic/*"},
{"Fn::Sub" : "arn:aws:iot:${Region}:${AccountID}:topicfilter/*"}
]
}
]
}
},
"Type": "AWS::IoT::Policy"
}
}
公式ドキュメントでも「組み込み関数の Fn::Sub が含まれています。この組み込み関数により、検証エラーが発生します。「すべての Default メンバーは文字列でなければなりません」。」と記載があり、この方法は不可
リージョン、アカウントIDに変数を使用してみる(成功)(2020/11/03 追加)
プロビジョニングテンプレートの内容(TemplateBody
)をJSON形式文字列として、!Sub
関数を使って変数を埋め込むことで対応することができた
これによって、リージョン、アカウントIDが入力不要になった
Resources:
ProvisioningTemplate:
Type: AWS::IoT::ProvisioningTemplate
Properties:
Enabled: true
PreProvisioningHook:
TargetArn: !GetAtt FleetProvisioningHook.Arn
ProvisioningRoleArn: !GetAtt ProvisioningRole.Arn
TemplateName: !Sub '${StackName}_ProvisioningTemplate'
TemplateBody: !Sub |
{
"Parameters": {
"SerialNumber": {
"Type": "String"
},
"AWS::IoT::Certificate::Id": {
"Type": "String"
}
},
"Resources": {
"certificate": {
"Properties": {
"CertificateId": {
"Ref": "AWS::IoT::Certificate::Id"
},
"Status": "Active"
},
"Type": "AWS::IoT::Certificate"
},
"policy": {
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:*",
"Resource": [
"arn:aws:${AWS::Region}:${AWS::AccountId}:client/*",
"arn:aws:${AWS::Region}:${AWS::AccountId}:topic/*",
"arn:aws:${AWS::Region}:${AWS::AccountId}:topicfilter/*"
]
}
]
}
},
"Type": "AWS::IoT::Policy"
},
...
クレーム証明書にポリシーをアタッチする(2020/11/05 追記)
クレーム証明書の生成と、IoT Coreへの登録は手動で行うとして、
クレーム証明書に必要なポリシーのアタッチをCloudFormationで行う
手動でポリシーをアタッチした場合
以下のようにプロビジョニングテンプレートのアクションから「アクセス許可を編集」をクリック
以下のようにポリシーをアタッチする証明書を選択して、「ポリシーをアタッチ」をクリック
CloudFormationでポリシーをアタッチした場合
以下が、アタッチするポリシーと、ポリシーをアタッチするリソース定義
CertificateAttachPolicy:
Type: AWS::IoT::Policy
Properties:
PolicyName: !Sub '${StackName}_CertificateAttachPolicy'
PolicyDocument: !Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:Connect"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Publish",
"iot:Receive"
],
"Resource": [
"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topic/$aws/certificates/create/*",
"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topic/$aws/provisioning-templates/${StackName}_ProvisioningTemplate/provision/*"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Subscribe"
],
"Resource": [
"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topicfilter/$aws/certificates/create/*",
"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topicfilter/$aws/provisioning-templates/${StackName}_ProvisioningTemplate/provision/*"
]
}
]
}
PolicyAttachment:
Type: AWS::IoT::PolicyPrincipalAttachment
Properties:
PolicyName: !Ref CertificateAttachPolicy
Principal: !Sub "arn:aws:iot:${AWS::Region}:${AWS::AccountId}:cert/${CertificateId}"
CertificateAttachPolicy
がアタッチするポリシーリソース定義、
PolicyAttachment
がAWS IoT ポリシーをプリンシパル (X.509 証明書または別の証明書) にアタッチするためのリソース定義
PolicyAttachment
内で使用している${CertificateId}
は別途Parameters
セクションで定義が必要
CloudFormation(AWS::IoT::PolicyPrincipalAttachment
)を使用してアタッチした場合、
対象のスタックを削除した場合、自動的にデタッチされる
ポリシーのみCloudFormationで定義し、手動でポリシーをアタッチした場合、
アタッチされたポリシーがCloudFormationから操作できなくなりスタックの更新、削除でエラーとなるため注意
PolicyDocument
に${iot:Connection.Thing.ThingName}
を使用する(2020/11/05 追記)
公式ドキュメントにあるように、以下のような${iot:Connection.Thing.ThingName}
を使用したポリシー定義に変更する
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action":["iot:Publish"],
"Resource": ["arn:aws:iot:us-east-1:123456789012:topic/${iot:Connection.Thing.ThingName}"]
},
{
"Effect": "Allow",
"Action": ["iot:Connect"],
"Resource": ["arn:aws:iot:us-east-1:123456789012:client/${iot:Connection.Thing.ThingName}"]
}
]
}
前述した以下のプロビジョニングテンプレート定義内の"arn:aws:${AWS::Region}:${AWS::AccountId}:client/*"
部分を変更する
TemplateBody: !Sub |
{
"Parameters": {
"SerialNumber": {
"Type": "String"
},
"AWS::IoT::Certificate::Id": {
"Type": "String"
}
},
"Resources": {
"certificate": {
"Properties": {
"CertificateId": {
"Ref": "AWS::IoT::Certificate::Id"
},
"Status": "Active"
},
"Type": "AWS::IoT::Certificate"
},
"policy": {
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:*",
"Resource": [
"arn:aws:${AWS::Region}:${AWS::AccountId}:client/*", <= この"*"を変更する
"arn:aws:${AWS::Region}:${AWS::AccountId}:topic/*",
"arn:aws:${AWS::Region}:${AWS::AccountId}:topicfilter/*"
]
}
]
}
},
"Type": "AWS::IoT::Policy"
},
...
単純に
"arn:aws:${AWS::Region}:${AWS::AccountId}:client/${iot:Connection.Thing.ThingName}"
とした場合は、!Sub
を使用してるため、iot:Connection.Thing.ThingName
が見つからずエラーとなる
そのため、一旦以下のようにParameters
で${iot:Connection.Thing.ThingName}
を文字列として定義してから、
"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:client/${ConnectionThingName}"
とする
Parameters:
ConnectionThingName:
Type: String
Default: '${iot:Connection.Thing.ThingName}'
Resources:
ProvisioningTemplate:
Type: AWS::IoT::ProvisioningTemplate
Properties:
...
TemplateBody: !Sub |
{
"Parameters": {
"SerialNumber": {
"Type": "String"
},
"AWS::IoT::Certificate::Id": {
"Type": "String"
}
},
"Resources": {
"certificate": {
"Properties": {
"CertificateId": {
"Ref": "AWS::IoT::Certificate::Id"
},
"Status": "Active"
},
"Type": "AWS::IoT::Certificate"
},
"policy": {
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:*",
"Resource": [
"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:client/${ConnectionThingName}",
"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topic/*",
"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topicfilter/*"
]
}
]
}
},
"Type": "AWS::IoT::Policy"
},
"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:client/${ConnectionThingName}"
は、デプロイ後、以下のように変換される
"arn:aws:iot:ap-northeast-1:xxxxxxxxxxxx:client/${ConnectionThingName}"