この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS OrganizationsがCloudFormation(以下CFn)をサポートしました。
以下リソースをCFnで管理できるようになりました。
- AWSアカウント
- organizational units (OUs)
- ポリシー
- 4種類あります。代表的なのはSCPですね。
- SCP (Service control policy)
- Artificial Intelligence (AI) services opt-out policy
- Backup policy
- Tag policy
- 4種類あります。代表的なのはSCPですね。
それぞれ使ってみます。
Organization作成
最初にそもそものOraganizationsを作成する必要があります。この部分はCFn未対応です。なので、以下ページを参考にマネジメントコンソールかCLIで作成しましょう。お使いのアカウントがOraganizationのマネジメントアカウントになります。
※ TerraformだとこのOrganization作成部分もIaC化できます。
ルート直下にアカウントを作成する
以下テンプレートでCFnスタックを作成してみます。Oraganizationのルート直下に1つアカウントを作成します。ルート直下なのでParentIds
プロパティにはr-
から始まるルートIDを設定します。(後から知ったのですが、ルート直下にアカウントを作成する場合はこのParentIds
プロパティは省略可能だそうです)
AWSTemplateFormatVersion: 2010-09-09
Description: AWS Organizations supports CloudFormations
Resources:
TestAccountMadeWithCfn:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn
Email: sample+made-with-cfn@gmail.com
ParentIds:
- r-xxxx
made-with-cfn
アカウントが無事作成できました。(他に以前作成したOUがあります。)
複数AWSアカウントの同時作成する
アカウント作成に使うCFnリソースAWS::Organizations::Account
のドキュメントに以下記述があります。
If you include multiple accounts in a single template, you must use the DependsOn attribute on each account resource type so that the accounts are created sequentially. If you create multiple accounts at the same time, Organizations returns an error and the stack operation fails.
複数アカウントを同時に作成するとエラーになるよ、だからDependsOn
を使って順次作成してね、とのことです。本当にエラーになるのか、以下のようにDependsOn
を使わずに2アカウント追加してみました。
AWSTemplateFormatVersion: 2010-09-09
Description: AWS Organizations supports CloudFormations
Resources:
TestAccountMadeWithCfn:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn
Email: sample+made-with-cfn@gmail.com
ParentIds:
- r-xxxx
+ MultipleCreation1:
+ Type: AWS::Organizations::Account
+ Properties:
+ AccountName: made-with-cfn2
+ Email: sample+made-with-cfn2@gmail.com
+ ParentIds:
+ - r-xxxx
+ MultipleCreation2:
+ Type: AWS::Organizations::Account
+ Properties:
+ AccountName: made-with-cfn3
+ Email: sample+made-with-cfn3@gmail.com
+ ParentIds:
+ - r-xxxx
予想に反してこのスタック更新は成功しました。CreateAccount APIのリファレンスには
Using CreateAccount to create multiple temporary accounts isn't recommended.
と「おすすめしない」とだけ書かれていたので、必ず複数同時作成が失敗するわけでも無いようですね。とはいえ本番運用する際には念の為DependsOn
句を書いておいた方が良さそうです。
OUを作成する
OUに関してはParentId
プロパティは必須項目で、省略はできません。
AWSTemplateFormatVersion: 2010-09-09
Description: AWS Organizations supports CloudFormations
Resources:
TestAccountMadeWithCfn:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn
Email: sample+made-with-cfn@gmail.com
ParentIds:
- r-xxxx
MultipleCreation1:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn2
Email: sample+made-with-cfn2@gmail.com
ParentIds:
- r-xxxx
MultipleCreation2:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn3
Email: sample+made-with-cfn3@gmail.com
ParentIds:
- r-xxxx
+ TestOu:
+ Type: AWS::Organizations::OrganizationalUnit
+ Properties:
+ Name: test-ou
+ ParentId: r-xxxx
作成成功しました。
OU配下にアカウントを移動する
OUリソースの論理名をRef
するとOUのIDが取得できます。
AWSTemplateFormatVersion: 2010-09-09
Description: AWS Organizations supports CloudFormations
Resources:
TestAccountMadeWithCfn:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn
Email: sample+made-with-cfn@gmail.com
ParentIds:
+ - !Ref TestOu
- - r-xxxx
MultipleCreation1:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn2
Email: sample+made-with-cfn2@gmail.com
ParentIds:
- r-xxxx
MultipleCreation2:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn3
Email: sample+made-with-cfn3@gmail.com
ParentIds:
- r-xxxx
TestOu:
Type: AWS::Organizations::OrganizationalUnit
Properties:
Name: test-ou
ParentId: r-xxxx
SCPを作成する
前述の通りポリシーは4種類ありますが、ここでは一番良く使うであろうSCPを作成してみたいと思います。以下ドキュメントを参考に、特定のタグを付与していない場合はEC2インスタンス作成が失敗するようにしてみます。
AWS::Organizations::Account
のRefを使ってみたかったので、ターゲットはOraganization全体でもOUでもなくアカウントにしています。
AWSTemplateFormatVersion: 2010-09-09
Description: AWS Organizations supports CloudFormations
Resources:
TestAccountMadeWithCfn:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn
Email: sample+made-with-cfn@gmail.com
ParentIds:
- !Ref TestOu
MultipleCreation1:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn2
Email: sample+made-with-cfn2@gmail.com
ParentIds:
- r-xxxx
MultipleCreation2:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn3
Email: sample+made-with-cfn3@gmail.com
ParentIds:
- r-xxxx
TestOu:
Type: AWS::Organizations::OrganizationalUnit
Properties:
Name: test-ou
ParentId: r-xxxx
+ TestSCP:
+ Type: AWS::Organizations::Policy
+ Properties:
+ Type: SERVICE_CONTROL_POLICY
+ TargetIds:
+ - !Ref TestAccountMadeWithCfn
+ Name: test-scp
+ Description: Require project tag to create EC2 instances
+ Content: |
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "DenyRunInstanceWithNoProjectTag",
+ "Effect": "Deny",
+ "Action": "ec2:RunInstances",
+ "Resource": [
+ "arn:aws:ec2:*:*:instance/*",
+ "arn:aws:ec2:*:*:volume/*"
+ ],
+ "Condition": {
+ "Null": {
+ "aws:RequestTag/Project": "true"
+ }
+ }
+ }
+ ]
+ }
こちらのテンプレートでスタック更新したところ、以下エラーになりました。
Resource handler returned message: "This operation can be performed only for enabled policy types. (Service: Organizations, Status Code: 400, Request ID: xxxx)" (RequestToken: yyyy, HandlerErrorCode: InvalidRequest)
調べたところ、ポリシーは4種類それぞれオプトインする必要があるんですね。Organizationsのコンソールで確認したところ、どれも有効化されていませんでした。
SCPを有効化して、再度スタック更新を実行したところ成功しました。Organizationsコンソールでポリシーが作成できていることも確認できました。
ターゲットもアカウント1つだけです。
SCPをテストしてみます。このアカウントにログインしてEC2インスタンスをProjectタグ無しで作成してみました。
狙い通りエラーになりました。
エラーメッセージがエンコードされていて内容がわかりません。以下ドキュメントを参考にエラーメッセージをデコードしてみました。
実行したコマンド
aws sts decode-authorization-message --encoded-message (コンソールからエンコードメッセージをコピペ) | jq .DecodedMessage --raw-output | jq .
コマンド結果
{
"allowed": false,
"explicitDeny": true,
"matchedStatements": {
"items": [
{
"statementId": "DenyRunInstanceWithNoProjectTag",
"effect": "DENY",
"principals": {
"items": [
{
"value": "AROAWWZIGWOTQSBVXYNIX"
}
]
},
"principalGroups": {
"items": []
},
"actions": {
"items": [
{
"value": "ec2:RunInstances"
}
]
},
"resources": {
"items": [
{
"value": "arn:aws:ec2:*:*:instance/*"
},
{
"value": "arn:aws:ec2:*:*:volume/*"
}
]
},
"conditions": {
"items": [
{
"key": "aws:RequestTag/Project",
"values": {
"items": [
{
"value": "true"
}
]
}
}
]
}
}
]
},
"failures": {
"items": []
},
"context": {
"principal": {
"id": "AROAWWZIGWOTQSBVXYNIX:xxxxxuser",
"arn": "arn:aws:sts::012345678901:assumed-role/AWSReservedSSO_AdministratorAccess_132ab909823e04ea/xxxxxuser"
},
"action": "ec2:RunInstances",
"resource": "arn:aws:ec2:ap-northeast-1:012345678901:instance/*",
"conditions": {
"items": [
{
"key": "ec2:InstanceMarketType",
"values": {
"items": [
{
"value": "on-demand"
}
]
}
},
{
"key": "aws:Resource",
"values": {
"items": [
{
"value": "instance/*"
}
]
}
},
{
"key": "aws:Account",
"values": {
"items": [
{
"value": "012345678901"
}
]
}
},
{
"key": "ec2:AvailabilityZone",
"values": {
"items": [
{
"value": "ap-northeast-1c"
}
]
}
},
{
"key": "ec2:ebsOptimized",
"values": {
"items": [
{
"value": "false"
}
]
}
},
{
"key": "ec2:IsLaunchTemplateResource",
"values": {
"items": [
{
"value": "false"
}
]
}
},
{
"key": "ec2:InstanceType",
"values": {
"items": [
{
"value": "t2.micro"
}
]
}
},
{
"key": "ec2:RootDeviceType",
"values": {
"items": [
{
"value": "ebs"
}
]
}
},
{
"key": "aws:Region",
"values": {
"items": [
{
"value": "ap-northeast-1"
}
]
}
},
{
"key": "aws:Service",
"values": {
"items": [
{
"value": "ec2"
}
]
}
},
{
"key": "ec2:InstanceID",
"values": {
"items": [
{
"value": "*"
}
]
}
},
{
"key": "aws:Type",
"values": {
"items": [
{
"value": "instance"
}
]
}
},
{
"key": "ec2:Tenancy",
"values": {
"items": [
{
"value": "default"
}
]
}
},
{
"key": "ec2:Region",
"values": {
"items": [
{
"value": "ap-northeast-1"
}
]
}
},
{
"key": "aws:ARN",
"values": {
"items": [
{
"value": "arn:aws:ec2:ap-northeast-1:012345678901:instance/*"
}
]
}
}
]
}
}
}
設定したSCPで弾かれていることがわかりますね。
では今度はタグを付けて作成してみます。SCPに"value": "arn:aws:ec2:*:*:volume/*"
も書いていたのでボリュームにもタグ付けが必要です。
今度は成功しました!
アカウントを削除する
以下のようにAWS::Organizations::Account
リソースをテンプレートから削除して更新しても、アカウントは削除されずただCFn管理下から外れるだけです。
AWSTemplateFormatVersion: 2010-09-09
Description: AWS Organizations supports CloudFormations
Resources:
TestAccountMadeWithCfn:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn
Email: sample+made-with-cfn@gmail.com
ParentIds:
- !Ref TestOu
MultipleCreation1:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn2
Email: sample+made-with-cfn2@gmail.com
ParentIds:
- r-xxxx
- MultipleCreation2:
- Type: AWS::Organizations::Account
- Properties:
- AccountName: made-with-cfn3
- Email: sample+made-with-cfn3@gmail.com
- ParentIds:
- - r-xxxx
TestOu:
Type: AWS::Organizations::OrganizationalUnit
Properties:
Name: test-ou
ParentId: r-xxxx
TestSCP:
Type: AWS::Organizations::Policy
Properties:
Type: SERVICE_CONTROL_POLICY
TargetIds:
- !Ref TestAccountMadeWithCfn
Name: test-scp
Description: Require project tag to create EC2 instances
Content: |
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRunInstanceWithNoProjectTag",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": [
"arn:aws:ec2:*:*:instance/*",
"arn:aws:ec2:*:*:volume/*"
],
"Condition": {
"Null": {
"aws:RequestTag/Project": "true"
}
}
}
]
}
これはデフォルトのDeletionPolicy
値がRetain
だからです。アカウントを削除したい場合はDeletionPolicy
を明示的に指定する必要があります。
AWSTemplateFormatVersion: 2010-09-09
Description: AWS Organizations supports CloudFormations
Resources:
TestAccountMadeWithCfn:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn
Email: sample+made-with-cfn@gmail.com
ParentIds:
- !Ref TestOu
MultipleCreation1:
Type: AWS::Organizations::Account
+ DeletionPolicy: Delete
Properties:
AccountName: made-with-cfn2
Email: sample+made-with-cfn2@gmail.com
ParentIds:
- r-xxxx
TestOu:
Type: AWS::Organizations::OrganizationalUnit
Properties:
Name: test-ou
ParentId: r-xxxx
TestSCP:
Type: AWS::Organizations::Policy
Properties:
Type: SERVICE_CONTROL_POLICY
TargetIds:
- !Ref TestAccountMadeWithCfn
Name: test-scp
Description: Require project tag to create EC2 instances
Content: |
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRunInstanceWithNoProjectTag",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": [
"arn:aws:ec2:*:*:instance/*",
"arn:aws:ec2:*:*:volume/*"
],
"Condition": {
"Null": {
"aws:RequestTag/Project": "true"
}
}
}
]
}
ですが、この更新はエラーになりました。エラーメッセージは以下です。
The submitted information didn't contain changes. Submit different information to create a change set.
DeletionPolicy
追加だけでは差分として認識してくれないんですね…ぐぬぬ
以下エントリを参考にタグ追加も加えます。
AWSTemplateFormatVersion: 2010-09-09
Description: AWS Organizations supports CloudFormations
Resources:
TestAccountMadeWithCfn:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn
Email: sample+made-with-cfn@gmail.com
ParentIds:
- !Ref TestOu
MultipleCreation1:
Type: AWS::Organizations::Account
+ DeletionPolicy: Delete
Properties:
AccountName: made-with-cfn2
Email: sample+made-with-cfn2@gmail.com
ParentIds:
- r-xxxx
+ Tags:
+ - Key: WillBeDeleted
+ Value: True
TestOu:
Type: AWS::Organizations::OrganizationalUnit
Properties:
Name: test-ou
ParentId: r-xxxx
TestSCP:
Type: AWS::Organizations::Policy
Properties:
Type: SERVICE_CONTROL_POLICY
TargetIds:
- !Ref TestAccountMadeWithCfn
Name: test-scp
Description: Require project tag to create EC2 instances
Content: |
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRunInstanceWithNoProjectTag",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": [
"arn:aws:ec2:*:*:instance/*",
"arn:aws:ec2:*:*:volume/*"
],
"Condition": {
"Null": {
"aws:RequestTag/Project": "true"
}
}
}
]
}
今度はスタック更新成功しました。
あとは該当AWS::Organizations::Account
リソースを削除して再度スタック更新です。
AWSTemplateFormatVersion: 2010-09-09
Description: AWS Organizations supports CloudFormations
Resources:
TestAccountMadeWithCfn:
Type: AWS::Organizations::Account
Properties:
AccountName: made-with-cfn
Email: sample+made-with-cfn@gmail.com
ParentIds:
- !Ref TestOu
- MultipleCreation1:
- Type: AWS::Organizations::Account
- DeletionPolicy: Delete
- Properties:
- AccountName: made-with-cfn2
- Email: sample+made-with-cfn2@gmail.com
- ParentIds:
- - r-xxxx
- Tags:
- - Key: WillBeDeleted
- Value: True
TestOu:
Type: AWS::Organizations::OrganizationalUnit
Properties:
Name: test-ou
ParentId: r-xxxx
TestSCP:
Type: AWS::Organizations::Policy
Properties:
Type: SERVICE_CONTROL_POLICY
TargetIds:
- !Ref TestAccountMadeWithCfn
Name: test-scp
Description: Require project tag to create EC2 instances
Content: |
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRunInstanceWithNoProjectTag",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": [
"arn:aws:ec2:*:*:instance/*",
"arn:aws:ec2:*:*:volume/*"
],
"Condition": {
"Null": {
"aws:RequestTag/Project": "true"
}
}
}
]
}
アカウントは即削除されるわけではないのです。90日の猶予期間があります。この点については以下のエントリをご参照ください。
感想
だいぶ昔にTerraformでOrganizationsまわりを作成してみたことがあったので、「え、まだCFnはサポートしてなかったのか」というのが正直な感想でした。とはいえIaCの選択肢が増えるのは嬉しいですね。OUやSCPはマルチアカウントでAWSを使う際にとても影響範囲の広い重要なリソースですので、IaCで変更履歴を管理するのは重要だと思います。