Organizations配下のマルチアカウントでのAWS Config設定

Organizations配下のマルチアカウント環境でStackSetsを使ってConfigを設定したメモ。

参考リンク

方針

今回は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の信頼されたアクセスを有効にする。

f:id:sotoiwa:20200514214935p:plain

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の作成」をクリックする。

上記テンプレートをアップロードする。

f:id:sotoiwa:20200514214743p:plain

名前やテンプレートに与えるパラメータなどを指定する。

f:id:sotoiwa:20200514214759p:plain

f:id:sotoiwa:20200514214820p:plain

リージョンは東京とバージニア北部にする。高速にデプロイするため並列数と失敗を許可する数を増やす。

f:id:sotoiwa:20200514215818p:plain

f:id:sotoiwa:20200514214835p:plain

最後に内容を確認してデプロイする。

上手くいけばスタックインスタンスが全て緑になる。既にCondigが有効になっていたリージョンでは失敗したので、設定を削除してからStackSetをアップデートして再デプロイした。

f:id:sotoiwa:20200514214905p:plain

なお、この方法では、マスターアカウント自体に対しては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"