CloudFormationで起動したCloudFrontに自動でRoute53のAレコードを設定するLambda functionを書いてみた


## 流れ
- Route53でホステッドゾーンを作成する
- CloudFormation起動時にSNSに通知を流す
- SNSに通知が行くと起動するLambda functionを作成する
- IAMロールを設定する
- Lambda functionにnodeのコードを突っ込む

## Route53でホステッドゾーンを作成する
まずはRoute53でホステッドゾーンを作成しておきます。

## CloudFormation起動時にSNSに通知を流す
CloudFormationで起動する際にSNSに通知を流すようにしておきます。
作成するCloudFormationには、「Domain（例:example.com）」と「WebSiteURL（例:[http://xxxxxxx.cloudfront.net](http://xxxxxxx.cloudfront.net)）のOutputsを出すようにします。

## SNSに通知が行くと起動するLambda functionを作成する
SNSから起動するLambdaのblueprintがあるので、それを使ってLambda functionを立ち上げます。

## IAMロールを設定する
LambdaにはIAMでCloudFrontのReadOnlyとRoute53のfullAccessを付与しておきます。

## Lambda functionにnodeのコードを突っ込む
いきなりコード全文です。
SNSから来た通知文からCloudFormationのステータスを確認し、スタック作成が完了したタイミングで以下の処理を走らせてます。

+ スタック名からCloudFormationの情報を取得
+ Outputsの中にあらかじめ仕込んである「Domain（例:example.com）」と「WebSiteURL（例:[http://xxxxxxx.cloudfront.net](http://xxxxxxx.cloudfront.net)）」の値を引っ張ってくる  
+ 引っ張って来た値のうち、「Domain」を使ってRoute53のホステッドゾーンIDを取得する
+ ホステッドゾーンIDを「WebSiteURL」を使ってRoute53のAレコードにCloudFrontのURLを設定する

```
'use strict';
console.log('Loading function');
var AWS = require('aws-sdk');
var region = "us-east-1";
var cloudformation = new AWS.CloudFormation({
  apiVersion: '2010-05-15',
  region: region
});
var route53 = new AWS.Route53({
  apiVersion: '2014-05-15',
  region: region
});


exports.handler = (event, context, callback) => {
    //console.log('Received event:', JSON.stringify(event, null, 2));
    var message = event.Records[0].Sns.Message;
    
    //Resouce Status
    var resourceStr = message.split( "ResourceStatus=\'" );
    resourceStr = resourceStr[1].split( "\'\nResourceStatusReason" );
    var resourceStatus = resourceStr[0];
    console.log(resourceStatus);
    
    //Resouce Type
    var resourceTypeStr = resourceStr[1].split( "ResourceType=\'" );
    resourceTypeStr = resourceTypeStr[1].split( "\'\nStackName=\'" );
    var resourceType = resourceTypeStr[0];
    console.log(resourceType);
    
    //Stack Name
    var stackNameStr = resourceTypeStr[1].split( "'" );
    var stackName = stackNameStr[0];
    console.log(stackName);
    
    if ( resourceType == 'AWS::CloudFormation::Stack' && resourceStatus == 'CREATE_COMPLETE' ) {
        console.log('Stack:' + stackName + 'is created!');
        var cfnParams = {
          StackName: stackName
        };
        addRecordSet( cfnParams );
        
    }
    console.log(message);
    
    callback(null, message);
};

function addRecordSet( cfnParams ) {
    var cdn = '';

    cloudformation.describeStacks(cfnParams).promise().then( (data) => {
        var output = data.Stacks[0].Outputs;
        var params = {};
        output.forEach( (value, index) => {
          if ( value.OutputKey === "Domain" ) {
            params.domain = value.OutputValue;
          } else if ( value.OutputKey === 'WebSiteURL' ) {
            params.cdn = value.OutputValue.split('http://')[1];
          }
        });
        cdn = params.cdn;
        var params = {
          DNSName: params.domain,
          MaxItems: '1'
        };
        return route53.listHostedZonesByName(params).promise();
      }).then( (data) =>  {
          console.log('Find Your Domain.');
        return data;
      }).then( (data) => {
        console.log('Upsert Your Domain.');
        var HostedZoneId = data.HostedZones[0].Id;
        HostedZoneId = HostedZoneId.split('/')[2];
    
        var params = {
          ChangeBatch: {
            Changes: [
              {
                Action: 'UPSERT',
                ResourceRecordSet: {
                  Name: data.DNSName + '.',
                  Type: 'A',
                  AliasTarget: {
                    DNSName: cdn,
                    EvaluateTargetHealth: true,
                    HostedZoneId: "Z2FDTNDATAQYW2"
                  },
                }
              }
            ],
          },
          HostedZoneId: HostedZoneId
        };
        return route53.changeResourceRecordSets(params).promise();
      }).then( (data) => {
        var message = 'success to add domain';
        console.log(data);
        console.log(message);
      }).catch( (err) => {
        var message = 'Fail to add domain';
        console.log(err);
        console.log(message);
      }
    );
}
```