CloudTrail で複数のアカウントからログファイルを受け取るバケットを CloudFormation で作成する

# はじめに

AWS Organizations を使わずにマルチアカウントで構成されたアカウントの CloudTrail 証跡ログをログアーカイブアカウントに集約するためにバケットに対してバケットポリシーを設定する必要があります。
必要なバケットポリシーが適用されたバケットを作成するために CloudFormation を利用して作成します。

https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-set-bucket-policy-for-multiple-accounts.html

## ドキュメントに記載されているポリシー

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AWSCloudTrailAclCheck20131101",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudtrail.amazonaws.com"
      },
      "Action": "s3:GetBucketAcl",
      "Resource": "arn:aws:s3:::myBucketName"
    },
    {
      "Sid": "AWSCloudTrailWrite20131101",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudtrail.amazonaws.com"
      },
      "Action": "s3:PutObject",
      "Resource": [
        "arn:aws:s3:::myBucketName/optionalLogFilePrefix/AWSLogs/111111111111/*",
        "arn:aws:s3:::myBucketName/optionalLogFilePrefix/AWSLogs/222222222222/*"
      ],
      "Condition": { 
        "StringEquals": { 
          "aws:SourceArn": "arn:aws:cloudtrail:region:myAccountID:trail/trailName",
          "s3:x-amz-acl": "bucket-owner-full-control"
        }
      }
    }
  ]
}
```

## テンプレート

環境に合わせて以下の入力を変更してください。

- \<prefix\>: S3プリフィックスの指定がある場合
- \<region\>: CloudTrailが作成されているリージョン
- \<account-id-N\>: CloudTrailの証跡ログを保存する対象のAWSアカウントID
- \<trail-name\>: CloudTrailの証跡名

```yaml:cloudtrail-log.cfn.yml
AWSTemplateFormatVersion: 2010-09-09
Description: CloudTrail Data Export Bucket

Parameters:
  Prefix:
    Type: String
    Default: "prefix"
  Env:
    Type: String
    Default: "prod"
    AllowedValues:
      - prod

Conditions:
  IsUSE1: !Equals [ !Ref "AWS::Region", "us-east-1" ]

Resources:
  CloudTrailLogBucket:
    Type: AWS::S3::Bucket
    Condition: IsUSE1
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      BucketName: !Sub "${Prefix}-${Env}-cloudtrail-${AWS::AccountId}"
      LifecycleConfiguration:
        Rules:
          - ExpirationInDays: 400
            Id: contents-life-cycle
            Status: Enabled
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
  CloudTrailLogBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Condition: IsUSE1
    DependsOn: CloudTrailLogBucket
    Properties:
      Bucket: !Ref CloudTrailLogBucket
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: AWSCloudTrailAclCheck20131101
            Principal:
              Service:
                - cloudtrail.amazonaws.com
            Effect: Allow
            Action:
              - 's3:GetBucketAcl'
            Resource:
              - !GetAtt CloudTrailLogBucket.Arn
          - Sid: AWSCloudTrailWrite20131101
            Principal:
              Service:
                - cloudtrail.amazonaws.com
            Effect: Allow
            Action:
              - 's3:PutObject'
            Resource:
              - !Sub "arn:aws:s3:::${CloudTrailLogBucket}/<prefix>/AWSLogs/<account-id-1>/*"
              - !Sub "arn:aws:s3:::${CloudTrailLogBucket}/<prefix>/AWSLogs/<account-id-2>/*"
            Condition:
              StringEquals:
                s3:x-amz-acl: "bucket-owner-full-control"
                aws:SourceArn: [
                  "arn:aws:cloudtrail:<region>:<account-id-1>:trail/<trail-name>",
                  "arn:aws:cloudtrail:<region>:<account-id-2>:trail/<trail-name>"
                ]
```

## デプロイ

GUI, CLIのいずれかでスタックを作成します。
以下はCLIでスタック作成する際のサンプルです。

```sh
aws cloudformation create-stack \
    --stack-name cloudtrail-log-stack \
    --region us-east-1 \
    --template-body file://cloudtrail-log.cfn.yml \
    --parameters \
        ParameterKey=Prefix,ParameterValue='prefix' \
        ParameterKey=Env,ParameterValue='prod' \
    --enable-termination-protection
```

## おまけ

バケットの作成が終わった後は各アカウントで証跡ログの送信先を変更してあげます。
以下のコマンドは既存の証跡に対して変更を行う場合です。

```sh
aws cloudtrail update-trail \
    --region <HomeRegion> \
    --name <CloudTrailName> \
    --s3-bucket-name <バケット名> \
    --s3-key-prefix <プリフィックス> \
    --is-multi-region-trail \
    --enable-log-file-validation
```