AWS CloudFormationのSNS-backedカスタムリソースを利用する
カスタムリソース
CloudFormationカスタムリソースは、テンプレートの中だけでは記述できないリソースタイプなどを外出しして記述する方法です。ユーザーがスタックを作成、更新、削除するたびに AWS CloudFormation がそれを実行します。
今回は、Amazon SNSを使って外部のリソースと連携してみたいと思います。
Amazon SNS-backedカスタムリソース
まず始めにCloudFormationテンプレートを作成したいと思います。カスタムリソース指定の部分に注目してください。このテンプレートは、Custom::HelloWorld型のリソースであるMyCustomLogicを定義しています。このカスタムリソースは、SNSに通知をしますので、このテンプレート内でSNSを作成しています。そして、後からポーリングして取り出せるように、SNSのサブスクライバとしてSQSを指定しています。
{ "AWSTemplateFormatVersion" : "2010-09-09", "Resources" : { "MyCustomLogic" : { "Type": "Custom::HelloWorld", "Version" : "1.0", "Properties" : { "ServiceToken": {"Ref" : "MySNSTopic"} } }, "MySNSTopic":{ "Type":"AWS::SNS::Topic", "Properties":{ "Subscription":[{ "Endpoint":{"Fn::GetAtt":["MyQueue","Arn"]}, "Protocol":"sqs" }] } }, "MyQueue":{ "Type":"AWS::SQS::Queue" }, "MyQueuePolicy":{ "Type":"AWS::SQS::QueuePolicy", "Properties":{ "PolicyDocument":{ "Version":"2012-10-17", "Id":"MyQueuePolicy", "Statement":[ { "Sid":"Allow-SendMessage-To-Queues-From-SNS-Topic", "Effect":"Allow", "Principal":"*", "Action":["sqs:SendMessage"], "Resource":"*", "Condition":{ "ArnEquals":{ "aws:SourceArn":{"Ref":"MySNSTopic"} } } } ] }, "Queues":[{"Ref":"MyQueue"}] } } } }
リソースプロバイダーが受け取るメッセージ
CloudFormation内のテンプレートデベロッパーから送られてきたメッセージは、カスタムリソースプロバイダーで処理されて応答メッセージを返すことになります。ここでは、通知メッセージのボディを見てみたいと思います。Messageブロックには、応答に必要な各種ID情報などが含まれています。
{ "Type" : "Notification", "MessageId" : "cd211b4f-1dc7-557e-99a1-5553926141df", "TopicArn" : "arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:CustomHello6-MySNSTopic-1W0F4LGK7AVL2", "Subject" : "AWS CloudFormation custom resource request", "Message" : "{ \"RequestType\":\"Create\", \"ServiceToken\":\"arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:CustomHello6-MySNSTopic-1W0F4LGK7AVL2\", \"ResponseURL\":\"https://cloudformation-custom-resource-response-apnortheast1.s3-ap-northeast-1.amazonaws.com/arn%3Aaws%3Acloudformation%3Aap-northeast-1%XXXXXXXXXXXX%3Astack/CustomHello6/0b8ba5b0-28ae-11e5-8e5c-50fa59279ce0%7CMyCustomLogic%7C27bb6dbe-557a-40f9-8793-85a25a908d71 \"StackId\":\"arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/CustomHello6/0b8ba5b0-28ae-11e5-8e5c-50fa59279ce0\", \"RequestId\":\"27bb6dbe-557a-40f9-8793-85a25a908d71\", \"LogicalResourceId\":\"MyCustomLogic\", \"ResourceType\":\"Custom::HelloWorld\", \"ResourceProperties\":{\"ServiceToken\":\"arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:CustomHello6-MySNSTopic-1W0F4LGK7AVL2\"}}", "Timestamp" : "2015-07-12T15:53:10.020Z", "SignatureVersion" : "1", "Signature" : "Lgtvyeg+sDWbTxHGJrWUNeB1UikS7eMOy2ml+pUhGBAYbyLkpDf7p2mn/06oapDsyV2qIX9y66CI6UuWfsAOTW/+BEjVpJpdx1kux+wuu9165p0MTq0o6c4o5qTaWSOX/IKn0DSLukNKd+5WDSNWwKH3hMs4W0aU7rKh9y5PLep2FtK3wXxFyHWBh4Rsy8Fk6UCDz+HK+K0AORxaRamVL4d4j/vZDXplAw6HbhSaasUnkakzRdllnISiZRd3iiwaaBny3c6M012t8MCVDfq8zUS/JoXRzFXoGmAr9PcExvHUuq+Dy1h/aCOlWHEZu8TmDF2zYyjf7oSkzR0gC2MA1A==", "SigningCertURL" : "https://sns.ap-northeast-1.amazonaws.com/SimpleNotificationService-d6d679a1d18e95c2f9ffcf11f4f9e198.pem", "UnsubscribeURL" : "https://sns.ap-northeast-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:CustomHello6-MySNSTopic-1W0F4LGK7AVL2:d6dec38a-a5f7-4cd8-a419-aa3cad7b1cd1" }
処理成功の応答をする。
カスタムリソースプロバイダー側の処理が成功したと仮定して、応答を返したいと思います。本来であれば、プログラミング等を結果を返すことになりますが、今回はわかりやすさを優先して、curlコマンドでメッセージを返します。以下は、応答メッセージのボディです。これをresult.jsonという名前で保存しました。
{ "Status" : "SUCCESS", "PhysicalResourceId" : "MyCustomLogic", "StackId" : "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/CustomHello6/0b8ba5b0-28ae-11e5-8e5c-50fa59279ce0", "RequestId" : "27bb6dbe-557a-40f9-8793-85a25a908d71", "LogicalResourceId" : "MyCustomLogic" }
次に、curlコマンドでS3にPUTします。ここの記述方法でかなりハマりました。HTTPステータスコードが200番になっていることを確認してください。
$ curl -v -X PUT -H 'User-Agent: ' -H 'Content-Type: ' -T body.json "https://cloudformation-custom-resource-response-apnortheast1.s3-ap-northeast-1.amazonaws.com/arn%3Aaws%3Acloudformation%3Aap-northeast-1%3AXXXXXXXXXXXX%3Astack/CustomHello6/0b8ba5b0-28ae-11e5-8e5c-50fa59279ce0%7CMyCustomLogic%7C27bb6dbe-557a-40f9-8793-85a25a908d71 > PUT /arn%3Aaws%3Acloudformation%3Aap-northeast-1%3AXXXXXXXXXXXX%3Astack/CustomHello6/0b8ba5b0-28ae-11e5-8e5c-50fa59279ce0%7CMyCustomLogic%7C27bb6dbe-557a-40f9-8793-85a25a908d71 HTTP/1.1 > Host: cloudformation-custom-resource-response-apnortheast1.s3-ap-northeast-1.amazonaws.com > Accept: */* > Content-Length: 285 > Expect: 100-continue > < HTTP/1.1 100 Continue * We are completely uploaded and fine < HTTP/1.1 200 OK < x-amz-id-2: IcIFg/AdjYZJ89C/f8YrHjPDTXDvKxS1K3oHtIi4w6lPHkmCed8pVdKoIuSAZ/x7F8oQ6PYmONo= < x-amz-request-id: 3CCDF82395162625 < Date: Sun, 12 Jul 2015 15:54:41 GMT < ETag: "60b76cabf261d140b4dbc32a5873973b" < Content-Length: 0 < Server: AmazonS3 < * Connection #0 to host cloudformation-custom-resource-response-apnortheast1.s3-ap-northeast-1.amazonaws.com left intact
スタック完了を確認
最後に、CloudFormationが完了したか確認を行います。Custom::HelloWorldのステータスがCREATE_COMPLETEになっていればOKです!
まとめ
今回は、CloudFormationの作成/更新/削除といったスタック操作に合わせて実行する、カスタムリソースについて学びました。環境構築に合わせたテストの実施や、あらかじめCloudFormationで用意されていない処理の実行など、応用範囲はかなり広いと思いました。当初、SNS連携しか用意されていませんでしたが、最近、Lambda対応したことにより、カスタムリソースを使いやすくなりましたので、今後活用が進むと思います。さらに、Service CatalogというCloudFormationを管理するサービスも出てきましたので、CloudFormationから目が離せませんね!
参考資料
Amazon Simple Notification Service-backed カスタムリソース
awslabs/aws-cfn-custom-resource-examples
【新機能】AWS CloudFormationのLambda-Backed Custom Resourcesを使ってBlue-Green Deploymentをより簡単に実現する