👋

AWS Config を AWS Organizations 配下に CloudFormation でデプロイ

2023/11/07に公開

概要

AWS Config は、AWS アカウントに作成したリソースの変更履歴を記録し、コンプライアンス要件などのルールに適合しているか継続的に監視するサービスです。Organizations 環境では、複数の AWS アカウントに対してまとめて適用したり、記録を集約することが多いと思いますが、様々なパターンについて CloudFormation で展開する方法を整理してみました。

複数のアカウントに一括適用するには CloudFormation StackSets を使用すると便利です。
参考) AWS CloudFormation StackSet を AWS Organizations 配下に AWS CLI でデプロイ

AWS Config に割り当てる権限は、IAM ロールを設定する代わりにプリセットされた Service-linked role を使用することもできます。その場合、保存先 S3 バケット、送信先 SNS トピックや EventBrigde のリソースポリシーにサービスプリンシパルからの書き込みを許可する設定をおこなう形になりますが、アクセス元を制限するにはアカウント ID をすべて列挙する必要があり Organization ID 指定でまとめて制限することができないようです。そのため、ここではそのパターンについては記述しません。

構成例

単一アカウント構成

リソースの変更履歴を保存する S3 バケット、通知する SNS トピック、および EventBridge を使用して条件に一致するイベント(コンプライアンス違反検知など)を通知する SNS トピックを同一アカウント内に作成します。

単一アカウント構成

  • ConfigBucket : リソースの変更履歴を保存する S3 バケット (必須)
  • ConfigTopic : リソースの変更を通知する SNS トピック (OPTIONAL)
  • ConfigAlertTopic : 特定のイベント(コンプライアンス違反検知など)を通知する SNS トピック (OPTIONAL)
    AWS Chatbot から Subscribe すれば Slack に以下のような通知をおこなうことができます。
    Slack 通知

マルチアカウント構成 (別アカウントに保存・通知)

リソースの変更履歴を保存する S3 バケット、通知する SNS トピック、および EventBridge を使用して条件に一致するイベント(コンプライアンス違反検知など)を通知する SNS トピックは、それぞれ別のアカウントに作成することもできます。Organizations 内の複数のメンバーアカウントの情報を一元的に保存、通知したい場合に有効です。

別アカウントに保存する

デプロイ前に

先に管理コンソールから AWS Config を有効にしたことがある場合、記録を停止しても設定が残っていてデプロイに失敗してしまいます。AWS CLI で以下のコマンドを実行して削除しておきましょう。

aws configservice stop-configuration-recorder --configuration-recorder-name default
aws configservice delete-delivery-channel --delivery-channel-name default
aws configservice delete-configuration-recorder --configuration-recorder-name default

テンプレートパターン

基本

単一アカウントによる AWS Config の最小設定です。リソースの変更履歴を保存する S3 バケットを設定します。

単一アカウント構成 - S3

Configuration Recorder (ConfigRecorder) がアカウント内のリソース情報を取得します。全てのリソースを読み取るための権限 AWS_ConfigRole ポリシーをアタッチしたロール (ConfigRecorderRole) を作成し、付与します。管理コンソールから有効化する場合はサービス用にプリセットされた Service-linked role を使用することもできますが、権限としてはこれと同等のものです。S3 バケットへの書き込み権限もこのロールに追加します。

  # リソースの変更履歴を保存する S3 バケット
  ConfigBucket:
    DeletionPolicy: Retain
    Type: AWS::S3::Bucket

  # Configuration Recorder
  ConfigRecorderRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - config.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        # Configuration Recorder 用の管理ポリシーをアタッチ
        - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWS_ConfigRole"
      Policies:
        # S3 バケットへの書き込み権限
        - PolicyName: RecorderPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - s3:PutObject
                Resource:
                  - !Sub "${ConfigBucket.Arn}/*"
  ConfigRecorder:
    Type: AWS::Config::ConfigurationRecorder
    Properties:
      RoleARN: !GetAtt ConfigRecorderRole.Arn
      RecordingGroup:
        AllSupported: True
        IncludeGlobalResourceTypes: False

SNS トピックへの通知

SNS トピックにもリソースの変更を通知します。

単一アカウント構成 - SNS

SNS トピック (ConfigTopic) を追加して Delivery Channel (ConfigDeliveryChannel) に SnsTopicARN プロパティを追記します。IAM ロール (ConfigRecorderRole) に SNS トピックへ通知する権限も追加します。

  # 通知先 SNS トピック
  ConfigTopic:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: AWS Config Topic

  # Configuration Recorder
  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"
      Policies:
        - PolicyName: RecorderPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - s3:PutObject
                Resource:
                  - !Sub "${ConfigBucket.Arn}/*"
              # SNS トピックへの通知権限を追加
              - Effect: Allow
                Action:
                  - sns:Publish
                Resource:
                  - !Ref ConfigTopic
  # Delivery Channel
  ConfigDeliveryChannel:
    Type: AWS::Config::DeliveryChannel
    Properties:
      ConfigSnapshotDeliveryProperties:
        DeliveryFrequency: TwentyFour_Hours
      S3BucketName: !Ref ConfigBucket
      SnsTopicARN: !Ref ConfigTopic  # 通知先 SNS トピックを追加

ルールを設定

Config Rule を追加して、リソースの変更を監視します。

以下は s3-bucket-public-write-prohibited マネージドルールの場合の例です。ルールによって設定は異なりますので、それぞれ確認してください。
参考) AWS Config マネージドルールのリスト

  CheckS3BucketPublicWriteProhibited:
    Type: AWS::Config::ConfigRule
    Properties:
      MaximumExecutionFrequency: TwentyFour_Hours
      Scope:
        ComplianceResourceTypes:
          - AWS::S3::Bucket
      Source:
        Owner: AWS
        SourceIdentifier: S3_BUCKET_PUBLIC_WRITE_PROHIBITED

イベントを SNS トピックへ通知

Config Rule により検出された非準拠イベントを通知するには、Event Bridge を使用します。

単一アカウント構成 - Alert

通知先の SNS トピック (ConfigAlertTopic) を作成し、Amazon EventBridge ルール (NonCompliantEventsRule) に通知条件を設定します。SNS トピックのアクセスポリシー (ConfigAlertTopicPolicy) には EventBridge からの通知を許可します。

  # Config Rule による非準拠イベントを通知する EventBridge Rule
  NonCompliantEventsRule:
    Type: AWS::Events::Rule
    Properties:
      # 通知するイベントを指定
      EventPattern:
        source:
          - aws.config
        detail-type:
          - Config Rules Compliance Change
        detail:
          messageType:
            - ComplianceChangeNotification
          newEvaluationResult:
            complianceType:
              - NON_COMPLIANT
      State: ENABLED
      Targets:
        - Id: ConfigAlertTopic
          Arn: !Ref ConfigAlertTopic  # 通知先 SNS トピック

  # イベントを通知する SNS トピック
  ConfigAlertTopic:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: AWS Config Alert Topic
  ConfigAlertTopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
      Topics:
        - !Ref ConfigAlertTopic
      PolicyDocument:
        Statement:
          - Action:
              - sns:Publish
            Effect: Allow
            Resource: !Ref ConfigAlertTopic
            Principal:
              Service:
                - events.amazonaws.com  # Amazon EventBridge からの通知を許可

別アカウントの S3 バケットに記録

リソースの変更履歴を、別の AWS アカウントの S3 バケットに保存します。

マルチアカウント構成 - S3

送信先のアカウントに S3 バケット (ConfigBucket) を作成します。バケットポリシー (ConfigBucketPolicy) に送信元の Organization ID を指定して許可します。
参考) Amazon S3 バケットへのアクセス許可を AWS Config に与える

  ConfigBucket:
    DeletionPolicy: Retain
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub "config-bucket-${AWS::AccountId}"
  ConfigBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: (バケット名)
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: AWSConfigBucketPermissionsCheck
            Effect: Allow
            Action: s3:GetBucketAcl
            Resource:
              - !Sub "arn:${AWS::Partition}:s3:::${ConfigBucket}"
            Principal:
              AWS: "*"
            Condition:
              StringEquals:
                aws:PrincipalOrgID: (Organization ID)
          - Sid: AWSConfigBucketExistenceCheck
            Effect: Allow
            Action: s3:ListBucket
            Resource:
              - !Sub "arn:${AWS::Partition}:s3:::${ConfigBucket}"
            Principal:
              AWS: "*"
            Condition:
              StringEquals:
                aws:PrincipalOrgID: (Organization ID)
          - Sid: AWSConfigBucketDelivery
            Effect: Allow
            Action: s3:PutObject
            Resource:
              - !Sub "arn:${AWS::Partition}:s3:::${ConfigBucket}/*"
            Principal:
              AWS: "*"
            Condition:
              StringEquals:
                s3:x-amz-acl: bucket-owner-full-control
                aws:PrincipalOrgID: (Organization ID)

アカウント ID を指定して制限する場合は Principal で指定します。

            Principal:
              AWS:
                - (アカウント ID)
                - (アカウント ID)

送信元アカウントから、保存先としてこの S3 バケット名を指定します。

  # Configuration Recorder
  ConfigRecorderRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - config.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      Policies:
        # 別アカウントの S3 バケットへの書き込みを許可
        - PolicyName: RecorderPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - s3:PutObject
                Resource:
                  - !Sub "arn:${AWS::Partition}:s3:::(バケット名)/*"
      ManagedPolicyArns:
        - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWS_ConfigRole"
  # Delivery Channel
  ConfigDeliveryChannel:
    Type: AWS::Config::DeliveryChannel
    Properties:
      ConfigSnapshotDeliveryProperties:
        DeliveryFrequency: TwentyFour_Hours
      # 保存先に別アカウントの S3 バケットを指定
      S3BucketName: (バケット名)

別アカウントの SNS トピックへ通知

変更を別の AWS アカウントの SNS トピックに通知します。SNS トピックは送信元と同じリージョンに作成する必要があります。

マルチアカウント構成 - SNS

送信先のアカウントに SNS トピック (ConfigTopic) を作成します。アクセスポリシー (ConfigTopicPolicy) に送信元の Organization ID を指定して許可します。
参考) Amazon SNS トピックへのアクセス許可

  ConfigTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: (トピック名)
      DisplayName: AWS Config Topic
  ConfigTopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
      Topics:
        - !Ref ConfigTopic
      PolicyDocument:
        Statement:
          - Sid: AWSConfigSNSPolicy
            Action:
              - sns:Publish
            Effect: Allow
            Resource:
              - !Ref ConfigTopic
            Principal:
              AWS: "*"
            Condition:
              StringEquals:
                aws:PrincipalOrgID: (Organization ID)

アカウント ID を指定して制限する場合は Principal で指定します。

            Principal:
              AWS:
                - (アカウント ID)
                - (アカウント ID)

送信元アカウントから、通知先としてこの SNS トピックの ARN を指定します。

  # Configuration Recorder
  ConfigRecorderRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - config.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: RecorderPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - s3:PutObject
                Resource:
                  - !Sub "arn:${AWS::Partition}:s3:::(バケット名)/*"
              # 別アカウントの SNS トピックへの通知を許可
              - Effect: Allow
                Action:
                  - sns:Publish
                Resource:
                  - !Sub "arn:${AWS::Partition}:sns:${AWS::Region}:(送信先アカウント ID):(SNS トピック名)"
      ManagedPolicyArns:
        - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWS_ConfigRole"
  # Delivery Channel
  ConfigDeliveryChannel:
    Type: AWS::Config::DeliveryChannel
    Properties:
      ConfigSnapshotDeliveryProperties:
        DeliveryFrequency: TwentyFour_Hours
      S3BucketName: (バケット名)
      # 通知先に別アカウントの SNS トピックを指定
      SnsTopicARN: !Sub "arn:${AWS::Partition}:sns:${AWS::Region}:(送信先アカウント ID):(SNS トピック名)"

イベントを別アカウントの SNS トピックへ通知

Config Rule により検出された非準拠イベントを、別の AWS アカウントの SNS トピックに通知します。Event Bridge から直接送信することはできないため、EventBus を中継します。EventBus は送信元とリージョンが異なっていても問題ありません。Event Rule は送信元、送信先アカウントの両方に定義します。この例では同じ条件を指定しています。

マルチアカウント構成 - Alert

送信先のアカウントに EventBus (ConfigEventBus) を作成します。EventBus のリソースポリシー (ConfigEventBusPolicy) に送信元の Organization ID を指定して許可します。

  ConfigEventBus:
    Type: AWS::Events::EventBus
    Properties:
      Name: (イベントバス名)
  ConfigEventBusPolicy:
    Type: AWS::Events::EventBusPolicy
    Properties:
      EventBusName: !GetAtt ConfigEventBus.Name
      Statement:
        Sid: ConfigEventBusPolicy
        Action:
          - events:PutEvents
        Effect: Allow
        Resource: !GetAtt ConfigEventBus.Arn
        Principal:
          AWS: "*"
        Condition:
          StringEquals:
            aws:PrincipalOrgID: !Ref OrganizationId
      StatementId: ConfigEventBusPolicy

  ConfigAlertTopic:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: AWS Config Alert Topic
  ConfigAlertTopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
      Topics:
        - !Ref ConfigAlertTopic
      PolicyDocument:
        Statement:
          - Action:
              - sns:Publish
            Effect: Allow
            Resource: !Ref ConfigAlertTopic
            Principal:
              Service:
                - events.amazonaws.com

  NonCompliantEventsRule:
    Type: AWS::Events::Rule
    Properties:
      EventBusName: !GetAtt ConfigEventBus.Name
      EventPattern:
        source:
          - aws.config
        detail-type:
          - Config Rules Compliance Change
        detail:
          messageType:
            - ComplianceChangeNotification
          newEvaluationResult:
            complianceType:
              - NON_COMPLIANT
      State: ENABLED
      Targets:
        - Id: NonCompliantNotification
          Arn: !Ref ConfigAlertTopic

送信元アカウントから、通知先としてこの EventBus を指定します。

  NonCompliantEventsRuleRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - events.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: PutEventsPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: PutEvents
                Action:
                  - events:PutEvents
                Effect: Allow
                Resource:
                  # 別アカウントの EventBus への送信を許可
                  - !Sub "arn:${AWS::Partition}:events:(送信先リージョン):(送信先アカウント ID):event-bus/(イベントバス名)"
      Path: /
  NonCompliantEventsRule:
    Type: AWS::Events::Rule
    Properties:
      EventPattern:
        source:
          - aws.config
        detail-type:
          - Config Rules Compliance Change
        detail:
          messageType:
            - ComplianceChangeNotification
          newEvaluationResult:
            complianceType:
              - NON_COMPLIANT
      State: ENABLED
      Targets:
        # イベントを EventBus へ送信する
        - Id: ConfigAlertTopic
          Arn: !Sub "arn:${AWS::Partition}:events:(送信先リージョン):(送信先アカウント ID):event-bus/(イベントバス名)"
          RoleArn: !GetAtt NonCompliantEventsRuleRole.Arn

Discussion