はじめてのAWS CloudFormation -S3バケット作成編-

# はじめに
AWSのCloudFormationに対して、なんとなくとっつきにくいイメージを持っていたので、手始めによくある設定のS3バケットをテンプレート化してみました。

# CloudFormationテンプレート作成環境を整える
VScodeでCloudFormationテンプレートを書くにあたって、以下の記事を参考に環境を整えました。

- 参考記事:[DevelopersIO:VS Code拡張機能を追加してCloudFormation開発環境をレベルアップする](https://dev.classmethod.jp/articles/20211008-vscode-extention-settings/)

拡張は"CloudFormation support for Visual Studio Code"と"cfn-lint"のみ導入しました。"pydot"と"indent-rainbow"は現状必要なさそうだったので導入していません。

# S3バケットのパラメータ設計
作成するバケットのパラメータを一度GUIベースで考えてみます。
AWSインフラ環境を新規構築する際によくある設定として以下のパラメータで作成します。

- 作成バケット
 - アクセスログ用バケット
 - コンテンツ用バケット

- 共通設定
 - パブリックアクセス:ブロック。
 - デフォルト暗号化:有効。S3管理キー(SSE-S3)。
    - バケットアクセスログ:有効。アクセスログ用バケットにバケット名ごとにディレクトリ作成して格納。([アクセスログ用バケット自体のアクセスログの記録は無効](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/enable-server-access-logging.html))
 - タグ:「Key:Env, Value:CFn-test」を付与。

- アクセスログ用バケット設定
 - ライフサイクルルール:有効。スコープは全体。"オブジェクトの現行バージョンを有効期限切れ"設定で有効期限は3日。

# テンプレート用パラメータの確認
[AWS公式ドキュメント(AWS CloudFormation User Guide)](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html)からS3バケットに関するPropertiesを確認します。
Propertiesとしては以下の設定項目があるようです。

```yml:AllBucketProperties
Properties: 
  AccelerateConfiguration: 
    AccelerateConfiguration
  AccessControl: String
  AnalyticsConfigurations: 
    - AnalyticsConfiguration
  BucketEncryption: 
    BucketEncryption
  BucketName: String
  CorsConfiguration: 
    CorsConfiguration
  IntelligentTieringConfigurations: 
    - IntelligentTieringConfiguration
  InventoryConfigurations: 
    - InventoryConfiguration
  LifecycleConfiguration: 
    LifecycleConfiguration
  LoggingConfiguration: 
    LoggingConfiguration
  MetricsConfigurations: 
    - MetricsConfiguration
  NotificationConfiguration: 
    NotificationConfiguration
  ObjectLockConfiguration: 
    ObjectLockConfiguration
  ObjectLockEnabled: Boolean
  OwnershipControls: 
    OwnershipControls
  PublicAccessBlockConfiguration: 
    PublicAccessBlockConfiguration
  ReplicationConfiguration: 
    ReplicationConfiguration
  Tags: 
    - Tag
  VersioningConfiguration: 
    VersioningConfiguration
  WebsiteConfiguration: 
    WebsiteConfiguration
```

# テンプレートに必要な項目の確認
公式ドキュメント記載の各パラメータを確認しながら、今回の設定に必要なパラメータを抜き出します。GUIの設定項目名とほとんど同じなので意外と簡単です。

```yml:BucketProperties
Properties: 
  #バケットACL設定
  AccessControl: String
  #デフォルト暗号化設定
  BucketEncryption: 
    BucketEncryption
  #バケット名設定
  BucketName: String
  #ライフサイクルルール設定
  LifecycleConfiguration: 
    LifecycleConfiguration
  #アクセスログ設定
  LoggingConfiguration: 
    LoggingConfiguration
  #パブリックアクセスブロック設定
  PublicAccessBlockConfiguration: 
    PublicAccessBlockConfiguration
  #タグ設定
  Tags: 
    - Tag
```

# 完成したCloudFormationテンプレート
公式ドキュメントをベースに各パラメータの記載方法を確認しながらテンプレートを作成し、最終的に出来上がったものが以下のテンプレートです。

```yml:CreateS3Bucket
AWSTemplateFormatVersion: "2010-09-09"
Description: Create S3 Bucket
Parameters:
 BucketNameAccessLog:
  Type: String
  Description: Name for AccessLog
 BucketNameBucket01:
  Type: String
  Description: Name for Bucket_1

Resources: 
  AccessLogBucket:
    Type: AWS::S3::Bucket
    Properties: 
      AccessControl: LogDeliveryWrite
      BucketName: !Ref BucketNameAccessLog
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      LifecycleConfiguration:
        Rules:
          - Id: "LifeCycle-01"
            Status: Enabled
            ExpirationInDays: 3
      Tags:
        - Key: Env
          Value: CFn-test
  Bucket01:
    Type: AWS::S3::Bucket
    Properties: 
      AccessControl: Private
      BucketName: !Ref BucketNameBucket01
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      LoggingConfiguration:
        DestinationBucketName: !Ref BucketNameAccessLog
        LogFilePrefix: !Join ["", [!Ref BucketNameBucket01, "/"]]
      Tags:
        - Key: Env
          Value: CFn-test
```

# CloudFormationStackを作成して流した実行結果の確認

Stackのイベント履歴
![CFn_2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/7c9d9afe-7426-4652-4e33-fc50596c5b46.jpeg)

作成されたバケット
![CFn_3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/d59d237f-e72d-0d61-91ca-c50be882c8e4.jpeg)

パブリックアクセスブロック設定
![CFn_8.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/c08da0a3-36de-e8f0-a993-6d686c0a25bb.jpeg)

タグと暗号化設定
![CFn_6.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/582f6d05-7068-789f-008f-de03ebf6c5bf.jpeg)

コンテンツ用バケットのアクセスログ設定
![CFn_5.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/bb7d78de-ec9a-5760-bbfe-64dac63c4961.jpeg)
![CFn_9.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/dc1c43ba-8405-67fe-b651-44343c55de07.jpeg)


アクセスログ用バケットのライフサイクルルール
![CFn_7.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/02f29506-4cfb-07d1-4d8c-beca15db64c3.jpeg)


# 考慮した点や途中発生したエラーについて
### テンプレートの使いまわしができるように書く
テンプレート中の`BucketName:`や`LogFilePrefix:`部分は最初ベタ書きでバケット名やプレフィックスを書いていたものの、使いまわしができるようにPropertiesセクションで変数として定義し、組み込み関数の"!Ref"を使ってResourcesセクションから呼び出せるように変更しました。
`LogFilePrefix:`の部分は"!Join"を使って指定したバケット名ごとにディレクトリが作成されるように変更しました。

Propertiesセクションに記載した項目はデプロイ時にパラメータとして指定し、入力した値に従ってデプロイされます。
![CFn_1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561546/81e49bfd-a5a5-6515-350a-24395f4da054.jpeg)

### アクセスログ用バケットのACL設定
アクセスログ用バケットのACL設定について最初は`AccessControl: Private`と記載していましたが、実際に流してみると「You must give the log-delivery group WRITE and READ_ACP permissions to the target bucket.」とエラーが出て作成されませんでした。
エラーの内容としてはバケットのACLでログ配信グループの書き込み許可がないために発生したエラーであり、`AccessControl: LogDeliveryWrite`と記載するのが正解のようです。
ドキュメント:[AccessControl:で指定するパラメータと内容](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/acl-overview.html#canned-acl)

「GUIから設定する時にアクセスログ用バケット側のACL設定いじった記憶がないなぁ」と思いましたが、GUIからアクセスログの設定をする際にはこのアクセスログ用バケットのACL設定を自動で書き換えてくれていたので意識することがなかっただけでした。GUIでポチポチと設定する時は自動でやってくれる部分もCloudFormationだと明示的に設定しなければいけない部分が出てくるということが分かりました。

# おわりに
Azureでは「今GUIで作ったリソースをテンプレート化するとどうなるのだろう？」というのがWebコンソールからすぐに確認できるのですが、
AWSでは今あるリソースのテンプレートを確認する機能がWebコンソールとしてはないため、なんとなくCloudFormationに対してとっつきにくいイメージを持っていました(初見だとCFnのWebデザイナーもよくわからない...)。
しかし、CFnテンプレートに記載する内容はGUIの設定項目と似ているため、書き方を把握してしまえば意外と簡単に作成できることを実感しました。
今回はParametersとResourcesセクションを使ってみましたが、今後MappingsやConditionsセクションも活用してみたいです。