AWS Backupを使ってEC2のバックアップをクロスリージョンにコピーする設定をCloudFormationで作成してみた

2023.03.07

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS Backupのクロスリージョンコピー設定を行う機会があったのでブログに残します。

構成

簡単な構成図ですが以下のようなことをやります。
1. 東京リージョンにあるEC2からAWS BackupでAMIを取得
2. 大阪リージョンのバックアップボールトに東京リージョンで取得したAMIをコピー
3. 大阪リージョンのVPCでEC2を復旧

上記1、2を行うAWS Backupの設定と構成図の環境をCloudFormationで作成していきます。
3については以下の公式ドキュメントの手順でバックアップボールトにある復旧ポイントから復旧を行います。
Amazon EC2 インスタンスを復元する

設定

まずは東京リージョンにVPC、EC2などを作成します。
作成は以下のCloudFormationテンプレートで作成します。

CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09"

Description: EC2 Stack

Metadata:
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------# 
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label: 
          default: Parameters for VPC
        Parameters:
          - VPCCIDR
      - Label: 
          default: Parameters for Subnet
        Parameters:
          - PublicSubnet01CIDR
      - Label: 
          default: Parameters for Security Group
        Parameters:
          - MyIP
      - Label: 
          default: Parameters for EC2
        Parameters:
          - EC2VolumeSize
          - EC2VolumeIOPS
          - EC2AMI
          - EC2InstanceType

Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------# 
  VPCCIDR:
    Default: 172.16.0.0/16
    Type: String

  PublicSubnet01CIDR:
    Default: 172.16.0.0/24
    Type: String

  MyIP:
    Type: String

  EC2VolumeSize:
    Default: 30
    Type: Number

  EC2VolumeIOPS:
    Default: 3000
    Type: Number

  EC2AMI:
    Default: ami-0329eac6c5240c99d
    Type: AWS::EC2::Image::Id

  EC2InstanceType:
    Default: t3.micro
    Type: String

Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------# 
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags: 
        - Key: Name
          Value: backup-test-ap-northeast-1

# ------------------------------------------------------------#
# InternetGateway
# ------------------------------------------------------------# 
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags: 
        - Key: Name
          Value: backup-test-igw-ap-northeast-1

  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------# 
  PublicSubnet01:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: !Ref PublicSubnet01CIDR
      MapPublicIpOnLaunch: true
      Tags: 
        - Key: Name
          Value: backup-test-public-subnet-01-ap-northeast-1
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------# 
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: backup-test-public-rtb-ap-northeast-1

  PublicRouteTableRoute1:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
      RouteTableId: !Ref PublicRouteTable

  PublicRtAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet01

# ------------------------------------------------------------#
# IAM
# ------------------------------------------------------------# 
  EC2IAMRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: Allow
            Principal: 
              Service: 
                - ec2.amazonaws.com
            Action: 
              - 'sts:AssumeRole'
      ManagedPolicyArns: 
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      RoleName: iam-role-ec2
      Tags:
        - Key: Name
          Value: iam-role-ec2

  EC2IAMInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: iam-instanceprofile-ec2
      Roles: 
        - !Ref EC2IAMRole

# ------------------------------------------------------------#
# Security Group
# ------------------------------------------------------------# 
  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: for ec2
      GroupName: backup-test-sg-ec2-ap-northeast-1
      SecurityGroupEgress: 
        - CidrIp: 0.0.0.0/0
          FromPort: -1
          IpProtocol: -1
          ToPort: -1
      SecurityGroupIngress:
        - CidrIp: !Ref MyIP
          FromPort: 80
          IpProtocol: tcp
          ToPort: 80
      Tags: 
        - Key: Name
          Value: backup-test-sg-ec2-ap-northeast-1
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# KeyPair
# ------------------------------------------------------------# 
  KeyPair:
    Type: AWS::EC2::KeyPair
    Properties: 
      KeyName: backup-test-ec2-key-ap-northeast-1
      KeyType: rsa
      Tags: 
        - Key: Name
          Value: backup-test-ec2-key-ap-northeast-1

# ------------------------------------------------------------#
# EC2
# ------------------------------------------------------------# 
  EC2:
    Type: AWS::EC2::Instance
    Properties:
      BlockDeviceMappings: 
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            Encrypted: true
            Iops: !Ref EC2VolumeIOPS
            VolumeSize: !Ref EC2VolumeSize
            VolumeType: gp3
      DisableApiTermination: false
      EbsOptimized: true
      IamInstanceProfile: !Ref EC2IAMInstanceProfile
      ImageId: !Ref EC2AMI
      InstanceType: !Ref EC2InstanceType
      KeyName: !Ref KeyPair
      NetworkInterfaces: 
        - AssociatePublicIpAddress: true
          DeleteOnTermination: true
          DeviceIndex: 0
          GroupSet: 
            - !Ref EC2SG
          SubnetId: !Ref PublicSubnet01
      Tags:
        - Key: Name
          Value: backup-test-ec2-1-ap-northeast-1
      UserData: !Base64 |
        #!/bin/bash
        yum update -y
        yum install httpd -y
        systemctl enable httpd
        systemctl start httpd
        touch /var/www/html/index.html
        echo "backup ec2 ap-northeast-1" > /var/www/html/index.html

上記のテンプレートでは特別なことはせずシンプルにパブリックサブネット上にWebサーバーのEC2を起動するようにしています。
デプロイは以下のコマンドを実行します。

aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --capabilities CAPABILITY_NAMED_IAM --parameters ParameterKey=MyIP,ParameterValue=WebサーバーへHTTPでアクセスする接続元のIPアドレス

作成完了後、EC2のパブリックIPアドレスにブラウザからアクセスすると画面に「backup ec2 ap-northeast-1」と表示されます。

確認ができたら大阪リージョンの方にVPCなどEC2を起動するための土台となるリソースとバックアップボールトを作成していきます。
作成は以下のCloudFormationテンプレートで作成します。

CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09"

Description: VPC Stack

Metadata:
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------# 
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label: 
          default: Parameters for VPC
        Parameters:
          - VPCCIDR
      - Label: 
          default: Parameters for Subnet
        Parameters:
          - PublicSubnet01CIDR
      - Label: 
          default: Parameters for Security Group
        Parameters:
          - MyIP

Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------# 
  VPCCIDR:
    Default: 172.16.0.0/16
    Type: String

  PublicSubnet01CIDR:
    Default: 172.16.0.0/24
    Type: String

  MyIP:
    Type: String

Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------# 
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags: 
        - Key: Name
          Value: backup-test-ap-northeast-3

# ------------------------------------------------------------#
# InternetGateway
# ------------------------------------------------------------# 
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags: 
        - Key: Name
          Value: backup-test-igw-ap-northeast-3

  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------# 
  PublicSubnet01:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-3a
      CidrBlock: !Ref PublicSubnet01CIDR
      MapPublicIpOnLaunch: true
      Tags: 
        - Key: Name
          Value: backup-test-public-subnet-01-ap-northeast-3
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------# 
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: backup-test-public-rtb-ap-northeast-3

  PublicRouteTableRoute1:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
      RouteTableId: !Ref PublicRouteTable

  PublicRtAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet01

# ------------------------------------------------------------#
# Security Group
# ------------------------------------------------------------# 
  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: for ec2
      GroupName: backup-test-sg-ec2
      SecurityGroupEgress: 
        - CidrIp: 0.0.0.0/0
          FromPort: -1
          IpProtocol: -1
          ToPort: -1
      SecurityGroupIngress:
        - CidrIp: !Ref MyIP
          FromPort: 80
          IpProtocol: tcp
          ToPort: 80
      Tags: 
        - Key: Name
          Value: backup-test-sg-ec2-ap-northeast-3
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# Backup
# ------------------------------------------------------------# 
  BackupVault:
    Type: AWS::Backup::BackupVault
    Properties:
      BackupVaultName: backup-test-vault-ap-northeast-3

Outputs:
# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------# 
  BackupVaultName:
    Value: !Ref BackupVault
    Export: 
      Name: BackupVaultName

  BackupVaultARN:
    Value: !GetAtt BackupVault.BackupVaultArn
    Export: 
      Name: BackupVaultARN

バックアップボールトは133~136行目で作成しています。
デプロイは以下のコマンドを実行します。

aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=MyIP,ParameterValue=WebサーバーへHTTPでアクセスする接続元のIPアドレス --region ap-northeast-3

大阪リージョンでデプロイするためコマンドのオプションで「--region ap-northeast-3」を付けています。

デプロイが完了したら東京リージョンにバックアップボールトと大阪リージョンにバックアップをコピーするバックアッププランを作成します。
作成は以下のCloudFormationテンプレートで作成します。

CloudFormationテンプレート (ここをクリックしてください)
AWSTemplateFormatVersion: "2010-09-09"

Description: Backup Stack

Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------# 
  DestinationBackupVaultArn:
    Type: String

Resources:
# ------------------------------------------------------------#
# Backup
# ------------------------------------------------------------# 
  BackupVault:
    Type: AWS::Backup::BackupVault
    Properties:
      BackupVaultName: backup-test-vault-ap-northeast-1

  BackupPlan:
    Type: AWS::Backup::BackupPlan
    Properties: 
      BackupPlan:
        BackupPlanName: backup-test-plan
        BackupPlanRule: 
          - CompletionWindowMinutes: 180
            CopyActions: 
              - DestinationBackupVaultArn: !Ref DestinationBackupVaultArn
                Lifecycle:
                  DeleteAfterDays: 1
            Lifecycle: 
              DeleteAfterDays: 1
            RuleName: backup-ec2
            ScheduleExpression: cron(0 13 * * ? *)
            StartWindowMinutes: 60
            TargetBackupVault: !Ref BackupVault

  BackupIAMPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties: 
      PolicyDocument:
        Version: "2012-10-17"
        Statement: 
          - Effect: Allow
            Action:
              - "iam:PassRole"
            Resource: 
              - "*"
      ManagedPolicyName: backup-policy

  BackupIamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: Allow
            Principal: 
              Service: 
                - backup.amazonaws.com
            Action: 
              - 'sts:AssumeRole'
      ManagedPolicyArns: 
        - arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup
        - arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForRestores
        - !Ref BackupIAMPolicy
      RoleName: backup-role

  BackupSelectionEC2:
    Type: AWS::Backup::BackupSelection
    Properties: 
      BackupPlanId: !Ref BackupPlan
      BackupSelection: 
        IamRoleArn: !GetAtt BackupIamRole.Arn
        ListOfTags: 
          - ConditionKey: Name
            ConditionType: STRINGEQUALS
            ConditionValue: backup-test-ec2-1-ap-northeast-1
        SelectionName: backup-selection-ec2

21~37行目でバックアッププランを作成しています。
「CopyActions」でコピー先のバックアップボールトを指定しています。
39~68行目でAWS Backupがバックアップを取得したり復元したりするときに使用するIAM周りを作成しています。
IAMポリシーで「iam:PassRole」を付けているのは復元時にIAMロールをEC2へアタッチできるようにするためです。
70~80行目でバックアップを取得するリソースを設定しています。
今回はNameタグで「backup-test-ec2-1-ap-northeast-1」がついているものが対象になるように設定しました。
デプロイは以下のコマンドを実行します。

aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --capabilities CAPABILITY_NAMED_IAM --parameters ParameterKey=DestinationBackupVaultArn,ParameterValue=コピー先バックアップボールトのARN (今回の場合は大阪リージョンのバックアップボールト)

デプロイが完了後、日本時間22時以降にバックアップが取得され始めます。
バックアップが完了すると以下の画像のようにバックアップボールトの復旧ポイントから確認することができます。

復元

復元は以下の公式ドキュメントの手順で行います。
Amazon EC2 インスタンスを復元する
まずはマネジメントコンソールでリージョンを大阪リージョンへ変更してAWS Backupのダッシュボードへ移動します。
移動後、画面左の項目から「保護されたリソース」をクリックします。

画面遷移後、復元する「リソースID」をクリックします。

画面遷移後、「復旧ポイント」を選択して右上にある「復元」をクリックします。

画面遷移後、「VPC」、「サブネット」、「セキュリティグループ」、ロールを復元で「ロール名」をCloudFormationで作成したものへ変更します。

「インスタンス IAM ロール」は元のIAMロールで復元にしておくことで東京リージョンで起動時に設定していたIAMロールが自動でアタッチされます。
※ロールを復元の選択を「デフォルトのロール」にするとIAMロールをアタッチする権限が無いため復元に失敗します。
変更後、画面下の「バックアップを復元」をクリックすると復元が開始されます。
復元が完了すると復元ジョブのステータスが「完了」になります。

復元が完了後、EC2のパブリックIPアドレスにブラウザからアクセスすると東京リージョンのEC2で確認した画面と同じものが表示されることが確認できます。

さいごに

クロスリージョンコピーはDR対策で使用するものになってくるので設定の流れを理解しておくとよいかと思いました。