awsサービス cloudformationからlambdaをキックさせる

cloudformationでインスタンスなどが立ち上げ終わった後に、マネジメントコンソールで管理しているlambdaを叩きたいなと思って実行したものです。


## カスタムリソース
[カスタムリソース](http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-custom-resources.html)というのが用意されています。これを使います。

## こいつは何か？
AWSのCloudFormationのリソースタイプとして使用できないリソースを含める必要があるときに、それらのリソースは、カスタムリソースを使用して含めることができるらしい。つまりこの方法によりすべての関連リソースを1つのスタックで管理できるという優れものです。
今回だとて使用できないリソースとしてlambdaが当たりますがSQSとかもそうらしいです。以下のような感じで記述できます。Test1、Test2のように任意の文字列も渡せます。

```
"CustomResource" : {
　　"Type" : "AWS::CloudFormation::CustomResource",
    "Properties" : {
        "ServiceToken": "指定のARN",
        "Test1": {"Ref": "InstanceName"},
        "Test2": {"Ref": "TargetGroupName"}
    }
}
```


## 使い方
lambdaをcloudformatinで使うにはあるものを呼ばないといけないらしい。それが[cfn-responseモジュール](http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-zipfile)と呼ばれるものだ。そしてこれを使うときの注意書きとして以下が記述してあります。

>cfn-response モジュールは、ZipFile プロパティを使用してソースコードを作成した場合にのみ使用できます。S3 バケットに保存されたソースコードには使用できません。S3 バケットのコードでは、独自の関数を作成してレスポンスを送信する必要があります。

どういうことかというとcloudformationでlambdaを使うときは以下のようにコードを直書きしないと使えませんよということ。

```
"ZipFile": { "Fn::Join": ["", [
  "var response = require('cfn-response');",
  "exports.handler = function(event, context) {",
  "  var input = parseInt(event.ResourceProperties.Input);",
  "  var responseData = {Value: input * 5};",
  "  response.send(event, context, response.SUCCESS, responseData);",
  "};"
```


あれ？？マネジメントコンソールですでに記述したやつを使いたかったのに・・・・
github調べてみたら出てきた

https://github.com/LukeMizuhashi/cfn-response/blob/c5bb51ada6110f4e84d7a8bad7799fd90e0b440d/index.js#L11


## 解決方法
実際に書いた。githubのほぼコピペなので迷うことありません。

```
cloudformation側

"CustomResource" : {
　　"Type" : "AWS::CloudFormation::CustomResource",
    "Properties" : {
        "ServiceToken": "lambdaのARN",
        "Test1": {"Ref": "InstanceName"},
        "Test2": {"Ref": "TargetGroupName"}
    }
}
```

そうすると”lambdaのARN”で指定したlambdaを叩きに行く。そしてlambda側で以下を入れておく。


```
lambda側

var SUCCESS = "SUCCESS";
var FAILED = "FAILED";
 
var cfnResponse = function(event, context, responseStatus, responseData, physicalResourceId) {
 
    var responseBody = JSON.stringify({
        Status: responseStatus,
        Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
        PhysicalResourceId: physicalResourceId || context.logStreamName,
        StackId: event.StackId,
        RequestId: event.RequestId,
        LogicalResourceId: event.LogicalResourceId,
        Data: responseData
    });
 
    console.log("Response body:\n", responseBody);
 
    var https = require("https");
    var url = require("url");
 
    var parsedUrl = url.parse(event.ResponseURL);
    var options = {
        hostname: parsedUrl.hostname,
        port: 443,
        path: parsedUrl.path,
        method: "PUT",
        headers: {
            "content-type": "",
            "content-length": responseBody.length
        }
    };
 
    var request = https.request(options, function(response) {
        console.log("Status code: " + response.statusCode);
        console.log("Status message: " + response.statusMessage);
        context.done();
    });
 
    request.on("error", function(error) {
        console.log("send(..) failed executing https.request(..): " + error);
        context.done();
    });
 
    request.write(responseBody);
    request.end();
}

exports.handler = (event, context, callback) => {
    // TODO implement
    if(event['RequestType'] == 'Delete'){
        cfnResponse(event, context, SUCCESS, {});
    }
    console.log(event);
    cfnResponse(event, context, SUCCESS, {});
};
```

これをlambda側に書いていないとインプログレスのまま一時間くらい止まってしまい、立ち上げ直さないといけない感じになる。

## 結果
cloudformationで環境を作った後に、lambdaを使うことに成功した。
その時にカスタマーリソールなるものを用いた、ちゃんとS3のcloudformationのタスクのレスポンスとして結果を返さないといけないので注意しましょ！！！