AWS CDKでAWS Configを有効化する

## はじめに
最近AWS ConfigをCDKで有効化したのですが、少しハマったのでコードと共に記録を残しておきます

## ハマったところ
ハマったのはAWS Configの仕様です。

具体的にはConfigを有効化する際に`AWS::Config::ConfigurationRecorder`と`AWS::Config::ConfigDeliveryChannel`を作成する必要があるのですが、これらは<b>同時に</b>有効化する必要があるようです。DependsOnなどでどちらかを優先すると一向に作成が終わりませんでした。

公式にはRecorderを先に作るべしという記述を見つけてDependsOnしていたので、少し気づくのに時間がかかりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552758/0303f990-079e-4ba9-bc81-1190a3805402.png)

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-config-deliverychannel.html

なぜ同時に有効化する必要があるのか。APIリファレンスを見たところ、Configuration Recorderの有効化にあたって2つのAPIを実行する必要があるとわかりました。一つ目は[PutConfigurationRecorder](https://docs.aws.amazon.com/config/latest/APIReference/API_PutConfigurationRecorder.html)、二つ目は[StartConfigurationRecorder](https://docs.aws.amazon.com/config/latest/APIReference/API_StartConfigurationRecorder.html)です。なお1つ目のAPIはDeliveryChannel作成の前、二つ目は作成の後に叩く必要があります。

CDK/CloudFormation経由でConfiguration Recorderを有効化するとPutConfigurationRecorderとStartConfigurationRecorderのタイミングを制御できないので、DependsOnをどちらかにつけるとエラーになってしまうようです。

## コード
というわけでCDK（Stack部分）のコードを置いておきます
```typescript
import * as cdk from "aws-cdk-lib";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as iam from "aws-cdk-lib/aws-iam";
import * as config from "aws-cdk-lib/aws-config";
import { Construct } from "constructs";

export class ConfigStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);


    const configBucket = new cdk.aws_s3.Bucket(this, "configBucket", {
      blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL,
      encryption: cdk.aws_s3.BucketEncryption.S3_MANAGED,
      enforceSSL: true,
      versioned: false,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
    });

    // Add bucket policy
    const bucketPolicy = new iam.PolicyStatement({
      sid: "AWSConfigBucketPermissionsCheck",
      effect: iam.Effect.ALLOW,
      principals: [new iam.ServicePrincipal("config.amazonaws.com")],
      actions: ["s3:GetBucketAcl"],
      resources: [configBucket.bucketArn],
      conditions: {
        StringEquals: {
          "AWS:SourceAccount": this.account, // Using the account ID of the stack
        },
      },
    });

    const listBucketPolicy = new iam.PolicyStatement({
      sid: "AWSConfigBucketExistenceCheck",
      effect: iam.Effect.ALLOW,
      principals: [new iam.ServicePrincipal("config.amazonaws.com")],
      actions: ["s3:ListBucket"],
      resources: [configBucket.bucketArn],
      conditions: {
        StringEquals: {
          "AWS:SourceAccount": this.account,
        },
      },
    });

    const putObjectPolicy = new iam.PolicyStatement({
      sid: "AWSConfigBucketDelivery",
      effect: iam.Effect.ALLOW,
      principals: [new iam.ServicePrincipal("config.amazonaws.com")],
      actions: ["s3:PutObject"],
      resources: [`${configBucket.bucketArn}/AWSLogs/${this.account}/Config/*`],
      conditions: {
        StringEquals: {
          "s3:x-amz-acl": "bucket-owner-full-control",
          "AWS:SourceAccount": this.account,
        },
      },
    });

    // Add the policy statements to the bucket
    configBucket.addToResourcePolicy(bucketPolicy);
    configBucket.addToResourcePolicy(listBucketPolicy);
    configBucket.addToResourcePolicy(putObjectPolicy);

    const configRole = new iam.Role(this, "ConfigRole", {
      assumedBy: new iam.ServicePrincipal("config.amazonaws.com"),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName(
          "service-role/AWS_ConfigRole"
        ),
      ],
    });

    const configRecorder = new config.CfnConfigurationRecorder(
      this,
      "ConfigRecorder",
      {
        name: "CloudFrontVpcOriginConfigRecorder",
        roleArn: configRole.roleArn,
        recordingGroup: {
          allSupported: true,
          includeGlobalResourceTypes: true,
        },
      }
    );

    const configDeliveryChannel = new config.CfnDeliveryChannel(
      this,
      "ConfigDeliveryChannel",
      {
        name: "CloudFrontVpcOriginConfigDeliveryChannel",
        s3BucketName: configBucket.bucketName, // S3バケット名を指定
        configSnapshotDeliveryProperties: {
          deliveryFrequency: "TwentyFour_Hours",
        },
      }
    );
    configDeliveryChannel.node.addDependency(configBucket);
    configDeliveryChannel.node.addDependency(configRole);
    configRecorder.node.addDependency(configRole);
    configRecorder.node.addDependency(configBucket);
  }
}

```
## おわりに

本記事が誰かのお役に立てば幸いです。