LoginSignup
14
17

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-11-20

Elastic IP をケチりたい

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

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

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

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

必要な権限

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

  • route53:ChangeResourceRecordSets
  • route53:ListResourceRecordSets

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

  • ec2:DescribeInstances

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

Lambda のコード

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 が現在のものでないと失敗するんだそうで。

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 のドキュメントがこれだけ長いと、なにかの拍子で例外が上がるかもしれないですね……。

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

14
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
17