はじめに
CloudFormationのResourceのPropertyの指定において、Typeが「Json」のものがいくつかあります。
IAM Roleの「AssumeRolePolicyDocument」や「Policies」などがこれに該当します。
しかしながら、IAM Entityに対して指定するPolicyドキュメントでCFnの関数を使いたいことが多く(PolicyドキュメントのResourceに、他のCFn Stackで作成したリソースを指定する、など)、JSONでは指定が難しいことも多々あります。
Typeが「Json」となっているProperyには、同等の内容をyamlとして定義して渡すことも可能です。
この仕様を利用して、PolicyドキュメントでCFn関数を指定することが可能です。
AWS::IAM::Role - AWS CloudFormation
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#cfn-iam-role-assumerolepolicydocument
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#cfn-iam-role-policies
AWS::IAM::Role Policy - AWS CloudFormation
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-policy.html#cfn-iam-policies-policydocument
IAM Role定義サンプル
以下の2つのCloudFormationテンプレート定義は等価です。
JSON形式でPolicyドキュメントを指定する
Resources:
# AWS::IAM::Role - AWS CloudFormation
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
SampleAdminRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "sts:AssumeRole"
}
]
}
Description:
Describe what you want
Policies:
- PolicyName: AllowAllActions
PolicyDocument:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}
yaml形式でPolicyドキュメントを指定する
AWSTemplateFormatVersion: '2010-09-09'
Description: Describe what you want.
Parameters:
# Define what you want.
Resources:
# AWS::IAM::Role - AWS CloudFormation
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
SampleAdminRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
AWS: "arn:aws:iam::123456789012:root"
Action: "sts:AssumeRole"
Description:
Describe what you want
Policies:
- PolicyName: AllowAllActions
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: "*"
Resource: "*"
AssumeRolePolicyDocumentのPrincipalでCFn関数を使う
上記のように、TypeとしてJsonが指定されているPropertyをyamlに変換したことで、CFn関数を利用しやすくなります。
先ほどの例ですと、AssumeRoleする主体となるPrincipalのAWSアカウントIDを、Parameterとして渡したくなると思います。
この場合、以下のように!Sub関数を利用することが可能です。
AWSTemplateFormatVersion: '2010-09-09'
Description: Describe what you want.
Parameters:
# Define what you want.
PrincipalAwsAccountId:
Description:
An AWS account id will be a principal to invoke the AssumeRole API.
Type: Number
Default: 123456789012
Resources:
# AWS::IAM::Role - AWS CloudFormation
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
SampleAdminRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
AWS: !Sub arn:aws:iam::${PrincipalAwsAccountId}:root
Action: "sts:AssumeRole"
Description:
Describe what you want
Policies:
- PolicyName: AllowAllActions
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: "*"
Resource: "*"
Fn::Sub - AWS CloudFormation
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html
CFnスタック作成時に、下記のようにPrincipalAwsAccountIdパラメータを指定することで、任意のAWSアカウントIDに対応することができるようになります。
$ aws cloudformation create-stack \
--stack-name iam-role-policy-with-function \
--template-body file://cfn-iam-role-policy-with-function.yml \
--capabilities CAPABILITY_NAMED_IAM \
--parameters ParameterKey=PrincipalAwsAccountId,ParameterValue=987654321098,UsePreviousValue=true
おわりに
CDKが出たので、今後はCDKを使うことでこのような用途はProgrammaticに解決できるようになると思います。
しかしながら、いきなり全て移行することは難しいため、このようなテクニックはまだ暫く必要になるでしょう。