AWSバックアップを別リージョンにレプリケーションしてみます。

概要

前回の続きです。

https://inugami-techlab.com/posts/20250603/

今回はバックアップを別リージョンにレプリケーション(今回は大阪リージョン)にしてみます。

この記事はそのメモやまとめです。
自分の環境用のメモでもあるので、省略している記載もあります。
すでに設定済みの項目がいくつかある。
試行錯誤しながらやったので、手順として不要かもしれない。あくまでメモ用。

EFS

EFSは前回と同じものを利用するので、省略します。

BackUp

他リージョンへのレプリケーション設定はplanから設定します。
requirements.tfに大阪リージョン用のProviderを記載することで、リージョンを複数利用できるようにします。

provider "aws" {
  region = "ap-northeast-1"
}

provider "aws" {
  alias  = "osaka"
  region = "ap-northeast-3"
}

大阪用のvault記載時にこのproviderを指定することで大阪リージョンにvaultを作成することができる。

resource "aws_backup_vault" "efs_backup_vault_osaka" {
  provider = aws.osaka
  name     = "efs-backup-vault-osaka"
}

各Ruleで以下のようにcopy_actionでレプリケーション先のvaultの指定とライフサイクルを設定させます。
今回はレプリケーションではないバックアップと同じ期間でライフサイクルを設定しました。

    copy_action {
      destination_vault_arn = aws_backup_vault.efs_backup_vault_osaka.arn
      lifecycle {
        delete_after = 7
      }
    }
  }

出来上がったEFSのコードは以下です。
レプリケーション用のIAMロールも追加しています。

# ----------------------------------------- #
# BackupVault
# ----------------------------------------- #

resource "aws_backup_vault" "efs_backup_vault" {
  name = "efs-backup-vault"
}

resource "aws_backup_vault" "efs_backup_vault_osaka" {
  provider = aws.osaka
  name     = "efs-backup-vault-osaka"
}


# ----------------------------------------- #
# BackupPlan
# ----------------------------------------- #

resource "aws_backup_plan" "efs_backup_plan" {
  name = "efs-backup-plan"

  rule {
    rule_name                    = "immediate_test_backup"
    target_vault_name            = aws_backup_vault.efs_backup_vault.name
    schedule                     = "cron(50 10 * * ? *)"
    schedule_expression_timezone = "Asia/Tokyo"
    start_window                 = 60
    completion_window            = 120

    lifecycle {
      delete_after = 7
    }

    recovery_point_tags = {
      BackupType = "Test"
    }

    copy_action {
      destination_vault_arn = aws_backup_vault.efs_backup_vault_osaka.arn
      lifecycle {
        delete_after = 7
      }
    }
  }

  # 通常の日次バックアップ(コールドストレージ制約を満たす設定)
  rule {
    rule_name                    = "daily_backup"
    target_vault_name            = aws_backup_vault.efs_backup_vault.name
    schedule                     = "cron(0 2 * * ? *)" # 毎日午前2時
    schedule_expression_timezone = "Asia/Tokyo"

    lifecycle {
      cold_storage_after = 30  # 30日後にコールドストレージ
      delete_after       = 120 # 120日で削除(90日間隔を満たす)
    }

    recovery_point_tags = {
      BackupType = "Daily"
    }

    copy_action {
      destination_vault_arn = aws_backup_vault.efs_backup_vault_osaka.arn
      lifecycle {
        cold_storage_after = 30  # 30日後にコールドストレージ
        delete_after       = 120 # 120日で削除(90日間隔を満たす)
      }
    }
  }
}

# ----------------------------------------- #
# BackupSelection
# ----------------------------------------- #

resource "aws_iam_role" "backup_role" {
  name = "efs-backup-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "backup.amazonaws.com"
        }
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "backup_policy" {
  role       = aws_iam_role.backup_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup"
}

resource "aws_iam_role_policy_attachment" "restore_policy" {
  role       = aws_iam_role.backup_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForRestores"
}

resource "aws_backup_selection" "efs_backup_selection" {
  iam_role_arn = aws_iam_role.backup_role.arn
  name         = "efs-backup-selection"
  plan_id      = aws_backup_plan.efs_backup_plan.id

  resources = [
    aws_efs_file_system.low_cost.arn
  ]

  condition {
    string_equals {
      key   = "aws:ResourceTag/BackupEnabled"
      value = "true"
    }
  }
}

# ----------------------------------------- #
# レプリケーション用のIAMロール
# ----------------------------------------- #

resource "aws_iam_role" "backup_replication_role" {
  name = "efs-backup-replication-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "backup.amazonaws.com"
        }
      }
    ]
  })

  tags = {
    Name = "EFS Backup Replication Role"
  }
}

resource "aws_iam_role_policy_attachment" "backup_replication_policy" {
  role       = aws_iam_role.backup_replication_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup"
}

resource "aws_iam_role_policy_attachment" "backup_replication_restore_policy" {
  role       = aws_iam_role.backup_replication_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForRestores"
}

# クロスリージョンレプリケーション用の追加ポリシー
resource "aws_iam_role_policy" "backup_cross_region_policy" {
  name = "backup-cross-region-policy"
  role = aws_iam_role.backup_replication_role.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "backup:CopyIntoBackupVault",
          "backup:DescribeBackupVault",
          "backup:DescribeRecoveryPoint"
        ]
        Resource = "*"
      }
    ]
  })
}

上記でapplyしてリソースを作成する。 GUIを確認するとバックアップとレプリケーションに成功していた。

001

CLIでも確認する

terraform/terraform_test on  master [📝] via 💠 default took 2s 
at 11:36:01 ⬢ [Docker] ❯ aws backup list-copy-jobs --region ap-northeast-1
{
    "CopyJobs": [
        {
            "AccountId": "xxxx",
            "CopyJobId": "E3DE69BA-8FC6-ED34-D18A-A3588A9BF60A",
            "SourceBackupVaultArn": "arn:aws:backup:ap-northeast-1:xxxx:backup-vault:efs-backup-vault",
            "SourceRecoveryPointArn": "arn:aws:backup:ap-northeast-1:xxxx:recovery-point:c53909a1-b8b2-4144-8ef6-25242913bee4",
            "DestinationBackupVaultArn": "arn:aws:backup:ap-northeast-3:xxxx:backup-vault:efs-backup-vault-osaka",
            "DestinationRecoveryPointArn": "arn:aws:backup:ap-northeast-3:xxxx:recovery-point:2580eca4-f799-4504-bd13-af63392fc355",
            "ResourceArn": "arn:aws:elasticfilesystem:ap-northeast-1:xxxx:file-system/fs-040c3f105bf89e47e",
            "CreationDate": "2025-06-04T11:09:21.202000+09:00",
            "CompletionDate": "2025-06-04T11:30:26.891000+09:00",
            "State": "COMPLETED",
            "BackupSizeInBytes": 73,
            "IamRoleArn": "arn:aws:iam::xxxx:role/efs-backup-role",
            "CreatedBy": {
                "BackupPlanId": "32299a85-0ead-41cd-9be6-c8b8ec867496",
                "BackupPlanArn": "arn:aws:backup:ap-northeast-1:xxxx:backup-plan:32299a85-0ead-41cd-9be6-c8b8ec867496",
                "BackupPlanVersion": "NGNkNGM5Y2YtMjYzOC00MTFmLTkzMmEtY2NlOTYwNzhlMjll",
                "BackupRuleId": "4277d50e-0443-4ca3-9317-6228fbf237bb"
            },
            "ResourceType": "EFS",
            "IsParent": false,
            "ResourceName": "low-cost-efs"
        }
    ]
}
terraform/terraform_test on  master [📝] via 💠 default 
at 11:36:07 ⬢ [Docker] ❯ 

terraform/terraform_test on  master [📝] via 💠 default 
at 11:37:21 ⬢ [Docker] ❯ aws backup list-backup-vaults --region ap-northeast-3
{
    "BackupVaultList": [
        {
            "BackupVaultName": "efs-backup-vault-osaka",
            "BackupVaultArn": "arn:aws:backup:ap-northeast-3:xxxx:backup-vault:efs-backup-vault-osaka",
            "CreationDate": "2025-06-04T10:42:04.489000+09:00",
            "EncryptionKeyArn": "arn:aws:kms:ap-northeast-3:xxxx:key/a95338a8-6879-481d-baa8-31c60dffc969",
            "NumberOfRecoveryPoints": 1,
            "Locked": false
        }
    ]
}
terraform/terraform_test on  master [📝] via 💠 default 
at 11:37:28 ⬢ [Docker] ❯ 

terraform/terraform_test on  master [📝] via 💠 default 
at 11:37:28 ⬢ [Docker] ❯ aws backup list-recovery-points-by-backup-vault --backup-vault-name efs-backup-vault-osaka --region ap-northeast-3  
{
    "RecoveryPoints": [
        {
            "RecoveryPointArn": "arn:aws:backup:ap-northeast-3:xxxx:recovery-point:2580eca4-f799-4504-bd13-af63392fc355",
            "BackupVaultName": "efs-backup-vault-osaka",
            "BackupVaultArn": "arn:aws:backup:ap-northeast-3:xxxx:backup-vault:efs-backup-vault-osaka",
            "SourceBackupVaultArn": "arn:aws:backup:ap-northeast-1:xxxx:backup-vault:efs-backup-vault",
            "ResourceArn": "arn:aws:elasticfilesystem:ap-northeast-1:xxxx:file-system/fs-040c3f105bf89e47e",
            "ResourceType": "EFS",
            "CreatedBy": {
                "BackupPlanId": "32299a85-0ead-41cd-9be6-c8b8ec867496",
                "BackupPlanArn": "arn:aws:backup:ap-northeast-1:xxxx:backup-plan:32299a85-0ead-41cd-9be6-c8b8ec867496",
                "BackupPlanVersion": "NGNkNGM5Y2YtMjYzOC00MTFmLTkzMmEtY2NlOTYwNzhlMjll",
                "BackupRuleId": "4277d50e-0443-4ca3-9317-6228fbf237bb"
            },
            "IamRoleArn": "arn:aws:iam::xxxx:role/efs-backup-role",
            "Status": "COMPLETED",
            "CreationDate": "2025-06-04T10:50:00+09:00",
            "CompletionDate": "2025-06-04T11:30:22.969000+09:00",
            "BackupSizeInBytes": 73,
            "CalculatedLifecycle": {
                "DeleteAt": "2025-06-11T10:50:00+09:00"
            },
            "Lifecycle": {
                "DeleteAfterDays": 7
            },
            "EncryptionKeyArn": "arn:aws:kms:ap-northeast-3:xxxx:key/a95338a8-6879-481d-baa8-31c60dffc969",
            "IsEncrypted": true,
            "IsParent": false,
            "ResourceName": "low-cost-efs"
        }
    ]
}
terraform/terraform_test on  master [📝] via 💠 default 
at 11:37:53 ⬢ [Docker] ❯ 

メモ

  • レプリケーションは通常のバックアップ完了後、レプリケーション完了まで少しラグがある
  • レプリケーションが完了したかはジョブ一覧の「コピージョブ」から確認できる

他にやりたいこと

  • Selectionでtag名に「develop*」の名前がつくリソースのみをバックアップの対象とする

参考記事

https://dev.classmethod.jp/articles/terraform-backup-copy-other-region/

終わりに

やっぱりterraformのほうが使いやすいと感じる。
現状通常業務であればterraformを利用し続けることにはなると思うので、もう少しterraformには強くなりたい
(OpenTofuを触るのは必要が出てきたらでも間に合いそう)