AWS CloudFormation - ตัวแปรที่กำหนดเองในแม่แบบ


18

มีวิธีใดบ้างในการกำหนดทางลัดสำหรับค่าที่ใช้บ่อยซึ่งได้มาจากพารามิเตอร์เทมเพลต CloudFormation

ยกตัวอย่างเช่น - ฉันมีสคริปต์ที่สร้างสแต็ค Multi-AZ โครงการที่มีชื่อ ELB projectและสองกรณีที่อยู่เบื้องหลัง ELB เรียกว่าและproject-1 project-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


ELBHostName เต็มพารามิเตอร์ที่คุณส่งผ่านไปยัง Cloudformation อย่างชัดเจนหรือไม่? ถ้าใช่ทำไมต้องใช้ Ref อาจใช้ Mustache เพื่อรวมตัวแปรในเทมเพลตของคุณและแปลงเป็น JSON ก่อนที่จะส่งไปยัง Cloudformation ขึ้นอยู่กับกระบวนการจัดเตรียมที่คุณมี
Canuteson

คำตอบ:


5

ฉันกำลังมองหาฟังก์ชั่นเดียวกัน การใช้ซ้อนกันซ้อนตามที่ SpoonMeiser แนะนำมาถึงใจ แต่แล้วฉันก็ตระหนักว่าสิ่งที่ฉันต้องการจริง ๆ คือฟังก์ชั่นที่กำหนดเอง โชคดีที่ CloudFormation อนุญาตให้ใช้AWS :: CloudFormation :: CustomResourceที่มีบิตของงานอนุญาตให้ทำอย่างนั้น รู้สึกเหมือน overkill สำหรับตัวแปรเพียงอย่างเดียว (สิ่งที่ฉันจะโต้แย้งว่าควรจะอยู่ใน CloudFormation ในตอนแรก) แต่มันทำให้งานเสร็จและนอกจากนี้ยังช่วยให้ทุกอย่างยืดหยุ่น (เลือก python / node / java) ควรสังเกตว่าฟังก์ชั่นแลมบ์ดามีค่าใช้จ่าย แต่เรากำลังพูดถึงเพนนีที่นี่เว้นแต่คุณจะสร้าง / ลบสแต็คของคุณหลายครั้งต่อชั่วโมง

ขั้นแรกคือการสร้างฟังก์ชั่นแลมบ์ดาในหน้านี้ที่ไม่ทำอะไรเลยนอกจากรับค่าอินพุตและคัดลอกไปยังเอาต์พุต เราสามารถให้ฟังก์ชั่นแลมบ์ดาทำทุกสิ่งที่บ้าได้ แต่เมื่อเรามีฟังก์ชั่นเอกลักษณ์แล้วสิ่งอื่น ๆ ก็ง่าย อีกทางหนึ่งเราอาจมีฟังก์ชั่นแลมบ์ดาที่ถูกสร้างขึ้นในสแต็กเอง เนื่องจากฉันใช้สแต็คจำนวนมากใน 1 บัญชีฉันจะมีฟังก์ชั่นแลมบ์ดาและบทบาทที่เหลือทั้งหมด (และต้องสร้างสแต็กทั้งหมดด้วย--capabilities=CAPABILITY_IAMเนื่องจากมันต้องการบทบาทด้วย

สร้างฟังก์ชั่นแลมบ์ดา

  • ไปที่แลมบ์ดาหน้าแรกและเลือกภูมิภาคที่คุณชื่นชอบ
  • เลือก "ฟังก์ชั่นที่ว่างเปล่า" เป็นแม่แบบ
  • คลิก "ถัดไป" (ไม่ได้กำหนดค่าทริกเกอร์ใด ๆ )
  • กรอก:
    • ชื่อ: CloudFormationIdentity
    • คำอธิบาย: ส่งคืนสิ่งที่ได้รับการสนับสนุนผันแปรใน Cloud Formation
    • รันไทม์: python2.7
    • ประเภทรายการรหัส: แก้ไขรหัสอินไลน์
    • รหัส: ดูด้านล่าง
    • handler: index.handler
    • บทบาท: สร้างบทบาทที่กำหนดเอง ณ จุดนี้ป๊อปอัปจะเปิดขึ้นเพื่อให้คุณสร้างบทบาทใหม่ ยอมรับทุกอย่างในหน้านี้และคลิก "อนุญาต" มันจะสร้างบทบาทที่มีสิทธิ์ในการโพสต์ลงในบันทึกของ cloudwatch
    • หน่วยความจำ: 128 (นี่คือขั้นต่ำ)
    • หมดเวลา: 3 วินาที (ควรมีมากมาย)
    • VPC: ไม่มี VPC

จากนั้นคัดลอกโค้ดด้านล่างในฟิลด์รหัส ด้านบนของฟังก์ชั่นเป็นรหัสจากโมดูล cfn-responseที่จะได้รับการติดตั้งอัตโนมัติหากฟังก์ชั่นแลมบ์ดาถูกสร้างขึ้นผ่าน 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 (ไปที่หน้าแรกแลมบ์ดาคลิกฟังก์ชั่นที่เพิ่งสร้างขึ้น 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 สำหรับฟังก์ชัน lambda การใส่ตัวแปรนี้ไว้ที่นี่หมายความว่าฉันต้องระบุเพียงครั้งเดียว Custom::Variableฉันจะทำให้ตัวแปรของฉันทุกประเภท CloudFormation ช่วยให้คุณใช้ชื่อประเภทเริ่มต้นด้วยCustom::สำหรับทรัพยากรที่กำหนดเอง

โปรดทราบว่าIdentityตัวแปรมี Arn สำหรับฟังก์ชั่นแลมบ์ดาสองครั้ง หนึ่งครั้งเพื่อระบุฟังก์ชั่นแลมบ์ดาที่จะใช้ ครั้งที่สองเป็นค่าของตัวแปร

ตอนนี้ฉันมีIdentityตัวแปรแล้วฉันสามารถกำหนดตัวแปรใหม่โดยใช้ServiceToken: !GetAtt [Identity, Arn](ฉันคิดว่ารหัส JSON ควรเป็นอย่างนั้น"ServiceToken": {"Fn::GetAtt": ["Identity", "Arn"]}) ฉันสร้างตัวแปรใหม่ 2 ตัวแต่ละตัวมี 2 ฟิลด์: ชื่อและ Arn ในส่วนที่เหลือของแม่แบบของฉันฉันสามารถใช้!GetAtt [ClientBucketVar, Name]หรือ!GetAtt [ClientBucketVar, Arn]เมื่อใดก็ตามที่ฉันต้องการมัน

คำเตือน

เมื่อทำงานกับทรัพยากรที่กำหนดเองหากฟังก์ชัน lambda ขัดข้องคุณติดอยู่ระหว่าง 1 ถึง 2 ชั่วโมงเนื่องจาก CloudFormation รอการตอบกลับจากฟังก์ชัน (หยุดทำงาน) เป็นเวลาหนึ่งชั่วโมงก่อนที่จะยอมแพ้ ดังนั้นจึงเป็นการดีที่จะระบุการหมดเวลาสั้น ๆ สำหรับสแต็กขณะที่พัฒนาฟังก์ชันแลมบ์ดา


คำตอบที่ยอดเยี่ยม! ฉันอ่านมันและวิ่งไปในกองซ้อนของฉัน แต่สำหรับฉันฉันไม่กังวลเกี่ยวกับการเพิ่มขึ้นของฟังก์ชั่นแลมบ์ดาในบัญชีของฉันและฉันชอบแม่แบบที่เป็นแบบสแตนด์อโลน (ฉันทำให้เป็นโมดูลโดยใช้cloudformation-toolอัญมณี) ดังนั้น แม่แบบนั้นสามารถใช้งานได้โดยตรงแทนที่จะสร้างIdentityทรัพยากรที่กำหนดเอง ดูที่นี่สำหรับรหัสของฉัน: gist.github.com/guss77/2471e8789a644cac96992c4102936fb3
Guss

เมื่อคุณเป็น "... คุณติดอยู่ระหว่าง 1 ถึง 2 ชั่วโมง ... " เนื่องจากแลมบ์ดาล้มเหลวและไม่ตอบกลับด้วยการตอบสนองของ CFN คุณสามารถทำให้เทมเพลตเคลื่อนที่อีกครั้งโดยใช้ curl / wget URL ที่ลงนามแล้ว เพียงตรวจสอบให้แน่ใจว่าได้พิมพ์เหตุการณ์ / URL ที่จุดเริ่มต้นของแลมบ์ดาเสมอเพื่อให้คุณสามารถไปที่ CloudWatch และรับ 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" และเพื่อให้บ้านนั้นมีตัวแปรทั้งหมดของฉัน การทำงานสำหรับสายที่เรียบง่าย แต่คุณไม่สามารถใช้ Intrinsics (Refs, Fn :: ร่วม ฯลฯ ) ภายในแมป

ผลงาน:

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

จะไม่ทำงาน:

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

นั่นเป็นเพียงตัวอย่าง คุณจะไม่ใส่ Ref แบบสแตนด์อโลนลงในตัวแปร


1
เอกสารระบุว่าค่าการแม็พต้องเป็นสตริงตัวอักษร
Ivan Anishchuk

3

คุณสามารถใช้สแต็กที่ซ้อนกันซึ่งแก้ไขตัวแปรทั้งหมดของคุณในเอาท์พุตแล้วใช้Fn::GetAttเพื่ออ่านเอาต์พุตจากสแต็กนั้น


2

คุณอาจใช้เทมเพลตที่ซ้อนกันซึ่งคุณ "แก้ไข" ตัวแปรทั้งหมดของคุณในเทมเพลตด้านนอกและส่งต่อไปยังเทมเพลตอื่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.