【AWS】CloudFormationでS3バケット作成とライフサイクルルールを設定する

CloudFormationでS3バケット作成、ライフサイクルルール・バケットポリシーの設定をしたのでCloudFormationのテンプレート紹介と説明をしていきます。(CloudFormationはCFnと以下略)

そもそもS3バケットの作成目的はCloudWatch LogsからS3へエクスポートするためでした。
その前提で、バケットポリシーを設定しています。
いずれCloudWatch LogsからS3へエクスポートするLambdaを開発したブログも書こうと思っています。

# 構築内容

- S3バケット作成
- ライフサイクルルールの設定
    - 30日後にストレージクラスを標準IA
    - 1年後に削除
- バケットポリシー
    - CloudWatch LogsからS3へエクスポート可能

# CFnテンプレート

GitHub Gistにもあげておきました -> [s3.yml](https://gist.github.com/homoluctus/4bd215d7fa6a6e4d07f69fb55c39afe9)

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
  CWLogsExportBucketName:
    Description: "S3 buket name for CW Logs backup"
    Type: String
  CWLogsExportBucketLifecycleStatus:
    Description: "Apply lifecycle or not"
    Type: String
    AllowedValues: ["Disabled", "Enabled"]

Resources:
  CWLogsExportBucket:
    Type: "AWS::S3::Bucket"
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      BucketName:
        Ref: CWLogsExportBucketName
      AccessControl: BucketOwnerFullControl
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      LifecycleConfiguration:
        Rules:
          - ExpirationInDays: 365
            Id: CWLogsExportBucketLifecycleRule
            Status:
              Ref: CWLogsExportBucketLifecycleStatus
            Prefix: logs/
            Transitions:
              - StorageClass: STANDARD_IA
                TransitionInDays: 30
      Tags:
        - Key: "isTest"
          Value: "True"

  CWLogsExportBucketPolicy:
    Type: "AWS::S3::BucketPolicy"
    Properties:
      Bucket:
        Ref: CWLogsExportBucket
      PolicyDocument:
        Statement:
          - Action:
              - "s3:GetBucketAcl"
            Effect: "Allow"
            Resource:
              Fn::Sub: "arn:aws:s3:::${CWLogsExportBucketName}"
            Principal:
              Service:
                - Fn::Sub: "logs.${AWS::Region}.amazonaws.com"
          - Action:
              - "s3:PutObject"
            Effect: "Allow"
            Resource:
              Fn::Sub: "arn:aws:s3:::${CWLogsExportBucketName}/*"
            Condition:
              StringEquals:
                s3:x-amz-acl: "bucket-owner-full-control"
            Principal:
              Service:
                - Fn::Sub: "logs.${AWS::Region}.amazonaws.com"
```

# 解説

## CWLogsExportBucket

S３バケットの作成します

### DeletionPolicy, UpdateReplacePolicy

スタックを削除しても作成したS3バケットは残すようにDeletionPolicyでRetainを指定します。
また、既存のS3バケットを更新する際にCFnが新たに作成し直そうとしても、既存のバケットが削除されないようにUpdateReplacePolicyで同様にRetainを指定しています。
せっかくログをS3にエクスポートしたのに設定更新のときに既存のバケットが削除されちゃった、みたいな事故を防ぐことができます。

### PublicAccessBlockConfiguration

バケットの公開をブロックするための設定パラメータです。
今回は内部でしか使用しないので、明示的に全ての公開に関するブロック設定をtrueにします。
これで完全に非公開になります。

### LifecycleConfiguration

#### ExpirationInDays

オブジェクトが作成されてから削除するまでの日数を指定します。
今回は1年保存すればいいので、365になっています。

#### Prefix

Prefixを指定することで、ライフサイクルルールを特定のオブジェクトだけに適用できます。
複数のオブジェクトがある場合に重宝します。

#### Transitions

ストレージクラスを移行するために使用します。
今回は30日経過したら標準IAのストレージクラスに移行するようにしてあります。
頻繁にログ分析をせず、バックアップ目的の場合は低アクセスのはずなので、それに見合うストレージクラスに移行するとコストカットできます。
半年経過したらGlacierに移行するってのもありですね。

## CWLogsExportBucketPolicy

CWLogsExportBucketのバケットポリシーを作成します

### PolicyDocument

PolicyDocumentにバケットポリシーの内容を記述していきます。
今回はCloudWatch Logsから作成したバケットにエクスポートできるように最小限のポリシーを適用しました。
ポリシーを引き受け可能なサービスを`Principal`でCloudWach Logsに限定して、`Resource`で作成するS3バケットを指定することで最小限のポリシーになります。

# Reference

- https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/S3Export.html
- https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html
- https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-s3-policy.html
- https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_elements_principal.html