web-dev-qa-db-ja.com

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

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

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

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

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

それを何度も繰り返す代わりに、Fn::Joinの出力をある種の変数に割り当てて、"Ref":ステートメントでできるように、それだけを参照したいと思います。

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

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

または同様に簡単なもの。

Amazon CloudFormationでそれは可能ですか?

20
MLu

同じ機能を探していました。 SpoonMeiserの提案どおりにネストされたスタックを使用することを思いついたのですが、実際に必要なのはカスタム関数であることがわかりました。幸いCloudFormationは AWS :: CloudFormation :: CustomResource の使用を許可します。少しの作業で、それを行うことができます。これは単なる変数(最初にCloudFormationにあるはずだったと私が主張するもの)に対してはやり過ぎのように感じられますが、それで仕事が完了するだけでなく、(python/nodeを自由に選択できます)/Java)。ラムダ関数はコストがかかることに注意してください。ただし、スタックを1時間に複数回作成/削除しない限り、ここでは数ペニーを話しています。

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

ラムダ関数を作成する

  • lambdaホームページ に移動し、お気に入りの地域を選択します
  • テンプレートとして「空白関数」を選択します
  • [次へ]をクリックします(トリガーを設定しないでください)
  • 埋める:
    • 名前:CloudFormationIdentity
    • 説明:取得したものを返します。クラウドフォーメーションでの変数サポート
    • ランタイム: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作成リクエスト」を選択できます。ログに、供給された変数が返されることが表示されます。

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

これでラムダ関数ができたので、CloudFormationテンプレートで使用できます。最初にラムダ関数Arnを書き留めます( lambda home page に移動し、作成した関数をクリックします。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]]]]

最初に、ラムダ関数のArnを含むIdentity変数を指定します。これを変数に入れると、指定する必要があるのは1回だけです。すべての変数をCustom::Variableタイプの変数にします。 CloudFormationでは、カスタムリソースに対してCustom::で始まる任意のタイプ名を使用できます。

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

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

注意の言葉

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

5
Claude

答えはありませんが、Fn::Sub 代わりに Fn::Join

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

置き換え

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

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

作品:

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

動作しません:

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

これは単なる例です。スタンドアロンのRefを変数に入れません。

3
Rob

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

3
SpoonMeiser

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

2
JoseOlcese