AWS Backupからの復元時にEC2に任意のプライベートIPアドレスを割り当てる方法

# はじめに
前回の記事で、`AMI`からの復元時に任意のプライベートIPアドレスを指定する方法を紹介しましたが、今回は`AWS Backup`で取得しているバックアップから復元する際に任意のプライベートIPアドレスを割り当てる方法も紹介したいと思います。

なお、今回は趣旨が異なるため、`AWS Backup`自体の説明や設定方法は解説せず、`AWS Backup`の運用が既に行われている前提で進めます。

- 【前】[EC2のプライベートIPアドレスを任意で割り当てる方法とサブネットCIDR予約](https://qiita.com/sakai00kou/items/83b08529e6b07cb06598)

## AWS Backupからの復元について
`AWS Backup`は設定することでEC2やEFS、RDS等のバックアップを定期的に行ってくれるサービスとなりますが、`AWS Backup`で取得したEC2のバックアップから復元する場合、`AMI`から再作成する場合と異なり、以下のように簡易的なネットワーク設定しかできないようになっております。

![Monosnap_20240818_142930.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/0d166aeb-825b-be22-57aa-cdda3ab9880b.png)

AWSマネジメントコンソール上からは簡易的な設定しかできませんが、`AWS CLI`を使って`AWS Backup`を操作することで任意のプライベートIPアドレスを割り当てたEC2インスタンスを復元することができるため、以下より紹介していきます。

基本的には以下公式の手順に沿って進めていきますので、手順のみ確認したい方は公式手順を参照してください。

- [AWS Backup で Amazon EC2 の復元を行うときに、プライベート IP アドレスまたはパブリック IP アドレスを変更または保持する方法を教えてください。](https://repost.aws/ja/knowledge-center/backup-change-retain-ip-ec2-restore)

## 手順の流れ
`AWS Backup`からの復元時にEC2に任意のプライベートIPアドレスを割り当てるには、復元する内容が記載されたメタデータを用意して、`AWS Backup`の復元コマンド実行時に用意したメタデータを読み込ませれば設定できます。

メタデータの作成には少し工夫が必要なので、以下より進めていきます。

## AWS Backupで作成したバックアップイメージのメタデータ取得
`AWS Backup`からの復元コマンドに読み込ませるメタデータの雛形を作成するため、`AWS Backup`でバックアップ済みのイメージからメタデータを取得します。

コマンドで取得する際に必要となる「**バックアップボールト名**」はAWS Backupダッシュボードの「**ボールト**」から作成済みのボールト名を確認してください。

同じく必要となる「**復元ポイントARN**」はAWS Backupダッシュボードの「**ジョブ**」→「**バックアップジョブ**」タブからメタデータを取得したいEC2の「**バックアップジョブID**」を選択。

![Monosnap_20240818_152207.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/1d88342d-8c74-27aa-c4a4-22def503fe28.png)

選択すると、以下のように「**復元ポイントARN**」を確認することができるため、ARNを控えます。

![Monosnap_20240818_152921.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/4830440b-2ce7-45d5-f59d-88fa3fa0ac03.png)

「**バックアップボールト名**」、「**復元ポイントARN**」確認後、以下コマンドを実行してください。

```bash:バックアップ済みイメージのメタデータ取得
aws backup get-recovery-point-restore-metadata --backup-vault-name [バックアップボールト名] --recovery-point-arn [復元ポイントARN] > metadata_template.json
```

:::note info
途中で内容が切れてしまう可能性があるため、コマンド結果をファイルに出力するようにしてください。
:::

先程のメタデータ取得コマンドを実行すると以下のような結果が`metadata_template.json`に出力されるため、以下を雛形にして次項より復元時に使用するメタデータファイルを作成していきます。

```json:コマンド出力したmetadata_template.jsonの表示例
{
    "BackupVaultArn": "arn:aws:backup:ap-northeast-1:123456789012:backup-vault:aws-backup-vault",
    "RecoveryPointArn": "arn:aws:ec2:ap-northeast-1::image/ami-084ddc30cf536ac1e",
    "RestoreMetadata": {
        "Architecture": "x86_64",
        "CapacityReservationSpecification": "{\"CapacityReservationPreference\":\"open\"}",
        "CpuOptions": "{\"CoreCount\":1,\"ThreadsPerCore\":1}",
        "CreditSpecification": "{\"CpuCredits\":\"standard\"}",
        "DisableApiTermination": "false",
        "EbsOptimized": "false",
        "EnaSupport": "true",
        "HibernationOptions": "{\"Configured\":false}",
        "IamInstanceProfileName": "ec2_role",
        "InstanceInitiatedShutdownBehavior": "stop",
        "InstanceType": "t2.micro",
        "KeyName": "ec2-keypair",
        "Monitoring": "{\"State\":\"disabled\"}",
        "NetworkInterfaces": "[{\"AssociatePublicIpAddress\":true,\"DeleteOnTermination\":true,\"Description\":\"\",\"DeviceIndex\":0,\"Groups\":[\"sg-06d3f4fca2b07b915\"],\"Ipv6AddressCount\":0,\"Ipv6Addresses\":[],\"NetworkInterfaceId\":\"eni-0196b22fc136f1f81\",\"PrivateIpAddress\":\"10.1.10.115\",\"PrivateIpAddresses\":[{\"Primary\":true,\"PrivateIpAddress\":\"10.1.10.115\"},{\"Primary\":false,\"PrivateIpAddress\":\"10.1.10.100\"}],\"SecondaryPrivateIpAddressCount\":1,\"SubnetId\":\"subnet-0bdb9444b6747e0d6\",\"InterfaceType\":\"interface\",\"Ipv4Prefixes\":[],\"Ipv6Prefixes\":[]}]",
        "Placement": "{\"AvailabilityZone\":\"ap-northeast-1a\",\"GroupName\":\"\",\"Tenancy\":\"default\"}",
        "RequireIMDSv2": "true",
        "RootDeviceType": "ebs",
        "SecurityGroupIds": "[\"sg-06d3f4fca2b07b915\"]",
        "SubnetId": "subnet-0bdb9444b6747e0d6",
        "VirtualizationType": "hvm",
        "VpcId": "vpc-0038965ef3ab23888",
        "aws:backup:request-id": "19f9ec92-f9a8-4a5f-9deb-8d790eb346b7"
    }
}
```

## 復元時に指定するメタデータの作成
復元時に必要となるメタデータは`RestoreMetadata`部分となるため、`RestoreMetadata`部分を雛形として抜き出し、`AWS Backup`からの復元時に読み込ませるメタデータを作成します。

以下`RestoreMetadata`のみ抜き出した雛形。

```json:RestoreMetadata部分のみ抜粋した雛形
{
  "Architecture": "x86_64",
  "CapacityReservationSpecification": "{\"CapacityReservationPreference\":\"open\"}",
  "CpuOptions": "{\"CoreCount\":1,\"ThreadsPerCore\":1}",
  "CreditSpecification": "{\"CpuCredits\":\"standard\"}",
  "DisableApiTermination": "false",
  "EbsOptimized": "false",
  "EnaSupport": "true",
  "HibernationOptions": "{\"Configured\":false}",
  "IamInstanceProfileName": "ec2_role",
  "InstanceInitiatedShutdownBehavior": "stop",
  "InstanceType": "t2.micro",
  "KeyName": "ec2-keypair",
  "Monitoring": "{\"State\":\"disabled\"}",
  "NetworkInterfaces": "[{\"AssociatePublicIpAddress\":true,\"DeleteOnTermination\":true,\"Description\":\"\",\"DeviceIndex\":0,\"Groups\":[\"sg-06d3f4fca2b07b915\"],\"Ipv6AddressCount\":0,\"Ipv6Addresses\":[],\"NetworkInterfaceId\":\"eni-0196b22fc136f1f81\",\"PrivateIpAddress\":\"10.1.10.115\",\"PrivateIpAddresses\":[{\"Primary\":true,\"PrivateIpAddress\":\"10.1.10.115\"},{\"Primary\":false,\"PrivateIpAddress\":\"10.1.10.100\"}],\"SecondaryPrivateIpAddressCount\":1,\"SubnetId\":\"subnet-0bdb9444b6747e0d6\",\"InterfaceType\":\"interface\",\"Ipv4Prefixes\":[],\"Ipv6Prefixes\":[]}]",
  "Placement": "{\"AvailabilityZone\":\"ap-northeast-1a\",\"GroupName\":\"\",\"Tenancy\":\"default\"}",
  "RequireIMDSv2": "true",
  "RootDeviceType": "ebs",
  "SecurityGroupIds": "[\"sg-06d3f4fca2b07b915\"]",
  "SubnetId": "subnet-0bdb9444b6747e0d6",
  "VirtualizationType": "hvm",
  "VpcId": "vpc-0038965ef3ab23888",
  "aws:backup:request-id": "19f9ec92-f9a8-4a5f-9deb-8d790eb346b7"
}
```

上記のうち、以下の項目と`NetworkInterfaces`項目の一部の設定は削る必要があるため、雛形から削ります。

|削る項目|備考|
|:--|:--|
|CpuOptions|復元時に使用するメタデータには指定できないため削除が必要|
|NetworkInterfacesの一部項目|後述|
|SecurityGroupIds|NetworkInterfaces内の設定と被るため削除・修正が必要|
|SubnetId|NetworkInterfaces内の設定と被るため削除が必要|

### NetworkInterfacesの項目の修正
`NetworkInterfaces`の項目は横に長く見づらいため、抜粋して少し見やすく修正したものを以下に示します。

```json:NetworkInterfaces項目抜粋＆整形
"NetworkInterfaces": "[{
  \"AssociatePublicIpAddress\":true,
  \"DeleteOnTermination\":true,
  \"Description\":\"\",
  \"DeviceIndex\":0,
  \"Groups\":[\"sg-06d3f4fca2b07b915\"],
  \"Ipv6AddressCount\":0,
  \"Ipv6Addresses\":[],
  \"NetworkInterfaceId\":\"eni-0196b22fc136f1f81\",
  \"PrivateIpAddress\":\"10.1.10.115\",
  \"PrivateIpAddresses\":[
    {
      \"Primary\":true,
      \"PrivateIpAddress\":\"10.1.10.115\"
    },
    {
      \"Primary\":false,
      \"PrivateIpAddress\":\"10.1.10.100\"
    }
  ],
  \"SecondaryPrivateIpAddressCount\":1,
  \"SubnetId\":\"subnet-0bdb9444b6747e0d6\",
  \"InterfaceType\":\"interface\",
  \"Ipv4Prefixes\":[],
  \"Ipv6Prefixes\":[]
}]",
```

抜粋した`NetworkInterfaces`の項目のうち、以下は削る必要があるため削除します。

ややこしいですが、`NetworkInterfaces`の項目には`PrivateIpAddress`と`PrivateIpAddresses`の項目があり、`PrivateIpAddress`のみ削除します。

|削る項目|備考|
|:--|:--|
|NetworkInterfaceId|復元時に新規に作成されるため削除|
|PrivateIpAddress|PrivateIpAddress**es**の項目内で指定するため削除|

また、私の環境では、バックアップ元の設定はパブリックIPを割り当てるようにはしていなかった（はず）ですが、パブリックIPアドレスを割り当てる`AssociatePublicIpAddress`がtrueになっていたため、falseに修正しておきます。

|修正する項目|備考|
|:--|:--|
|AssociatePublicIpAddress|パブリックIPアドレスが割り振られないようにfalseに修正|

`PrivateIpAddresses`の部分に関しては今回のメインの設定部分となるため、次項目で説明します。

### PrivateIpAddressesの項目の設定
以下、先ほど`NetworkInterfaces`部分のみ抜粋した項目のうち、復元時に設定するIPアドレス設定部分のみ更に抜粋したものですが、復元時に設定したいIPアドレス構成によって内容を修正します。

```json:IPアドレス設定部分の抜粋
  \"PrivateIpAddresses\":[
    {
      \"Primary\":true,
      \"PrivateIpAddress\":\"10.1.10.115\"
    },
    {
      \"Primary\":false,
      \"PrivateIpAddress\":\"10.1.10.100\"
    }
  ],
  \"SecondaryPrivateIpAddressCount\":1,
```

ややこしいので設定したい内容ごとにまとめます。

- [プライマリIP動的、セカンダリIP無しで設定する場合](#プライマリip動的セカンダリip無しで設定する場合)
- [プライマリIP固定、セカンダリIP無しで設定する場合](#プライマリip固定セカンダリip無しで設定する場合)
- [プライマリIP固定、セカンダリIP動的で設定する場合](#プライマリip固定セカンダリip動的で設定する場合)
- [プライマリIP固定、セカンダリIP固定で設定する場合](#プライマリip固定セカンダリip固定で設定する場合)
- [プライマリIP動的、セカンダリIP固定で設定する場合](#プライマリip動的セカンダリip固定で設定する場合)

#### プライマリIP動的、セカンダリIP無しで設定する場合
特に任意のプライベートIPを割り当てることはせず、プライマリIPのみで作成する場合、先ほど抜粋したIPアドレス設定部分は全て削除でOKです。

EC2復元時、何も指定していなくても必ず１つのネットワークは作成されることから、通常通りAWS側から動的に１つのIPアドレスが割り当たります。

#### プライマリIP固定、セカンダリIP無しで設定する場合
プライマリIPを`10.1.10.100`で設定し、セカンダリIPは無しで設定する場合、`PrivateIpAddresses`の`Primary`をtrue、`PrivateIpAddress`を`10.1.10.100`と指定します。

セカンダリIPの設定は行っていないため、任意に指定したプライマリIPアドレスが割り当てられたEC2が復元されます。

```json:プライマリIP固定、セカンダリIP無しで設定する場合の例
  \"PrivateIpAddresses\":[
    {
      \"Primary\":true,
      \"PrivateIpAddress\":\"10.1.10.100\"
    }
  ],
```

#### プライマリIP固定、セカンダリIP動的で設定する場合
プライマリIPを`10.1.10.100`、セカンダリIPは作成するがAWSから動的に割り当てたい場合、先程の例に`SecondaryPrivateIpAddressCount`の設定を加えます。

`SecondaryPrivateIpAddressCount`はいくつのセカンダリIPを作成するかの指定となり、「**1**」とすれば、プライマリIPとは別に１つのセカンダリIPが作成されます。

```json:プライマリIP固定、セカンダリIP動的で設定する場合の例
  \"PrivateIpAddresses\":[
    {
      \"Primary\":true,
      \"PrivateIpAddress\":\"10.1.10.110\"
    },
  ],
  \"SecondaryPrivateIpAddressCount\":1,
```

#### プライマリIP固定、セカンダリIP固定で設定する場合
プライマリIPを`10.1.10.100`、セカンダリIPを`10.1.10.101`でどちらも固定で割り当てたい場合、プライマリIPの固定方法と同じような設定を加えます。

但し、`Primary`はfalseにする必要があるので注意してください。

また、今回の場合、セカンダリIPアドレスは固定で指定することから、`SecondaryPrivateIpAddressCount`の設定は冗長となるため不要となります。

```json:プライマリIP固定、セカンダリIP固定で設定する場合の例
  \"PrivateIpAddresses\":[
    {
      \"Primary\":true,
      \"PrivateIpAddress\":\"10.1.10.100\"
    },
    {
      \"Primary\":false,
      \"PrivateIpAddress\":\"10.1.10.101\"
    }
  ],
```

#### プライマリIP動的、セカンダリIP固定で設定する場合
ここまでくれば設定がなんとなく予想できるかと思いますが、プライマリIPをAWS側から動的に割り当てて、セカンダリIPを`10.1.10.100`で指定するには先程の設定からプライマリIPの設定部分を削除した設定を行います。

```json:プライマリIP固定、セカンダリIP固定で設定する場合の例
  \"PrivateIpAddresses\":[
    {
      \"Primary\":false,
      \"PrivateIpAddress\":\"10.1.10.100\"
    }
  ],
```

### 最終的なメタデータ
「[プライマリIP動的、セカンダリIP固定で設定する場合](#プライマリip動的セカンダリip固定で設定する場合)」で設定した、復元実行用の最終的なメタデータファイル例（metadata.json）を以下に示します。

説明用に見やすくした`NetworkInterfaces`の項目は元の１行の記載に戻してください。

```json:metadata.json
{
  "Architecture": "x86_64",
  "CapacityReservationSpecification": "{\"CapacityReservationPreference\":\"open\"}",
  "CreditSpecification": "{\"CpuCredits\":\"standard\"}",
  "DisableApiTermination": "false",
  "EbsOptimized": "false",
  "EnaSupport": "true",
  "HibernationOptions": "{\"Configured\":false}",
  "IamInstanceProfileName": "ec2_role",
  "InstanceInitiatedShutdownBehavior": "stop",
  "InstanceType": "t2.micro",
  "KeyName": "ec2-keypair",
  "Monitoring": "{\"State\":\"disabled\"}",
  "NetworkInterfaces": "[{\"AssociatePublicIpAddress\":false,\"DeleteOnTermination\":true,\"Description\":\"\",\"DeviceIndex\":0,\"Groups\":[\"sg-06d3f4fca2b07b915\"],\"Ipv6AddressCount\":0,\"Ipv6Addresses\":[],\"PrivateIpAddresses\":[{\"Primary\":false,\"PrivateIpAddress\":\"10.1.10.100\"}],\"SubnetId\":\"subnet-0bdb9444b6747e0d6\",\"InterfaceType\":\"interface\",\"Ipv4Prefixes\":[],\"Ipv6Prefixes\":[]}]",
  "Placement": "{\"AvailabilityZone\":\"ap-northeast-1a\",\"GroupName\":\"\",\"Tenancy\":\"default\"}",
  "RequireIMDSv2": "true",
  "RootDeviceType": "ebs",
  "VirtualizationType": "hvm",
  "VpcId": "vpc-0038965ef3ab23888",
  "aws:backup:request-id": "19f9ec92-f9a8-4a5f-9deb-8d790eb346b7"
}
```

## AWS Backup使用ロールへのPassRoleポリシー付与
`AWS Backup`による復元実行時、`AWS Backup`に付与されているIAMロールの権限で復元を行いますが、マネジメントコンソール上から復元するのではなく、今回のようにメタデータファイルを用意してコマンドから復元し、EC2にインスタンスプロファイルでIAMロールも付与する場合、`AWS Backup`がデフォルトで使用するIAMロールでは`PassRole`権限が無いためEC2へのIAMロール付与に失敗してしまいます。

:::note info
調査しきれていませんが、マネジメントコンソール上から復元する場合はバックアップイメージのメタデータに付与されているIAMロールがそのまま復元されて使用することからマネジメントコンソール上から復元する場合はPassRole権限が不要なようです。

おそらくメタデータファイルを使用することで、バックアップイメージに付与されているEC2のIAMロールがリセットされ、AWS Backupから再度IAMロールをアタッチする必要があるのではないかと思われるため、コマンドで行う場合はPassRole権限の付与が必要なのだと思います。
:::

`AWS Backup`が使用しているIAMロールを確認するには、AWS Backupダッシュボードの「**バックアッププラン**」から「**バックアッププラン名**」を選択し、「**リソースの割り当て**」の「**IAMロールARN**」から確認できます。

`AWS Backup`デフォルトのロールを使用している場合、ARNは`arn:aws:iam::123456789012:role/service-role/AWSBackupDefaultServiceRole`（`123456789012`は自分のアカウントID）となるため、今回はデフォルトのロールにインラインポリシーで付与する場合を想定して手順を記載します。（もちろん新たにポリシーを作成して付与してもOK）

`AWSBackupDefaultServiceRole`のロールから「**許可を追加**」→「**インラインポリシーを作成**」で以下のポリシーを付与してください。

```json:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EC2PassRole",
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": ["*"]
        }
    ]
}
```

説明簡略化のため、リソース指定は「＊」としましたが、制限する場合は、EC2に割り当てるIAMロールのARNを指定してください。

## AWS Backupによる復元の実行
作成したメタデータファイルを使用し、以下コマンドで`AWS Backup`から復元します。

`AWS Backup`のデフォルトロールを使用する方法で以下記載していますが、自分の環境に合わせてロールARNを指定してください。

```bash:metadata.jsonファイルを使った復元の実行
aws backup start-restore-job --region [リージョン名] --recovery-point-arn "[復元ポイントARN]" --iam-role-arn "arn:aws:iam::123456789012:role/service-role/AWSBackupDefaultServiceRole" --metadata file://metadata.json
```

復元が成功すれば、AWS Backupダッシュボードの「**ジョブ**」→「**復元ジョブ**」タブのステータスが「**完了**」となっているはずです。

![Monosnap_20240818_201551.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/7718144a-5ad3-10fd-e53b-7aa8bd270cc8.png)

失敗している場合はメタデータの設定が失敗していたり、IPアドレスが重複していたりするかと思いますので、復元ジョブの結果を確認してください。

# おわりに
前回からの続きで、`AWS Backup`での復元時にプライベートIPアドレスを割り当てる方法を紹介しました。

今まで`AWS Backup`はAWSマネジメントコンソール上からの復元しか行ったことがありませんでしたが、コマンドから行うと任意のIPアドレスの割り当てなど色々と設定ができる反面、ハマりポイントが多く、しっかり検証しないと使いこなすのは難しいと感じました。

IPアドレス設定以外にも`AWS Backup`での復元時に詳細な設定を行いたい場合はEC2の`RunInstances`のAPIリファレンス等を参考にメタデータファイルを作成するとできるかもしれませんので、どうしても凝った設定を行いたい方はやってみてください。

- [RunInstances](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/APIReference/API_RunInstances.html)