AWS Organizations の CloudFormation StackSets で AWS Config を有効化

2021年4月29日

概要

やりたいこと

マネジメントアカウント(旧組織マスターアカウント)配下の AWS アカウントに対して、 Organizations の CloudFormation StackSets で利用して、 OU (Organizations Unit) 単位で Config を有効化する。

つまり、自分のアカウント以外の AWS アカウントに対して CloudFormation を実行する。

自分のアカウントに対してのみ CloudFormation StackSets を利用する場合(セルフマネージド型)は以下。

AWS Organizations

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

今回は、OU (Organizational Unit) と呼ばれるアカウントのグルーピング機能が存在し、後述する CloudFormation StackSets が OU ごとに設定適用する。

CloudFormation 以外の AWS サービスにも権限の移譲などを行える。

Organizations の CloudFormation StackSets

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

セルフマネージド型の CloudFormation StackSets は、 IAM Role を実行管理アカウントとターゲットアカウントに作成しておく必要がある。

  • 実行管理アカウント : AWSCloudFormationStackSetAdministrationRole
  • ターゲットアカウント : AWSCloudFormationStackSetExecutionRole

しかし、サービス型マネージド型 StackSets の場合は、この IAM Role の用意は不要となる。

CloudFormation StackSets

前提:Organizations でアカウント B が所属する OU を作成していること。

  • アカウント A : CloudFormation StackSets を実行するマネジメントアカウント
  • アカウント 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  cloudformation deploy \
--profile organizations-test-lac \
--stack-name config-bucket \
--template-file config_s3.yaml \
--parameter-overrides ConfigBucket=test-config-organizations3 OrganizationId=o-xxxxxxxxxxxx

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

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

クラウドワークスのブログのテンプレートを参考にさせていただきました。

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 パラメータで、グローバルリソースの変更履歴を東京リージョンだけで記録するように絞っていること。
全リージョンでグローバルリソースの変更履歴を記録すると、重複したログが発生してしまうため。

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 となり、グローバルリソースの記録はされなくなる。

CloudFormation StackSets CLI でデプロイ

cloudformation cli から デプロイする。

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

aws cloudformation create-stack-set \
--profile organizations-test-lac \
--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 cloudformation create-stack-instances \
--profile organizations-test-lac \
--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 の連携が強化された