Help us understand the problem. What is going on with this article?

EC2インスタンス起動時にIPアドレスを自動でRoute53に登録する

Elastic IP をケチりたい

たまにしか起動しないインスタンスに Elastic IP をアタッチするのはもったいない。といって、起動するたびにIPアドレスが変わるとホスト名でアクセスできなくて面倒くさい。

……というテーマで以前、記事を書きました。

今回はそれを推し進めて CloudWatch Event でインスタンスの起動を監視して、起動したら自動でIPアドレスを Route53 のAレコードに登録する、という Lambda を書きました。これでEIP(と無駄な支出)とはおさらばだ!

あと、余計かもしれないけどインスタンスを停止したときにレコードを削除するようにしてみました。意味あるかなぁ。

必要な権限

登録したい Route 53 のホストゾーンのレコード一覧を取得・変更するための権限:

  • route53:ChangeResourceRecordSets
  • route53:ListResourceRecordSets

インスタンスの情報を取得するための権限:

  • ec2:DescribeInstances

あとは Lambda 利用の基本ポリシーである AWSLambdaBasicExecutionRole

Lambda のコード

https://github.com/keioni/qiita-sample/blob/master/set_route53_record.py

set_route53_record.py
import json
import os
from datetime import datetime

import boto3


def json_dt(o):
    if isinstance(o, datetime):
        return o.isoformat()


def change_record(action, host_name, host_addr):
    domain_name = os.environ.get('DomainName')
    if domain_name:
        host_name = F"{host_name}.{domain_name}."
    client = boto3.client('route53')
    change_batch = {
        "Comment": "optional comment about the changes in this change batch request",
        "Changes": [
            {
                "Action": action,
                "ResourceRecordSet": {
                    "Name": host_name,
                    "Type": "A",
                    "TTL": 300,
                    "ResourceRecords": [
                        {
                          "Value": host_addr
                        }
                    ]
                }
            }
        ]
    }
    print("change_batch: " + json.dumps(change_batch, default=json_dt))
    response = client.change_resource_record_sets(
        HostedZoneId=os.environ.get('HostedZoneId'),
        ChangeBatch=change_batch
    )
    print("result: " + json.dumps(response, default=json_dt))
    return response


def get_hostname_from_tags(tags):
    host_name = ''
    for tag in tags:
        if tag['Key'].lower() == 'hostname':
            host_name = tag['Value'].lower()
    return host_name


def check_action(state):
    if state == 'running':
        action = 'UPSERT'
    elif state == 'stopping':
        action = 'DELETE'
    else:
        action = ''
    return action


def lambda_handler(event, context):
    result = dict()
    print('event: ' + json.dumps(event, default=json_dt))
    action = check_action(event['detail']['state'])
    if action:
        ec2 = boto3.resource('ec2')
        instance = ec2.Instance(event['detail']['instance-id'])
        host_name = get_hostname_from_tags(instance.tags)
        if host_name:
            host_addr = instance.public_ip_address
            result = change_record(action, host_name, host_addr)
    return json.dumps(result, default=json_dt)

ざっくり説明

Hostname タグがあるインスタンスだけ対象にします。

Lambda の設定

環境変数

今この Lambda を使っている環境では、ドメイン名と Route53 のホストゾーンIDはひとつだけなので Lambda の環境変数で設定するようにしました。

複数のホストゾーンにまたがって動くようにすると、結構めんどうくさそう。

HostedZoneId=ZABCDEFGHIJKL

CloudWatch Event の設定

スクリーンショットを参考に、なんとか……。

make_cloudwatch_event.png

Lambda に渡される event の例

こんな感じで、InstanceIdState が取れます。

{
    "version": "0",
    "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "detail-type": "EC2 Instance State-change Notification",
    "source": "aws.ec2",
    "account": "123456789012",
    "time": "2019-11-20T08:52:00Z",
    "region": "ap-northeast-1",
    "resources": [
        "arn:aws:ec2:ap-northeast-1:123456789012:instance/i-0123456789abcdefg"
    ],
    "detail": {
        "instance-id": "i-0123456789abcdefg",
        "state": "stopping"
    }
}

注意点

レコードを DELETE するときは、レコードの Value が現在のものでないと失敗するんだそうで。

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/route53.html#Route53.Client.change_resource_record_sets

In the case of a DELETE action, if the current value does not match the actual value, an error is returned.

エラーハンドリングなにもしていないので、あとで考えます。
change_resource_record_sets のドキュメントがこれだけ長いと、なにかの拍子で例外が上がるかもしれないですね……。

いずれエラーハンドリングをきちんとして、記事を更新したいと思います。いずれ……。

keys
双子のロシアンブルーと同居するインフラエンジニア。楽するための苦労はいとわないタイプ。最適化とか自動化が大好き。IPv6, AWS, Docker, Serverless, Python, セキュリティといった単語に反応するようです。すみっコぐらしの推しはとかげ。みにっコだとざっそう。
https://devel.keys.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away