AWSのCloudWatch Events+Lambda+Route53でDDNSを構築


AWS Compute Blogで紹介されていたCloudWatch Events+Lambda+Route53でDDNSを構築する方法を試してみました。

元記事:
[Building a Dynamic DNS for Route 53 using CloudWatch Events and Lambda | AWS Compute Blog](https://aws.amazon.com/jp/blogs/compute/building-a-dynamic-dns-for-route-53-using-cloudwatch-events-and-lambda/)
	
## AWS環境構築

**※arnのアカウントIDにはダミーの値が入っています。**

### IAM

Lambda用のPolicyを作成。

```/tmp/ddns-policy.json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "ec2:Describe*",
    "Resource": "*"
  }, {
    "Effect": "Allow",
    "Action": [
      "dynamodb:*"
    ],
    "Resource": "*"
  }, {
    "Effect": "Allow",
    "Action": [
      "logs:CreateLogGroup",
      "logs:CreateLogStream",
      "logs:PutLogEvents"
    ],
    "Resource": "*"
  }, {
    "Effect": "Allow",
    "Action": [
      "route53:*"
    ],
    "Resource": [
      "*"
    ]
  }]
}
```

```
$ aws iam create-policy --policy-name ddns-lambda-policy --policy-document file:///tmp/ddns-policy.json
{
    "Policy": {
        "PolicyName": "ddns-lambda-policy",
        "CreateDate": "2016-05-08T05:29:31.474Z",
        "AttachmentCount": 0,
        "IsAttachable": true,
        "PolicyId": "XXXXXXXXXXXXXXXXXXXXX",
        "DefaultVersionId": "v1",
        "Path": "/",
        "Arn": "arn:aws:iam::000000000000:policy/ddns-lambda-policy",
        "UpdateDate": "2016-05-08T05:29:31.474Z"
    }
}
```

Lambda用のRoleを作成。

```/tmp/ddns-trust.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

```
$ aws iam create-role --role-name ddns-lambda-role --assume-role-policy-document file:///tmp/ddns-trust.json
{
    "Role": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    },
                    "Effect": "Allow",
                    "Sid": ""
                }
            ]
        },
        "RoleId": "XXXXXXXXXXXXXXXXXXXXX",
        "CreateDate": "2016-05-08T05:29:53.160Z",
        "RoleName": "ddns-lambda-role",
        "Path": "/",
        "Arn": "arn:aws:iam::000000000000:role/ddns-lambda-role"
    }
}
```

RoleとPolicyを紐付け。

```
$ aws iam attach-role-policy --role-name ddns-lambda-role --policy-arn arn:aws:iam::000000000000:policy/ddns-lambda-policy
```

### Lambda

AWSが提供しているLambda Functionをダウンロード。

```
$ curl -L https://github.com/awslabs/aws-lambda-ddns-function/raw/master/union.py.zip -o /tmp/union.py.zip
```

Lambdaを作成。

```
$ aws lambda create-function --function-name ddns_lambda --runtime python2.7 --role arn:aws:iam::000000000000:role/ddns-lambda-role --handler union.lambda_handler --timeout 30 --zip-file "fileb:///tmp/union.py.zip"
{
    "CodeSha256": "Ho7yoabYeILgQRU/hdB7n0EPC6CXtBTquiLu0GDeuhw=",
    "FunctionName": "ddns_lambda",
    "CodeSize": 4753,
    "MemorySize": 128,
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:000000000000:function:ddns_lambda",
    "Version": "$LATEST",
    "Role": "arn:aws:iam::000000000000:role/ddns-lambda-role",
    "Timeout": 30,
    "LastModified": "2016-05-08T05:40:49.300+0000",
    "Handler": "union.lambda_handler",
    "Runtime": "python2.7",
    "Description": ""
}
```

### CloudWatch Events

ルールを作成。

```
$ aws events put-rule --event-pattern "{\"source\":[\"aws.ec2\"],\"detail-type\":[\"EC2 Instance State-change Notification\"],\"detail\":{\"state\":[\"running\",\"shutting-down\",\"stopped\"]}}" --state ENABLED --name ec2_lambda_ddns_rule
{
    "RuleArn": "arn:aws:events:ap-northeast-1:000000000000:rule/ec2_lambda_ddns_rule"
}
```

Targetを作成。

```
$ aws events put-targets --rule ec2_lambda_ddns_rule --targets Id=id123456789012,Arn=arn:aws:lambda:ap-northeast-1:000000000000:function:ddns_lambda
{
    "FailedEntries": [],
    "FailedEntryCount": 0
}
```

EventをLambdaに紐付け。

```
$ aws lambda add-permission --function-name ddns_lambda --statement-id 45 --action lambda:InvokeFunction --principal events.amazonaws.com --source-arn arn:aws:events:ap-northeast-1:000000000000:rule/ec2_lambda_ddns_rule
{
    "Statement": "{\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:events:ap-northeast-1:000000000000:rule/ec2_lambda_ddns_rule\"}},\"Action\":[\"lambda:InvokeFunction\"],\"Resource\":\"arn:aws:lambda:ap-northeast-1:000000000000:function:ddns_lambda\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"events.amazonaws.com\"},\"Sid\":\"45\"}"
}
```

### Route53

Private Hosted Zoneを作成。

![Screen Shot 2016-05-08 at 2.45.13 PM.png](https://qiita-image-store.s3.amazonaws.com/0/5774/8f2a3f55-f32d-0cb8-3169-d3f843e1230d.png "Screen Shot 2016-05-08 at 2.45.13 PM.png")

### VPC

VPCのDNS Hostnamesを有効に変更。

![Screen Shot 2016-05-08 at 3.01.56 PM.png](https://qiita-image-store.s3.amazonaws.com/0/5774/8b9a22d9-27a2-1d55-7663-a9df4511c999.png "Screen Shot 2016-05-08 at 3.01.56 PM.png")

DHCP options setを作成。

![Screen Shot 2016-05-08 at 2.47.05 PM.png](https://qiita-image-store.s3.amazonaws.com/0/5774/3749d515-f068-e9fc-462f-52e35a0a87e6.png "Screen Shot 2016-05-08 at 2.47.05 PM.png")


## 動作確認

### Private Hosted Zone

対象のインスタンスにZONE/CNAMEタグを設定してインスタンスを起動。末尾の'.'を忘れずに。

![Screen Shot 2016-05-08 at 3.35.17 PM.png](https://qiita-image-store.s3.amazonaws.com/0/5774/91ee4b2e-abae-4d12-3954-a3210dc665c2.png "Screen Shot 2016-05-08 at 3.35.17 PM.png")

起動後、Route53の該当のHosted ZoneにPrivate IPのレコードが作成されていることを確認。

![Screen Shot 2016-05-08 at 3.22.24 PM.png](https://qiita-image-store.s3.amazonaws.com/0/5774/c9e89acb-8a85-138d-f390-93dae1a0b8df.png "Screen Shot 2016-05-08 at 3.22.24 PM.png")


### Public Hosted Zone

Private Hosted Zoneと同様の手順を実施することにより、PublicのHosted ZoneにPublic IPのレコードが作成されていることも確認済み。

## その他

起動するとDynamoDBにDDNSテーブルが作成されます。このテーブルのCapacityはRead/Rwrite共に4に設定されていますが、ほとんどの場合1で問題ないと思いますので、適宜変更しましょう。