AWS Organizations の CloudFormation StackSets で AWS Config を有効化し別アカウントへログエクスポート

2020年8月8日

概要

やりたいこと

Organizations 配下の複数アカウントに対して、 Organizations の CloudFormation StackSets で利用して、 OU 単位で Config を有効化する。

単一アカウントの全リージョンの Config 有効化は以前行った。

クロスアカウントアクセスは以下で行った。

AWS Organizations

複数のAWSアカウントを管理できるサービス。

セキュリティ監視する場合 「アカウント数 × リージョン数」 が監視対象となるため、設定範囲が大量になる。

Organizations を利用すれば、全アカウントに共通の設定を適用できるため便利。

OU (Organizational Unit) と呼ばれるアカウントのグルーピング機能が存在し、グループごとに設定適用が可能。

Organizations の CloudFormation StackSets

各アカウントへの設定適用には CloudFormation StackSets を利用する。

CloudFormation StackSets には、自身のアカウントへの適用(セルフマネージド型)と、 Organizations を利用した他アカウントへの適用(サービスマネージド型)の、2種類が存在する。

セルフマネージド型の StackSets を利用する場合、 IAM Role を StackSetsリソースを作成するアカウント、StackSets を利用して設定変更されるアカウント両方で作成する必要がある。

しかし、サービス型マネージド型 StackSets の場合は、これが不要である。

CloudFormation

  • アカウント A : CloudFormation StackSets を実行する Organizations アカウント
  • アカウント B : Config を有効化するメンバーアカウント

アカウント A : Organizationsアカウント

信頼されたアクセスを有効にする

Organizations と CloudFormation StackSets の連携を許可する。

Organizations コンソールより「設定」、アクセスの有効化をクリックする。

こうなれば連携完了。

S3 バケット 作成

test-config-organizations3 バケットを作成する。

AWS 公式のサンプルテンプレート を流用する。

AWSTemplateFormatVersion: 2010-09-09
Description: Create S3 bucket for AWS Config

Parameters:
  ConfigBucket:
    Type: String
  OrganizationId:
    Type: String

Resources:

  Bucket:
    DeletionPolicy: Retain
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref ConfigBucket

  ConfigBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref Bucket
      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: AWSConfigBucketDelivery
            Effect: Allow
            Principal:
              Service:
                - config.amazonaws.com
            Action: s3:PutObject
            Resource:
              - !Sub "arn:aws:s3:::${ConfigBucket}/${OrganizationId}/AWSLogs/*/Config/*"
            Condition:
              StringEquals:
                "s3:x-amz-acl": "bucket-owner-full-control"

デプロイ。

aws --profile organizations-test-lac cloudformation deploy --stack-name config-bucket --template-file config_s3.yaml --parameter-overrides ConfigBucket=test-config-organizations3 OrganizationId=o-xxxxxxxxxxxx

アカウント B : メンバーアカウント

Config 有効化用 CloudFormation テンプレート 解説

AWS 公式のサンプルテンプレートは少し冗長なので、クラウドワークスのブログのテンプレートを参考にさせていただきました。

AWSTemplateFormatVersion: 2010-09-09
Description: Enable AWS Config
 
Parameters:
  ConfigBucket:
    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:
      RoleName: !Sub "config-role-${AWS::Region}"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - config.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSConfigRole
 
  ConfigRecorder:
    Type: AWS::Config::ConfigurationRecorder
    DependsOn: ConfigRecorderRole
    DeletionPolicy: Delete
    Properties:
      RoleARN: !GetAtt ConfigRecorderRole.Arn
      Name: !Sub "configuration-recorder-${AWS::Region}"
      RecordingGroup:
        AllSupported: true
        IncludeGlobalResourceTypes: !If [IsIncludeGlobalResourceTypeRegion, true, false]
 
  ConfigDeliveryChannel:
    Type: AWS::Config::DeliveryChannel
    DependsOn: ConfigRecorderRole
    DeletionPolicy: Delete
    Properties:
      Name: !Sub "delivery-channel-${AWS::Region}"
      ConfigSnapshotDeliveryProperties:
        DeliveryFrequency: One_Hour
      S3BucketName: !Ref ConfigBucket
      S3KeyPrefix: !Ref OrganizationId

このコードのキモは、 IncludeGlobalResourceTypeRegion パラメータで、グローバルリージョンのリソース変更履歴を一つのリージョンだけで記録するように絞っていること。
全リージョンでグローバルリージョンを記録する Config を設定すると、重複したログが発生してしまうため。

Parameters:
  IncludeGlobalResourceTypeRegion:
    Type: String
    Default: ap-northeast-1

〜省略〜

Conditions:
  IsIncludeGlobalResourceTypeRegion: !Equals [ !Ref IncludeGlobalResourceTypeRegion, !Ref "AWS::Region" ]

〜省略〜

 ConfigRecorder:
    Type: AWS::Config::ConfigurationRecorder
    DeletionPolicy: Delete
    Properties:
      Name: !Sub "configuration-recorder-${AWS::Region}"
      RoleARN: !GetAtt ConfigRecorderRole.Arn
      RecordingGroup:
        AllSupported: true
        IncludeGlobalResourceTypes: !If [IsIncludeGlobalResourceTypeRegion, true, false]

デフォルトの場合( ap-northeast-1 の時)、 IsIncludeGlobalResourceTypeRegion が true になり、IncludeGlobalResourceTypes も true となり、グローバルリージョンの記録は有効となる。
それ以外のときは false となり、グローバルリージョンの記録はされなくなる。

OU を指定して StackSets でデプロイ

cloudformation cli から デプロイする。

create-stack-set コマンドでサービスマネージド型のスタックを作成する。

aws --profile organizations-test-lac cloudformation create-stack-set \
--stack-set-name config-organizations \
--template-body file://config.yaml \
--parameters ParameterKey=ConfigBucket,ParameterValue=test-config-organizations3 ParameterKey=OrganizationId,ParameterValue=o-xxxxxxxx \
--capabilities CAPABILITY_NAMED_IAM \
--permission-model SERVICE_MANAGED \
--auto-deployment Enabled=true,RetainStacksOnAccountRemoval=true

以下のようにスタックが作成される。

続いて create-stack-instances でデプロイ先の OU と region を指定する。

aws --profile organizations-test-lac cloudformation create-stack-instances \
--stack-set-name config-organizations \
--deployment-targets OrganizationalUnitIds="ou-xxxx-xxxxxxxx" \
--regions '["ap-northeast-1","ap-northeast-2", "ap-south-1", "ap-southeast-1", "ap-southeast-2", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "eu-west-3", "sa-east-1", "us-east-1", "us-east-2", "us-west-1", "us-west-2"]' \
--operation-preferences FailureToleranceCount=0,MaxConcurrentCount=1

OU 配下のアカウント× region 分が登録される。

以下2つが実現できていること。

  • 指定した OU 配下のアカウントでのみ Config が有効になっていること
  • s3 にログがエクスポートされていること

エラー

ResourceLogicalId:ConfigDeliveryChannel, ResourceType:AWS::Config::DeliveryChannel, ResourceStatusReason:Insufficient delivery policy to s3 bucket: test-config-organizations3, unable to write to bucket, provided s3 key prefix is ‘null’.

s3バケットへの配信ポリシーが不十分です:s3キープレフィックスが「null」の場合、test-config-organizations3バケットに書き込めません。

下記でも同じエラーが報告。

解決方法が示されていた。

  1. Amazon S3 バケットポリシーを確認し、その後に config.amazonaws.com サービスがターゲットバケットへの書き込みを許可していることを確認してください。
  2. IAM エンティティのアクセス許可を確認し、AWS Config のフルアクセスポリシーを使用します。
  3. IAM エンティティに s3:GetBucketAcl バケットと s3:PutObject* バケットへの書き込み許可があることを確認します。

今回は、バケットポリシーではS3 prefix を付けていた(${OrganizationId)が、

  ConfigBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref Bucket
      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: AWSConfigBucketDelivery
            Effect: Allow
            Principal:
              Service:
                - config.amazonaws.com
            Action: s3:PutObject
            Resource:
              - !Sub "arn:aws:s3:::${ConfigBucket}/${OrganizationId}/AWSLogs/*/Config/*"

デリバリーチャンネルの方には指定していなかった 。

  ConfigDeliveryChannel:
    Type: AWS::Config::DeliveryChannel
    DependsOn: ConfigRecorderRole
    DeletionPolicy: Delete
    Properties:
      Name: !Sub "delivery-channel-${AWS::Region}"
      ConfigSnapshotDeliveryProperties:
        DeliveryFrequency: One_Hour
      S3BucketName: !Ref ConfigBucket

最後の行に指定したら上手くいった。

  ConfigDeliveryChannel:
    Type: AWS::Config::DeliveryChannel
    DependsOn: ConfigRecorderRole
    DeletionPolicy: Delete
    Properties:
      Name: !Sub "delivery-channel-${AWS::Region}"
      ConfigSnapshotDeliveryProperties:
        DeliveryFrequency: One_Hour
      S3BucketName: !Ref ConfigBucket
      S3KeyPrefix: !Ref OrganizationId

参考

Organization内のAWS ConfigをCloudFormation StackSetsで一気に設定する

CloudFormation StackSets でAWS Organization 管理下のアカウントのすべてのリージョンにAWS Config を設定して記録を単一バケットに集約するサンプル

AWS Organizations とAWS CloudFormation StackSets の連携が強化された