aws インスタンス開始時にRoute53のレコードのIPアドレスを自動更新する

気分次第で記事公開しました。多分4年ぶりの投稿です。

## はじめに

最近、何かとインフラ部分を任されることがあり、マイクラやARKなどのゲームサーバやGitLabやRedmineなどのAppサーバ等々、いろいろなものを構築するようになりました。
コスト削減のため、ゲームサーバのような使う時だけインスタンスを起動する運用を行っている場合、パブリックIPが起動する都度変わってしまうのは煩わしいですよね。
今回はできるだけ安く、簡単にRoute53レコードのIPアドレスを更新したいということで、その設定方法を記載します。ちなみに、この[記事](https://wellknowledge.org/no-aws-elastic-ip/)を参考に設定しています。

※ 今回はOSを最新のものにしたかったので、AL2023のAMIで設定しています

## ロールとポリシーを作成

参考にしたサイトでは、アクセスキーとシークレットキーを発行しています。AWSはアクセスキーを発行することは非推奨で、IAM Roleといったセキュリティ認証情報を使用することをベストプラクティスとしています（[参考](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_access-keys.html)）。

今回はそこまで大きな権限を渡すわけではないのですが、EC2にIAM Roleを紐づけることでRoute53のレコードを更新する処理を実現します。

- IAM Policyを作成
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "route53:ChangeResourceRecordSets",
                "route53:ListResourceRecordSets"
            ],
            "Resource": "arn:aws:route53:::hostedzone/<ゾーンID>"
        }
    ]
}
```

- IAM Roleを作成
  - 信頼されたエンティティタイプ: カスタム信頼ポリシーを選択し、以下jsonを入力
  ```json
  {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
  }
  ```
  - 許可ポリシー: 先ほど作成したポリシーを選択
   

## レコード更新のスクリプト作成

boto3でAWSリソースに変更を加えます。
Name, Type, TTLが一致するレコードに対してIPアドレスを更新するため、Route53で作成したレコードの設定を見比べてコードを書き換えてください。

```python
import boto3
import requests

AWS_HOST_ZONE_ID="<ゾーンID>"
AWS_RECORD_NAME="<レコード名>"

client = boto3.client('route53')

response = client.list_resource_record_sets(
    HostedZoneId=AWS_HOST_ZONE_ID,
    StartRecordName=AWS_RECORD_NAME,
    StartRecordType='A',
    MaxItems='1'
)

now_ip=response['ResourceRecordSets'][0]['ResourceRecords'][0]['Value']
new_ip=requests.get("http://httpbin.org/ip").json()['origin']

if now_ip != new_ip:
    response = client.change_resource_record_sets(
        HostedZoneId=AWS_HOST_ZONE_ID,
        ChangeBatch={
            'Changes': [
                {
                    'Action': 'UPSERT',
                    'ResourceRecordSet': {
                        'Name': AWS_RECORD_NAME,
                        'Type': 'A',
                        'TTL': 300, # Route53の設定に合わせること
                        'ResourceRecords': [
                            {
                                'Value': new_ip
                            },
                        ],
                    }
                },
            ]
        }
        )
```

### 動作確認

必要なライブラリをインストール後、作成したpythonのファイルを実行してみましょう。
特に出力がなければ、おそらくRoute53のレコードのIPアドレスが変わっていることでしょう。変わっていなければ、レコード検索内容が誤っているかIAM Role、Policyの設定がミスっているかなどが考えられるので、確認してください。

## 自動起動設定

動作確認できましたら、pythonファイルを自動で実行するように設定します。
今回は、スクリプトの呼び出しにchkconfigを用いることにしました。ユーザデータからインスタンス開始ごとに呼び出すこともできるので、好きなやり方で呼び出してください。

まずは、先ほど書いたスクリプトを実行するためのスクリプトを作成します。ファイル名は適当に`hoge`としています。
```shell:hoge
#!/bin/sh
# chkconfig: 2345 99 10

case "$1" in
  start)
    python3 <作成したpythonのpath>
       ;;
  *) break ;;
esac

```

あとは、自動で呼び出すようにchkconfigに登録します。
```
sudo chkconfig --add hoge
sudo chkconfig hoge on
```

以下のコマンドでリストを出して確認できます
```
/sbin/chkconfig --list
・
・
・
hoge   0:off   1:off   2:on    3:on    4:on    5:on    6:off
```

以上の設定でインスタンスを開始するたびにRoute53のIPアドレスが更新されるようになります。

---

## メモ

pythonのスクリプトを実行するために必要なコマンドを載せます
```
yum install pip
pip install boto3
```

たまにインスタンスに接続できないときがあるけど、Route53のIPアドレスはきちんと更新されてるっぽいんですよね。おそらくTTLの方の問題なんですかね。