(第3回)CloudFormationで環境構築を効率化 ~ Amazon S3 クロスリージョンレプリケーション構成編 ~

(第3回)CloudFormationで環境構築を効率化 ~ Amazon S3 クロスリージョンレプリケーション構成編 ~

目次

前回の振り返り
構成イメージ
大阪リージョン用テンプレート
itport-s3-osaka.yaml
東京リージョン用テンプレート
itport-s3-tokyo.yaml
確認
おわりに

前回の振り返り

前回まではVPCやEC2の起動をCloud Formationを使って構築してみました。今回はS3バケットをCloud Formationを使用して構築します。

構成イメージ

Amzon S3の標準ストレージは 99.999999999 %(9 × 11)の堅牢性があると言われております。
1つのリージョンで使用するだけでもハード障害によるデータのロストは心配いらないように思いますが、ビジネス要件によっては遠隔地へのバックアップやオペレーションミス等によるデータロストに備えるといった事も出てくるかもしれません。
今回は2つのバケットを作成しクロスリージョンレプリケーション(CRR)をする構成をCloud Formationで実現してみたいと思います。

構成イメージ
  • クロスリージョンレプリケーション(東京→大阪)
  • オブジェクトのバージョニングを有効
  • 過去バージョンのオブジェクトは180日経過でスタンダードIAクラスへ変更
  • S3のデフォルト暗号化を有効化

という4点を盛り込んでいきたいと思います。

大阪リージョン用テンプレート

Cloud Formationのスタックは作成するリソースのリージョンと同じリージョンでスタックを実行する必要があります。まずは大阪リージョンでバックアップストレージ用S3を作成します。

itport-s3-osaka.yaml

AWSTemplateFormatVersion: "2010-09-09"
Description:
  Create S3 bucket in Osaka Region

Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          Default: "Project Name Prefix"
        Parameters:
          - PJPrefix
      - Label:
          Default: "Group Name"
        Parameters:
          - Group
      - Label:
          Default: "Environment"
        Parameters:
          - Environment

    ParameterLabels:
      PJPrefix:
        default: "Project Name Prefix"
      Group:
        default: "Group Tag Value"
      Environment:
        default: "Environment Tag Value"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------#
Parameters:
  PJPrefix:
    Type: String
    Default: "itport"
  Group:
    Type: String
    Default: "itport"
  Environment:
    Type: String
    AllowedValues:
      - development
      - production

  # バケット名
  BucketName:
    Type: String
    Default: "itport-osaka"

Resources:
# ------------------------------------------------------------#
# S3 Bucket
# ------------------------------------------------------------#
  # 大阪リージョン(DR用)
  S3Bucket:
    Type: "AWS::S3::Bucket"
    # CFnスタック削除時のポリシー (Retain = 保持)
    DeletionPolicy: Retain
    # CFnスタック更新時のポリシー (Retain = 保持)
    UpdateReplacePolicy: Retain
    Properties:
      # バケット名
      BucketName: !Sub ${BucketName}
      # バージョニング
      VersioningConfiguration:
        Status: Enabled
      # バケットACL
      AccessControl: BucketOwnerFullControl
      # 暗号化
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      # パブリックアクセスブロック
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      # ライフサイクル
      LifecycleConfiguration:
        Rules:
          - Id: !Join ['-', [!Sub '${BucketName}', 'Contents-LifeCycle']]
            Status: Enabled
            # バージョニングされているデータは180日経過したらスタンダードIAに移行する
            NoncurrentVersionTransition:
              StorageClass: STANDARD_IA
              TransitionInDays: 180
      # タグ
      Tags:
        - Key: "Name"
          Value: !Sub ${BucketName}
        - Key: "Group"
          Value: !Ref Group
        - Key: "Environment"
          Value: !Ref Environment

  # バケットポリシー(大阪用)
  BucketPolicy:
    Type: "AWS::S3::BucketPolicy"
    Properties:
      Bucket: !Ref S3Bucket
      PolicyDocument:
        Statement:
          - Sid: "DenyIncorrectEncryptionHeader"
            Action:
              - "s3:PutObject"
            Effect: "Deny"
            Principal: "*"
            Resource: !Sub "arn:aws:s3:::${BucketName}/*"
            Condition:
              "StringNotEquals":
                "s3:x-amz-server-side-encryption": "AES256"
          - Sid: "DenyUnencryptedObjectUploads"
            Action:
              - "s3:PutObject"
            Effect: "Deny"
            Principal: "*"
            Resource: !Sub "arn:aws:s3:::${BucketName}/*"
            Condition:
              "Null":
                "s3:x-amz-server-side-encryption": "true"

作成したyamlファイルからスタックを作成します。

Cloud Formation「スタックの作成」画面

東京リージョン用テンプレート

続いて東京リージョンに移動してスタックを作成します。今回のスタックではバケットを作成する以外にIAM Roleなども作成します。

itport-s3-tokyo.yaml

AWSTemplateFormatVersion: "2010-09-09"
Description:
  Create S3 Bucket in Tokyo Region

Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          Default: "Project Name Prefix"
        Parameters:
          - PJPrefix
      - Label:
          Default: "Group Name"
        Parameters:
          - Group
      - Label:
          Default: "Environment"
        Parameters:
          - Environment

    ParameterLabels:
      PJPrefix:
        default: "Project Name Prefix"
      Group:
        default: "Group Tag Value"
      Environment:
        default: "Environment Tag Value"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------#
Parameters:
  PJPrefix:
    Type: String
    Default: "itport"
  Group:
    Type: String
    Default: "itport"
  Environment:
    Type: String
    AllowedValues:
      - development
      - production

  # バケット名
  BucketName:
    Type: String
    Default: "itport-tokyo"

  # バックアップバケット名(完全なバケット名)
  BackupBucketName:
    Type: String
    Default: "itport-osaka"

Resources:
# ------------------------------------------------------------#
# IAM Role
# ------------------------------------------------------------#
  WorkItemBucketBackupRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - 'sts:AssumeRole'
            Effect: Allow
            Principal:
              Service:
                - s3.amazonaws.com
      # ポリシー
      Policies:
        - PolicyName: S3Replication
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetReplicationConfiguration
                  - s3:ListBucket
                Resource:
                  - !Sub "arn:aws:s3:::${BucketName}"
                  - !Sub "arn:aws:s3:::${BackupBucketName}"
              - Effect: Allow
                Action:
                  - s3:GetObjectVersion
                  - s3:GetObjectVersionAcl
                  - s3:GetObjectVersionTagging
                Resource:
                  - !Sub "arn:aws:s3:::${BucketName}/*"
                  - !Sub "arn:aws:s3:::${BackupBucketName}/*"
              - Effect: Allow
                Action:
                  - s3:ReplicateObject
                  - s3:ReplicateDelete
                  - s3:ReplicateTags
                Resource:
                  - !Sub "arn:aws:s3:::${BucketName}/*"
                  - !Sub "arn:aws:s3:::${BackupBucketName}/*"
      # タグ
      Tags:
        - Key: "Name"
          Value: !Sub ${PJPrefix}-s3-backup-role
        - Key: "Group"
          Value: !Ref Group
        - Key: "Environment"
          Value: !Ref Environment

# ------------------------------------------------------------#
# S3 Bucket
# ------------------------------------------------------------#
  # 東京リージョン(メイン)
  S3Bucket:
    Type: "AWS::S3::Bucket"
    # CFnスタック削除時のポリシー (Retain = 保持)
    DeletionPolicy: Retain
    # CFnスタック更新時のポリシー (Retain = 保持)
    UpdateReplacePolicy: Retain
    Properties:
      # バケット名
      BucketName: !Sub ${BucketName}
      # レプリケーション (東京リージョンのみ)
      ReplicationConfiguration:
        Role: !GetAtt
          - WorkItemBucketBackupRole
          - Arn
        Rules:
          - Destination:
              Bucket: !Sub arn:aws:s3:::${BackupBucketName}
              StorageClass: STANDARD
            Id: Backup
            Prefix: ''
            Status: Enabled
      # バージョニング
      VersioningConfiguration:
        Status: Enabled
      # バケットACL
      AccessControl: BucketOwnerFullControl
      # 暗号化
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      # パブリックアクセスブロック
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      # ライフサイクル
      LifecycleConfiguration:
        Rules:
          - Id: !Join ['-', [!Sub '${BucketName}', 'Contents-LifeCycle']]
            Status: Enabled
            # バージョニングされているデータは180日経過したらスタンダードIAに移行する
            NoncurrentVersionTransition:
              StorageClass: STANDARD_IA
              TransitionInDays: 180
      # タグ
      Tags:
        - Key: "Name"
          Value: !Sub ${BucketName}
        - Key: "Group"
          Value: !Ref Group
        - Key: "Environment"
          Value: !Ref Environment

  # バケットポリシー(東京用)
  BucketPolicy:
    Type: "AWS::S3::BucketPolicy"
    Properties:
      Bucket: !Ref S3Bucket
      PolicyDocument:
        Statement:
          - Sid: "DenyIncorrectEncryptionHeader"
            Action:
              - "s3:PutObject"
            Effect: "Deny"
            Principal: "*"
            Resource: !Sub "arn:aws:s3:::${BucketName}/*"
            Condition:
              "StringNotEquals":
                "s3:x-amz-server-side-encryption": "AES256"
          - Sid: "DenyUnencryptedObjectUploads"
            Action:
              - "s3:PutObject"
            Effect: "Deny"
            Principal: "*"
            Resource: !Sub "arn:aws:s3:::${BucketName}/*"
            Condition:
              "Null":
                "s3:x-amz-server-side-encryption": "true"

確認

東京リージョンのバケットにファイルをアップロードすると大阪リージョンのバケットにもファイルがコピーされました。

Amazon S3「itport-osaka」バケットの状況

おわりに

別リージョンへのレプリケーション設定がテンプレートを使用することで比較的簡単に出来るようになりました。S3のレプリケーションはバックアップ目的で利用することがほとんどかと思います。今回は削除は同期されませんが、削除マーカーのレプリケーションも可能です。要件によってカスタマイズして使っていきたいと思います。

削除マーカーのレプリケーション設定は「AWS::S3::Bucket DeleteMarkerReplication」をご参照ください。