Organizations配下のマルチアカウント環境でStackSetsを使ってConfigを設定したメモ。
参考リンク
- CloudFormation StackSets でAWS Organization 管理下のアカウントのすべてのリージョンにAWS Config を設定して記録を単一バケットに集約するサンプル
- AWS Organizations とAWS CloudFormation StackSets の連携が強化された
- Organization内のAWS ConfigをCloudFormation StackSetsで一気に設定する
方針
今回はAWS Landing Zoneのような、以下のようなアカウント構成を想定する。
- マスターアカウント
- 共通サービスアカウント
- 監査アカウント
- ログアカウント
- リソースアカウント
CloudFormationのユーザーガイドにあるサンプルテンプレートだと、CloudTrailやConfig用のバケットは各アカウントのリージョン毎に作成しそうにみえるが、今回はログアカウントにバケットを集約したい。
バケットの作成
ログアカウントにバケットを作成する。必要なアクセス許可は以下を参考にする。暗号化は今回は行わない。
テンプレートを作成する。
AWSTemplateFormatVersion: 2010-09-09 Description: Create S3 Bucket for Config Parameters: ConfigBucketName: Type: String OrganizationId: Type: String Resources: ConfigBucket: DeletionPolicy: Retain Type: AWS::S3::Bucket Properties: BucketName: !Ref ConfigBucketName ConfigBucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref ConfigBucket PolicyDocument: Version: 2012-10-17 Statement: - Sid: AWSConfigBucketPermissionsCheck Effect: Allow Principal: Service: - config.amazonaws.com Action: s3:GetBucketAcl Resource: - !Sub "arn:aws:s3:::${ConfigBucket}" - Sid: AWSConfigBucketExistenceCheck Effect: Allow Principal: Service: - config.amazonaws.com Action: s3:ListBucket Resource: - !Sub "arn:aws:s3:::${ConfigBucket}" - Sid: AWSConfigBucketDelivery Effect: Allow Principal: Service: - config.amazonaws.com Action: s3:PutObject Resource: - !Sub "arn:aws:s3:::${ConfigBucket}/AWSLogs/*" Condition: StringEquals: s3:x-amz-acl: "bucket-owner-full-control" aws:PrincipalOrgID: [ !Ref 'OrganizationId' ]
パラメータファイルも作成する。
[ { "ParameterKey": "ConfigBucketName", "ParameterValue": "config-bucket-o-hogehoge" }, { "ParameterKey": "OrganizationId", "ParameterValue": "o-hogehoge" } ]
ログアカウントでCloudFormationを実行してバケットを作成する。リージョンはap-northeast-1にする。
STACK_NAME=ConfigBucketStack TEMPLATE_FILE=config-bucket.yaml PARAMETER_FILE=config-bucket.parameter.json aws cloudformation create-stack --stack-name ${STACK_NAME} \ --template-body file://${TEMPLATE_FILE} \ --parameters file://${PARAMETER_FILE} \ --region ap-northeast-1 aws cloudformation wait stack-create-complete --stack-name ${STACK_NAME} --region ap-northeast-1
OrganizationsのStackSetsの有効化
StackSetsをために必要なアクセス権限のセットアップを行う。以下2つの方法があるが、後者のOrganizationsを使ったやり方の方が簡単。
上記はCloudFormationコンソールで設定するやり方で、Organizationsコンソールでやる方法は以下に記載がある。
マスターアカウントのOrganizationsコンソールでStackSetsの信頼されたアクセスを有効にする。
Configの有効化
Condigに与えるロールは「サービスにリンクされたロール」を使う。もしバケットをカスタマーキーで暗号化する場合はキーポリシーでこのサービスロールに許可を与える必要があると思われる。
テンプレートを用意する。
cat <<"EOF" > enable-config.yaml AWSTemplateFormatVersion: 2010-09-09 Description: Enable AWS Config Parameters: ConfigBucketName: Type: String OrganizationId: Type: String IncludeGlobalResourceTypeRegion: Type: String Default: ap-northeast-1 Conditions: IsIncludeGlobalResourceTypeRegion: !Equals [ !Ref IncludeGlobalResourceTypeRegion, !Ref "AWS::Region" ] Resources: ServiceLinkedRoleForConfig: Type: AWS::IAM::ServiceLinkedRole DeletionPolicy: Retain Properties: AWSServiceName: config.amazonaws.com Description: Allows Config to call AWS services and collect resource configurations on your behalf. ConfigRecorder: Type: AWS::Config::ConfigurationRecorder Properties: Name: default RoleARN: !Sub arn:aws:iam::${AWS::AccountId}:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig RecordingGroup: AllSupported: true IncludeGlobalResourceTypes: !If [IsIncludeGlobalResourceTypeRegion, true, false] ConfigDeliveryChannel: Type: AWS::Config::DeliveryChannel Properties: Name: default S3BucketName: !Ref ConfigBucketName EOF
StackSetsの作成はGUIで行う。マスターアカウントで作業する。リージョンはap-northeast-1から実行する。
CloudFormationコンソールのStacksSetsメニューから、「StackSetsの作成」をクリックする。
上記テンプレートをアップロードする。
名前やテンプレートに与えるパラメータなどを指定する。
リージョンは東京とバージニア北部にする。高速にデプロイするため並列数と失敗を許可する数を増やす。
最後に内容を確認してデプロイする。
上手くいけばスタックインスタンスが全て緑になる。既にCondigが有効になっていたリージョンでは失敗したので、設定を削除してからStackSetをアップデートして再デプロイした。
なお、この方法では、マスターアカウント自体に対してはStackをデプロイできない模様。
追記
当初サービスにリンクされたロールを使っていたが、自動デプロイが上手くいかないことが多く、サンプルテンプレートのようにリージョン毎に個別にロールを作るようにしたが、結果としては変わらず新規でデプロイ時はInsufficientDeliveryPolicyException
となってしまう。
AWSTemplateFormatVersion: 2010-09-09 Description: Enable AWS Config Parameters: ConfigBucketName: Type: String OrganizationId: Type: String IncludeGlobalResourceTypeRegion: Type: String Default: ap-northeast-1 Conditions: IsIncludeGlobalResourceTypeRegion: !Equals [ !Ref IncludeGlobalResourceTypeRegion, !Ref "AWS::Region" ] Resources: ConfigRecorderRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - config.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWS_ConfigRole" ConfigRecorder: Type: AWS::Config::ConfigurationRecorder Properties: Name: default RoleARN: !GetAtt ConfigRecorderRole.Arn RecordingGroup: AllSupported: true IncludeGlobalResourceTypes: !If [IsIncludeGlobalResourceTypeRegion, true, false] ConfigDeliveryChannel: Type: AWS::Config::DeliveryChannel Properties: Name: default S3BucketName: !Ref ConfigBucketName
バケットポリシーに組織IDを条件キーとして設定していたが、これを外すと上手くいった。
AWSTemplateFormatVersion: 2010-09-09 Description: Create S3 Bucket for Config Parameters: ConfigBucketName: Type: String OrganizationId: Type: String Resources: ConfigBucket: DeletionPolicy: Retain Type: AWS::S3::Bucket Properties: BucketName: !Ref ConfigBucketName ConfigBucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref ConfigBucket PolicyDocument: Version: 2012-10-17 Statement: - Sid: AWSConfigBucketPermissionsCheck Effect: Allow Principal: Service: - config.amazonaws.com Action: s3:GetBucketAcl Resource: - !Sub "arn:aws:s3:::${ConfigBucket}" - Sid: AWSConfigBucketExistenceCheck Effect: Allow Principal: Service: - config.amazonaws.com Action: s3:ListBucket Resource: - !Sub "arn:aws:s3:::${ConfigBucket}" - Sid: AWSConfigBucketDelivery Effect: Allow Principal: Service: - config.amazonaws.com Action: s3:PutObject Resource: - !Sub "arn:aws:s3:::${ConfigBucket}/AWSLogs/*" Condition: StringEquals: s3:x-amz-acl: "bucket-owner-full-control"