CloudFormation StackSets CLI で AWS Config を有効化する

2020年7月12日

概要

やりたいこと

CloudFormation StackSets を利用して Config を全リージョンで有効化したい。

AWS Config と AWS Config Rules

名前が似ているが役割が違う。

  • Config : AWS各種リソースの設定変更を記録する
  • Config Rules : AWS各種リソースの設定が決められた基準を満たしているか確認する

AWS Config の CloudFormation

AWS 公式から Config Rules の CloudFormation テンプレートが公開されているので参考になる。

AWS Config を有効にするには、設定レコーダーと配信チャネルを作成する必要がある。

いずれもリージョナルサービスのため、リージョンごとに有効化する必要がある。

ConfigurationRecorder

AWS Config が設定変更を記録する AWS リソースのタイプを指定する。

以下の場合だと、全リソースを監視対象となる。

  ConfigRecorder:
    Type: AWS::Config::ConfigurationRecorder
    Properties:
      RoleARN: String
      RecordingGroup:
        AllSupported: True
        IncludeGlobalResourceTypes: True

RecordingGroup 内で ResourceTypes を指定すると、指定したリソースのみ記録するようになる。
その場合、AllSupported は False に指定する必要がある。

  ConfigRecorder:
    Type: AWS::Config::ConfigurationRecorder
    Properties:
      RoleARN: String
      RecordingGroup:
        AllSupported: False
        ResourceTypes:
          - AWS::EC2::Instance

DeliveryChannel

Config の記録情報を配信する配信先(Amazon S3 バケットおよび Amazon SNS トピック)と配信頻度を指定する。

下記の場合だと、1時間に1度、S3 と SNS トピックに配信している。

Type: AWS::Config::DeliveryChannel
Properties: 
  ConfigSnapshotDeliveryProperties: 
    DeliveryFrequency: "One_Hour"
  S3BucketName: 
      Ref: ConfigBucket
    SnsTopicARN: 
      Ref: ConfigTopic

CloudFormation

単一リージョン Config 有効化

AWS CloudFormation StackSets サンプルテンプレート の「AWS Config を有効にする」を参考に、Config を有効化する箇所だけ抜粋。

AWSTemplateFormatVersion: 2010-09-09
Description: Enable AWS Config

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Recorder Configuration
        Parameters:
          - AllSupported
          - IncludeGlobalResourceTypes
          - ResourceTypes
      - Label:
          default: Delivery Channel Configuration
        Parameters:
          - DeliveryChannelName
          - Frequency
      - Label:
          default: Delivery Notifications
        Parameters:
          - TopicArn
          - NotificationEmail
    ParameterLabels:
      AllSupported:
        default: Support all resource types
      IncludeGlobalResourceTypes:
        default: Include global resource types
      ResourceTypes:
        default: List of resource types if not all supported
      DeliveryChannelName:
        default: Configuration delivery channel name
      Frequency:
        default: Snapshot delivery frequency
      TopicArn:
        default: SNS topic name
      NotificationEmail:
        default: Notification Email (optional)

Parameters:
  AllSupported:
    Type: String
    Default: True
    Description: Indicates whether to record all supported resource types.
    AllowedValues:
      - True
      - False
  IncludeGlobalResourceTypes:
    Type: String
    Default: True
    Description: Indicates whether AWS Config records all supported global resource types.
    AllowedValues:
      - True
      - False
  ResourceTypes:
    Type: List<String>
    Description: A list of valid AWS resource types to include in this recording group, such as AWS::EC2::Instance or AWS::CloudTrail::Trail.
    Default: <All>

Conditions:
  IsAllSupported: !Equals
    - !Ref AllSupported
    - True

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:
        - arn:aws:iam::aws:policy/service-role/AWSConfigRole

  ConfigRecorder:
    Type: AWS::Config::ConfigurationRecorder
    Properties:
      RoleARN: !GetAtt ConfigRecorderRole.Arn
      RecordingGroup:
        AllSupported: !Ref AllSupported
        IncludeGlobalResourceTypes: !Ref IncludeGlobalResourceTypes
        ResourceTypes: !If
          - IsAllSupported
          - !Ref AWS::NoValue
          - !Ref ResourceTypes

反映する。

aws cloudformation deploy --stack-name config --template-file config.yaml --capabilities CAPABILITY_NAMED_IAM

Config コンソールの設定から、レコーダーが有効になっていることを確認。

次の作業があるため、削除にしておく。

aws cloudformation delete-stack --stack-name config

StackSets で全リージョンへデプロイ

AWS 公式のテンプレートをそのまま利用。

以下の条件で変更履歴が記録されるようになる。

  • 全リージョン
  • 全リソース
  • 配信頻度は 24 時間
AWSTemplateFormatVersion: 2010-09-09
Description: Enable AWS Config

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Recorder Configuration
        Parameters:
          - AllSupported
          - IncludeGlobalResourceTypes
          - ResourceTypes
      - Label:
          default: Delivery Channel Configuration
        Parameters:
          - DeliveryChannelName
          - Frequency
      - Label:
          default: Delivery Notifications
        Parameters:
          - TopicArn
          - NotificationEmail
    ParameterLabels:
      AllSupported:
        default: Support all resource types
      IncludeGlobalResourceTypes:
        default: Include global resource types
      ResourceTypes:
        default: List of resource types if not all supported
      DeliveryChannelName:
        default: Configuration delivery channel name
      Frequency:
        default: Snapshot delivery frequency
      TopicArn:
        default: SNS topic name
      NotificationEmail:
        default: Notification Email (optional)

Parameters:
  AllSupported:
    Type: String
    Default: True
    Description: Indicates whether to record all supported resource types.
    AllowedValues:
      - True
      - False

  IncludeGlobalResourceTypes:
    Type: String
    Default: True
    Description: Indicates whether AWS Config records all supported global resource types.
    AllowedValues:
      - True
      - False

  ResourceTypes:
    Type: List<String>
    Description: A list of valid AWS resource types to include in this recording group, such as AWS::EC2::Instance or AWS::CloudTrail::Trail.
    Default: <All>

  DeliveryChannelName:
    Type: String
    Default: <Generated>
    Description: The name of the delivery channel.

  Frequency:
    Type: String
    Default: 24hours
    Description: The frequency with which AWS Config delivers configuration snapshots.
    AllowedValues:
      - 1hour
      - 3hours
      - 6hours
      - 12hours
      - 24hours

  TopicArn:
    Type: String
    Default: <New Topic>
    Description: The Amazon Resource Name (ARN) of the Amazon Simple Notification Service (Amazon SNS) topic that AWS Config delivers notifications to.

  NotificationEmail:
    Type: String
    Default: <None>
    Description: Email address for AWS Config notifications (for new topics).

Conditions:
  IsAllSupported: !Equals
    - !Ref AllSupported
    - True
  IsGeneratedDeliveryChannelName: !Equals
    - !Ref DeliveryChannelName
    - <Generated>
  CreateTopic: !Equals
    - !Ref TopicArn
    - <New Topic>
  CreateSubscription: !And
    - !Condition CreateTopic
    - !Not
      - !Equals
        - !Ref NotificationEmail
        - <None>

Mappings:
  Settings:
    FrequencyMap:
      1hour   : One_Hour
      3hours  : Three_Hours
      6hours  : Six_Hours
      12hours : Twelve_Hours
      24hours : TwentyFour_Hours

Resources:

  ConfigBucket:
    DeletionPolicy: Retain
    Type: AWS::S3::Bucket

  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: AWSConfigBucketDelivery
            Effect: Allow
            Principal:
              Service:
                - config.amazonaws.com
            Action: s3:PutObject
            Resource:
              - !Sub "arn:aws:s3:::${ConfigBucket}/AWSLogs/${AWS::AccountId}/*"

  ConfigTopic:
    Condition: CreateTopic
    Type: AWS::SNS::Topic
    Properties:
      TopicName: !Sub "config-topic-${AWS::AccountId}"
      DisplayName: AWS Config Notification Topic

  ConfigTopicPolicy:
    Condition: CreateTopic
    Type: AWS::SNS::TopicPolicy
    Properties:
      Topics:
        - !Ref ConfigTopic
      PolicyDocument:
        Statement:
          - Sid: AWSConfigSNSPolicy
            Action:
              - sns:Publish
            Effect: Allow
            Resource: !Ref ConfigTopic
            Principal:
              Service:
                - config.amazonaws.com

  EmailNotification:
    Condition: CreateSubscription
    Type: AWS::SNS::Subscription
    Properties:
      Endpoint: !Ref NotificationEmail
      Protocol: email
      TopicArn: !Ref ConfigTopic

  ConfigRecorderRole:
    Type: AWS::IAM::Role
    Properties:
      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:
      - ConfigBucketPolicy
      - ConfigTopicPolicy
    Properties:
      RoleARN: !GetAtt ConfigRecorderRole.Arn
      RecordingGroup:
        AllSupported: !Ref AllSupported
        IncludeGlobalResourceTypes: !Ref IncludeGlobalResourceTypes
        ResourceTypes: !If
          - IsAllSupported
          - !Ref AWS::NoValue
          - !Ref ResourceTypes

  ConfigDeliveryChannel:
    Type: AWS::Config::DeliveryChannel
    DependsOn:
      - ConfigBucketPolicy
      - ConfigTopicPolicy
    Properties:
      Name: !If
        - IsGeneratedDeliveryChannelName
        - !Ref AWS::NoValue
        - !Ref DeliveryChannelName
      ConfigSnapshotDeliveryProperties:
        DeliveryFrequency: !FindInMap
          - Settings
          - FrequencyMap
          - !Ref Frequency
      S3BucketName: !Ref ConfigBucket
      SnsTopicARN: !If
        - CreateTopic
        - !Ref ConfigTopic
        - !Ref TopicArn

StackSets 作成。カスタム IAM Role の作成が必要なため、–capabilities CAPABILITY_NAMED_IAM を付ける必要がある。

aws cloudformation create-stack-set --stack-set-name config-stackset --template-body file://config_all.yaml --administration-role-arn arn:aws:iam::111111111111:role/AWSCloudFormationStackSetAdministrationRole --execution-role-name AWSCloudFormationStackSetExecutionRole --capabilities CAPABILITY_NAMED_IAM

デプロイしたいリージョンを指定すると、デプロイされる。

aws cloudformation create-stack-instances --stack-set-name config-stackset --accounts '["111111111111"]' --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

※途中で失敗して止まった場合、再度デプロイしたいリージョンのみ指定してアップデート

aws cloudformation update-stack-instances --stack-set-name config-stackset --accounts '["111111111111"]' --regions '["us-east-1", "us-east-2", "us-west-1", "us-west-2"]'

全リージョンの Status が CURRENT になれば、全リージョンで Config が有効化されている。

IAM Role もリージョン分作成されてしまう。。

エラー

Failed to put configuration recorder ‘config-ConfigRecorder-1E0B9MVZ3WX2I’ because the maximum number of configuration recorders: 1 is reached. (Service: AmazonConfig; Status Code: 400; Error Code: MaxNumberOfConfigurationRecordersExceededException; Request ID: bb5f8747-ac21-4f5a-9bfb-dd9854c15027)

recorder はリージョンごとで作成できる最大数は一つ。AWS Config を GUIか ら無効化したあとに IaC から有効化をしようとする遭遇するエラー。

まず、recorder を確認する。

aws configservice describe-configuration-recorders
{
    "ConfigurationRecorders": [
        {
            "name": "default",
            "roleARN": "arn:aws:iam::111111111111:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig",
            "recordingGroup": {
                "allSupported": true,
                "includeGlobalResourceTypes": false,
                "resourceTypes": []
            }
        }
    ]
}

default という名前の recorder が存在したため、こいつを消す。

aws configservice delete-configuration-recorder --configuration-recorder-name default

ResourceLogicalId:ConfigRecorder, ResourceType:AWS::Config::ConfigurationRecorder, ResourceStatusReason:Failed to put configuration recorder ‘StackSet-config-stackset-f04e9016-5d58-4c36-9380-8b1871ea089a-ConfigRecorder-YJP7YEIZVV4R’ because the maximum number of configuration recorders: 1 is reached. (Service: AmazonConfig; Status Code: 400; Error Code: MaxNumberOfConfigurationRecordersExceededException; Request ID: 2e54e35d-e9e0-4a6e-bfb8-4b324b51731f).

また消せなかったレコーダーがあるのかと確認したが、存在していない。

aws configservice describe-configuration-recorders
{
    "ConfigurationRecorders": []
}

デリバリチャンネルが存在した。こちらもリージョンで一つしか作れない。

aws configservice describe-delivery-channels
{
    "DeliveryChannelsStatus": [
        {
            "name": "default",
            "configSnapshotDeliveryInfo": {},
            "configHistoryDeliveryInfo": {
                "lastStatus": "SUCCESS",
                "lastAttemptTime": 1592999120.615,
                "lastSuccessfulTime": 1592999120.615
            },
            "configStreamDeliveryInfo": {
                "lastStatus": "NOT_APPLICABLE",
                "lastStatusChangeTime": 1592999120.623
            }
        }
    ]
}

削除する。

aws configservice delete-delivery-channel --delivery-channel-name default

デフォルトリージョン以外の確認。

aws --region us-east-1 configservice describe-delivery-channels

デフォルトリージョン以外の削除。

aws –region us-east-1 configservice delete-configuration-recorder –configuration-recorder-name default

参考

設定レコーダーの管理

AWS CloudFormation StackSets サンプルテンプレート