AWS CloudFormation-テンプレートのカスタム変数


18

CloudFormationテンプレートパラメータから派生した、よく使用される値のショートカットを定義する方法はありますか?

たとえば、ELB名projectとELBの背後にある2つのインスタンスとで呼び出されるMulti-AZプロジェクトスタックを作成するスクリプトがproject-1ありproject-2ます。ELBHostNameテンプレートにパラメーターを渡し、後でそれを使用して構築するだけです:

"Fn::Join": [
    ".", [
        { "Fn::Join": [ "", [ { "Ref": "ELBHostName" }, "-1" ] ] },
        { "Ref": "EnvironmentVersioned" },
        { "Ref": "HostedZone" }
    ]
]

EC2ホスト名、Route53レコードなどを作成するために、この構造または非常に類似したものがテンプレート全体で何度も繰り返されます。

それを何度も繰り返す代わりに、その出力Fn::Joinを何らかの変数に割り当て、それを参照するだけ"Ref":です。これは、with with ステートメントと同じです。

理想的には次のようなものです:

Var::HostNameFull = "Fn::Join": [ ... ]
...
{ "Name": { "Ref": "Var::HostNameFull" } }

または同様にシンプルなもの。

Amazon CloudFormationで可能ですか?


ELBHostNameは、Cloudformationに明示的に渡すパラメーターでいっぱいですか?もしそうなら、なぜRefを使用するのですか?Mustacheを使用してテンプレートに変数を含め、Cloudformationに出荷する前にJSONに変換することができます。プロビジョニングプロセスがどのように見えるかによって異なります。
キャヌテソン

回答:


5

私は同じ機能を探していました。SpoonMeiserが示唆したようにネストされたスタックを使用することは思い浮かびましたが、実際に必要なのはカスタム関数であることがわかりました。幸いなことに、CloudFormationはAWS :: CloudFormation :: CustomResourceの使用を許可します。これにより、少し作業を行うだけで、それを実行できます。これは単なる変数(私が第一にCloudFormationにあるべきだと主張するもの)に対してはやり過ぎのように感じますが、仕事は完了し、さらに(python / nodeを選んでください)のすべての柔軟性を可能にします/ java)。ラムダ関数には費用がかかることに注意してください。しかし、スタックを1時間に複数回作成/削除しない限り、ここでは少額の費用がかかります。

最初のステップは、このページでラムダ関数を作成し、入力値を取得して出力にコピーするだけです。ラムダ関数にあらゆる種類のクレイジーな処理を実行させることもできますが、アイデンティティ関数を取得したら、それ以外は簡単です。または、スタック自体にラムダ関数を作成することもできます。1つのアカウントで多くのスタックを使用しているため、残りのラムダ関数とロールがたくさんあります(ロール--capabilities=CAPABILITY_IAMも必要なので、すべてのスタックはで作成する必要があります。

ラムダ関数を作成する

  • ラムダホームページに移動し、お気に入りの地域を選択します
  • テンプレートとして「Blank Function」を選択します
  • [次へ]をクリックします(トリガーを構成しないでください)。
  • 埋める:
    • 名前:CloudFormationIdentity
    • 説明:取得したものを返します。CloudFormationの変数サポート
    • ランタイム:python2.7
    • コード入力タイプ:コードのインライン編集
    • コード:以下を参照
    • ハンドラ: index.handler
    • 役割:カスタム役割を作成します。この時点で、新しいロールを作成できるポップアップが開きます。このページのすべてを受け入れ、「許可」をクリックします。cloudwatchログに投稿する権限を持つロールを作成します。
    • メモリー:128(これは最小値です)
    • タイムアウト:3秒(十分なはずです)
    • VPC:VPCなし

次に、コードフィールドに以下のコードをコピーして貼り付けます。関数の上部はcfn-response python moduleのコードです。これは、何らかの奇妙な理由で、ラムダ関数がCloudFormationを介して作成された場合にのみ自動インストールされます。このhandler関数はかなり一目瞭然です。

from __future__ import print_function
import json

try:
    from urllib2 import HTTPError, build_opener, HTTPHandler, Request
except ImportError:
    from urllib.error import HTTPError
    from urllib.request import build_opener, HTTPHandler, Request


SUCCESS = "SUCCESS"
FAILED = "FAILED"


def send(event, context, response_status, reason=None, response_data=None, physical_resource_id=None):
    response_data = response_data or {}
    response_body = json.dumps(
        {
            'Status': response_status,
            'Reason': reason or "See the details in CloudWatch Log Stream: " + context.log_stream_name,
            'PhysicalResourceId': physical_resource_id or context.log_stream_name,
            'StackId': event['StackId'],
            'RequestId': event['RequestId'],
            'LogicalResourceId': event['LogicalResourceId'],
            'Data': response_data
        }
    )
    if event["ResponseURL"] == "http://pre-signed-S3-url-for-response":
        print("Would send back the following values to Cloud Formation:")
        print(response_data)
        return

    opener = build_opener(HTTPHandler)
    request = Request(event['ResponseURL'], data=response_body)
    request.add_header('Content-Type', '')
    request.add_header('Content-Length', len(response_body))
    request.get_method = lambda: 'PUT'
    try:
        response = opener.open(request)
        print("Status code: {}".format(response.getcode()))
        print("Status message: {}".format(response.msg))
        return True
    except HTTPError as exc:
        print("Failed executing HTTP request: {}".format(exc.code))
        return False

def handler(event, context):
    responseData = event['ResourceProperties']
    send(event, context, SUCCESS, None, responseData, "CustomResourcePhysicalID")
  • 「次へ」をクリックします
  • 「関数の作成」をクリックします

[テスト]ボタンを選択してラムダ関数をテストし、サンプルテンプレートとして[CloudFormation Create Request]を選択できるようになりました。ログに送られた変数が返されていることがわかります。

CloudFormationテンプレートで変数を使用する

このラムダ関数ができたので、CloudFormationテンプレートで使用できます。まず、ラムダ関数Arnを書き留めます(ラムダホームページに移動し、作成したばかりの関数をクリックします。Arnは右上にあるはずarn:aws:lambda:region:12345:function:CloudFormationIdentityです。

テンプレートのリソースセクションで、次のように変数を指定します。

Identity:
  Type: "Custom::Variable"
  Properties:
    ServiceToken: "arn:aws:lambda:region:12345:function:CloudFormationIdentity"
    Arn: "arn:aws:lambda:region:12345:function:CloudFormationIdentity"

ClientBucketVar:
  Type: "Custom::Variable"
  Properties:
    ServiceToken: !GetAtt [Identity, Arn]
    Name: !Join ["-", [my-client-bucket, !Ref ClientName]]
    Arn: !Join [":", [arn, aws, s3, "", "", !Join ["-", [my-client-bucket, !Ref ClientName]]]]

ClientBackupBucketVar:
  Type: "Custom::Variable"
  Properties:
    ServiceToken: !GetAtt [Identity, Arn]
    Name: !Join ["-", [my-client-bucket, !Ref ClientName, backup]]
    Arn: !Join [":", [arn, aws, s3, "", "", !Join ["-", [my-client-bucket, !Ref ClientName, backup]]]]

まずIdentity、ラムダ関数のArnを含む変数を指定します。これをここの変数に入れると、一度指定するだけで済みます。typeのすべての変数を作成しますCustom::Variable。CloudFormationを使用するとCustom::、カスタムリソースで始まる任意のタイプ名を使用できます。

Identity変数には、ラムダ関数のArnが2回含まれていることに注意してください。使用するラムダ関数を1回指定します。2回目は変数の値として。

今、私が持っているIdentity変数を、私が使用して新しい変数を定義することができますServiceToken: !GetAtt [Identity, Arn](私はJSONコードのようなものであるべきだと思います"ServiceToken": {"Fn::GetAtt": ["Identity", "Arn"]})。それぞれ2つのフィールドを持つ2つの新しい変数を作成します:NameとArn。私のテンプレートの残りの部分では私が使用することができます!GetAtt [ClientBucketVar, Name]!GetAtt [ClientBucketVar, Arn]私はそれを必要なとき。

注意事項

CloudFormationは(クラッシュした)関数からの応答を1時間待機してから放棄するため、カスタムリソースで作業しているときにラムダ関数がクラッシュすると、1〜2時間スタックします。したがって、ラムダ関数の開発中にスタックの短いタイムアウトを指定することをお勧めします。


素晴らしい答え!私はそれを読んで自分のスタックで実行しましたが、私にとっては、アカウント内のラムダ関数の拡散を心配せず、スタンドアロンのテンプレートが好きです(cloudformation-toolgem を使用してモジュール化します)ので、ラムダ作成をテンプレートを作成し、Identityカスタムリソースを作成する代わりに直接使用できます。私のコードについてはこちらをご覧ください:gist.github.com/guss77/2471e8789a644cac96992c4102936fb3
Guss

ラムダがクラッシュし、cfn-responseで応答しなかったため、「... 1時間から2時間の間立ち往生しています...」の場合、curl / wget onを手動で使用して、テンプレートを再び移動できます。署名付きURL。CloudWatchにアクセスして、ハングした場合にURLを取得できるように、ラムダの先頭に必ずイベント/ URLを出力するようにしてください。
テイラー

12

答えはありませんが、Fn::Sub代わりに使用することで多くの苦痛を軽減できることを指摘したかったのです。Fn::Join

{ "Fn::Sub": "${ELBHostName"}-1.${EnvironmentVersioned}.${HostedZone}"}

置換

"Fn::Join": [
    ".", [
        { "Fn::Join": [ "", [ { "Ref": "ELBHostName" }, "-1" ] ] },
        { "Ref": "EnvironmentVersioned" },
        { "Ref": "HostedZone" }
    ]
]

3

いいえ。試しましたが、空っぽになりました。私にとって意味のある方法は、「CustomVariables」というマッピングエントリを作成し、すべての変数を格納することでした。単純な文字列に対して機能しますが、Mappings内で組み込み関数(Ref、Fn :: Joinsなど)を使用することはできません

作品:

"Mappings" : {
  "CustomVariables" : {
    "Variable1" : { "Value" : "foo" },
    "Variable2" : { "Value" : "bar" }
  }
}

動作しません:

  "Variable3" : { "Value" : { "Ref" : "AWS::Region" } }

それはほんの一例です。変数にスタンドアロンRefを配置しません。


1
ドキュメンテーションでは、マッピング値はリテラル文字列でなければならないという。
イワンアニシュチュク

3

出力内のすべての変数を解決するネストされたスタックを使用Fn::GetAttし、そのスタックから出力を読み取るために使用できます


2

外側のテンプレートのすべての変数を「解決」して別のテンプレートに渡すネストされたテンプレートを使用できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.