AWS Backup から EC2 をリストアする時に同じ IP で復元する

# やりたいこと

EC2 インスタンスを AWS Backup から復旧させる場合、IP アドレスが変わらないようにしたい
AWS Backup から復旧させる場合、新しい EC2 インスタンスが正常に起動することを確認してから、古い EC2 インスタンスを削除するようにしたい


# 検証

EC2 インスタンスのネットワークインターフェイス 1 にプライマリ IP とセカンダリ IP を付与するが、Windows の設定でセカンダリ IP のみを利用し、プライマリ IP は使わないという選択肢です。

この場合、以下のような考慮が不要となる認識です。

- アプリケーション側で両方の IP を利用するのか、セカンダリ IP のみを利用するのか
- FW などでホストで穴あけをしていた場合、両方の IP を指定する必要がある
- リストア後に、Windows のネットワークの設定を変更しなくても良い
  ..etc

「リストア後に、Windows のネットワークの設定を変更しなくても良い」ことについて、もう少し書くと、

例えば、古い EC2 インスタンスで Windows のネットワークの設定を以下の通りとしたとします。

```
PS C:\Users\Administrator> ipconfig /all
イーサネット アダプター イーサネット 3:
   IPv4 アドレス . . . . . . . . . . . .: 10.10.10.30(優先)
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   IPv4 アドレス . . . . . . . . . . . .: 10.10.10.10(優先)
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .: 10.10.10.1
   DNS サーバー. . . . . . . . . . . . .: 10.10.0.2
```

古い EC2 インスタンスが削除されていない状態では、プライマリ IP はデタッチできないので、新しい EC2 インスタンスのプライマリ IP は変更するしかありません。そのため、次の通り新しい EC2 インスタンスを起動します。

- ネットワークインターフェイス 1
  - プライマリ IP: 10.10.10.20
  - セカンダリ IP: 10.10.10.30

しかし、先程の Windows のネットワークの設定では、10.10.10.30 と 10.10.10.10 が設定されているので、Windows のネットワークの設定も 10.10.10.10 を 10.10.10.20 に変更する必要が発生するということかなと思います。
ただし、古い EC2 インスタンスの Windows のネットワークの設定では、10.10.10.30 のみしか設定されていないため、セカンダリ IP をデタッチ後は一切接続できなくなります。

***ちょっと色々と図にしてみました。図を書き忘れたのですが、結局 EC2 インスタンスを削除する時に EBS は残すようにしておけばいいんじゃないか説。であれば、一番上の方法でいい気がする。***
![ec2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/54cb58ae-901e-cb08-2795-454799441d72.png)


## 古い EC2 インスタンスの起動

基本的には、ネットワークインターフェイス 1 に、プライマリ IP と セカンダリ IP を付与して起動しておくだけです。 

- ネットワークインターフェイス 1
     - プライマリ IP: 10.10.10.10
     - セカンダリ IP: 10.10.10.30

![スクリーンショット 2024-11-29 19.13.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/00613ab2-ffec-8722-ce64-2daa240228a8.png)

なお、ユーザデータで Powershell の New-NetIPAddress を利用して、IP アドレスを変更しようとしましたが、原則として IP アドレスへの変更はできないようです。（試してみましたが、実際にできなく、調べたらこのようでした。違ったらすいません。。。）

RDP 接続し、`ipconfig /all` で現在のネットワークを確認しておきます。

当然ですが、プライマリ IP: 10.10.10.10 のみしか認識していません。

```
PS C:\Users\Administrator> ipconfig /all
イーサネット アダプター イーサネット 3:
   IPv4 アドレス . . . . . . . . . . . .: 10.10.10.10(優先)
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .: 10.10.10.1
   DNS サーバー. . . . . . . . . . . . .: 10.10.0.2
```

セカンダリ IP: 10.10.10.30 が認識するように、Windows のネットワークの設定を変更していきます。

![スクリーンショット 2024-11-29 20.46.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/0851ee24-c790-3914-6e0d-535ad109cc09.png)


現在は、プライマリ IP: 10.10.10.10 で接続しているため、再度 RDP で接続し直し、再度`ipconfig /all` で現在のネットワークを確認しておきます。

```
PS C:\Users\Administrator> ipconfig /all
イーサネット アダプター イーサネット 3:
   IPv4 アドレス . . . . . . . . . . . .: 10.10.10.30(優先)
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .: 10.10.10.1
   DNS サーバー. . . . . . . . . . . . .: 10.10.0.2
```

## セカンダリ IP のデタッチ

旧 EC2 を停止し、セカンダリ IP: 10.10.10.30 をデタッチします。

```
aws ec2 stop-instances --instance-ids $(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=ec2" \
    --query "Reservations[].Instances[].InstanceId" \
    --output text)

aws ec2 unassign-private-ip-addresses \
    --network-interface-id $(aws ec2 describe-instances \
        --filters "Name=tag:Name,Values=ec2" \
        --query "Reservations[].Instances[].NetworkInterfaces[].NetworkInterfaceId" \
        --output text) \
    --private-ip-addresses 10.10.10.30
```

念の為、AWS コンソールからもセカンダリ IP: 10.10.10.30 がデタッチされていることを確認しておきましょう。

![スクリーンショット 2024-12-02 10.44.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/2228bd0a-7dcd-b910-6093-e89422e25ba7.png)


## AWS Backup からの復元

`aws backup list-recovery-points-by-backup-vault` で、「RecoveryPointArn」を確認します。

```
aws backup list-recovery-points-by-backup-vault \
    --backup-vault-name [backup-vault-name] \
    | jq '.RecoveryPoints[] | {RecoveryPointArn, Status, CreationDate, CompletionDate, ResourceName}'

{
  "RecoveryPointArn": "arn:aws:ec2:ap-northeast-1::image/ami-0cde33f354e4ddba7",
  "Status": "COMPLETED",
  "CreationDate": "2024-12-01T21:00:00+09:00",
  "CompletionDate": "2024-12-01T21:20:47.607000+09:00",
  "ResourceName": "ec2"
}
{
  "RecoveryPointArn": "arn:aws:ec2:ap-northeast-1::image/ami-0af302fc13f009a61",
  "Status": "COMPLETED",
  "CreationDate": "2024-11-30T21:00:00+09:00",
  "CompletionDate": "2024-11-30T21:20:21.768000+09:00",
  "ResourceName": "ec2"
}
```

`aws backup get-recovery-point-restore-metadata` で、バックアップの作成に使用されたメタデータを取得します。

この時に以下の処理をします。

-  メタデータの RestoreMetadata のみを取得
-  CpuOptions, SecurityGroupIds, SubnetId を削除
-  NetworkInterfaces 
    -  NetworkInterfaceId, PrivateIpAddresses, SecondaryPrivateIpAddressCount を削除
    -  AssociatePublicIpAddress を false に編集
    -  PrivateIpAddress 10.10.10.10 を 10.10.10.20 に置き換え

これにより、新しい EC2 の IP アドレスは以下の通りとなります。

- ネットワークインターフェイス 1
  - プライマリ IP: 10.10.10.20
  - セカンダリ IP: 10.10.10.30

```
backup_vault_name="Default"
RecoveryPointArn="arn:aws:ec2:ap-northeast-1::image/ami-0cde33f354e4ddba7"
old_ip="10.10.10.10"
new_ip="10.10.10.20"

aws backup get-recovery-point-restore-metadata \
    --backup-vault-name "$backup_vault_name" \
    --recovery-point-arn "$RecoveryPointArn" \
    | jq --arg oldIp "$old_ip" \
         --arg newIp "$new_ip" '
      .RestoreMetadata 
      | del(.CpuOptions)
      | del(.SecurityGroupIds)
      | del(.SubnetId)
      | .NetworkInterfaces |= (
          fromjson
          | map(
              del(.NetworkInterfaceId)
              | del(.PrivateIpAddress)
              | del(.SecondaryPrivateIpAddressCount)
              | (.AssociatePublicIpAddress = false)
              | (.PrivateIpAddresses |= map(
                  if .PrivateIpAddress == $oldIp then
                    .PrivateIpAddress = $newIp
                  else
                    .
                  end
                ))
            )
          | tojson
        )
    ' > restore_metadata.json
```

コマンド実行後のメタデータは以下の通りです。

```restore_metadata.json
{
  "Architecture": "x86_64",
  "CapacityReservationSpecification": "{\"CapacityReservationPreference\":\"open\"}",
  "CreditSpecification": "{\"CpuCredits\":\"unlimited\"}",
  "DisableApiTermination": "false",
  "EbsOptimized": "true",
  "EnaSupport": "true",
  "HibernationOptions": "{\"Configured\":false}",
  "IamInstanceProfileName": "AmazonSSMRoleForInstancesQuickSetup",
  "InstanceInitiatedShutdownBehavior": "stop",
  "InstanceType": "t3.small",
  "KeyName": "windows",
  "Monitoring": "{\"State\":\"disabled\"}",
  "NetworkInterfaces": "[{\"AssociatePublicIpAddress\":false,\"DeleteOnTermination\":true,\"Description\":\"\",\"DeviceIndex\":0,\"Groups\":[\"sg-0ab05c4d4946e22ee\"],\"Ipv6AddressCount\":0,\"Ipv6Addresses\":[],\"PrivateIpAddresses\":[{\"Primary\":true,\"PrivateIpAddress\":\"10.10.10.20\"},{\"Primary\":false,\"PrivateIpAddress\":\"10.10.10.30\"}],\"SubnetId\":\"subnet-0d6ec4e42c29ea23b\",\"InterfaceType\":\"interface\",\"Ipv4Prefixes\":[],\"Ipv6Prefixes\":[]}]",
  "Placement": "{\"AvailabilityZone\":\"ap-northeast-1a\",\"GroupName\":\"\",\"Tenancy\":\"default\"}",
  "Platform": "windows",
  "RequireIMDSv2": "false",
  "RootDeviceType": "ebs",
  "VirtualizationType": "hvm",
  "VpcId": "vpc-05d96ee40d75582bc",
  "aws:backup:request-id": "f577d135-1fc9-480c-9cb2-2a7883ae02f0"
}
```

では、復元の準備が整いましたので実行してみますが、メタデータファイルを用意して、CLI から実行する場合、AWS Backup の IAM ロールに `iam:PassRole` を付与してあげる必要があるようです。

https://dev.classmethod.jp/articles/tsnote-ec2-restore-backup-authorization-error/

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

`aws backup start-restore-job` で復元してみます。今回 AWS CLI は 1 つずつ実行していってますが、スクリプトにしても良いと思います。

```
aws backup start-restore-job --region ap-northeast-1 \
    --recovery-point-arn "$RecoveryPointArn" \
    --iam-role-arn "arn:aws:iam::123456789012:role/service-role/AWSBackupDefaultServiceRole" \
    --metadata file://restore_metadata.json \
    --copy-source-tags-to-restored-resource

{
    "RestoreJobId": "EE682088-127A-5D7C-7573-B2AEB43A00C4"
}
```

数分後、ジョブの実行が完了しているか確認します。ジョブの実行が完了するまでに数分かかるので、スクリプトで実装するのであれば、数分おきに Status が COMPLETED なっているか確認するようにすれば良いと思います。

```
aws backup describe-restore-job \
    --region ap-northeast-1 \
    --restore-job-id "EE682088-127A-5D7C-7573-B2AEB43A00C4" \
    --query "Status" \
    --output text

COMPLETED
```

## 新しい EC2 インスタンスの動作確認

AWS コンソールから、以下の IP アドレスになっていることを確認します。

- ネットワークインターフェイス 1
  - プライマリ IP: 10.10.10.20
  - セカンダリ IP: 10.10.10.30

![スクリーンショット 2024-12-02 14.13.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/54660288-ea2c-dcb6-552c-0fec002ca818.png)

RDP で接続し、ipconfig /all で現在のネットワークを確認し、セカンダリ IP: 10.10.10.30 のままであれば成功です。

```
PS C:\Users\Administrator> ipconfig /all
イーサネット アダプター イーサネット 3:
   IPv4 アドレス . . . . . . . . . . . .: 10.10.10.30(優先)
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .: 10.10.10.1
   DNS サーバー. . . . . . . . . . . . .: 10.10.0.2
```