AWS CloudFormation - Biến tùy chỉnh trong mẫu


18

Có cách nào để xác định các phím tắt cho các giá trị thường được sử dụng bắt nguồn từ các tham số mẫu của CloudFormation không?

Ví dụ: Tôi đã có một tập lệnh tạo ngăn xếp Dự án Multi-AZ với tên ELB projectvà hai trường hợp đằng sau ELB được gọi project-1project-2. Tôi chỉ truyền ELBHostNametham số cho mẫu và sau đó sử dụng nó để xây dựng:

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

Cấu trúc này hoặc rất giống nhau được lặp lại nhiều lần trong toàn bộ mẫu - để tạo tên máy chủ EC2, bản ghi Route53, v.v.

Thay vì lặp đi lặp lại nhiều lần, tôi muốn gán đầu ra của nó Fn::Joincho một biến số nào đó và chỉ đề cập đến điều đó, giống như tôi có thể với "Ref":câu lệnh.

Lý tưởng nhất là:

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

hoặc một cái gì đó tương tự đơn giản.

Điều đó có khả thi với Amazon CloudFormation không?


ELBHostName có đầy đủ một tham số mà bạn rõ ràng đang chuyển đến Cloudform không? Nếu vậy, tại sao sử dụng một Ref? Có thể sử dụng Mustache để bao gồm các biến trong mẫu của bạn và chuyển đổi nó thành JSON trước khi chuyển nó sang Cloudform. Phụ thuộc vào quá trình cung cấp của bạn trông như thế nào.
Canuteson

Câu trả lời:


5

Tôi đã tìm kiếm các chức năng tương tự. Sử dụng một ngăn xếp lồng nhau như SpoonMeiser gợi ý đã xuất hiện, nhưng sau đó tôi nhận ra rằng điều tôi thực sự cần là các chức năng tùy chỉnh. May mắn thay, CloudFormation cho phép sử dụng AWS :: CloudFormation :: CustomResource , với một chút công việc, cho phép một người làm điều đó. Cảm giác này giống như quá mức cho các biến số (thứ mà tôi cho rằng đáng lẽ phải có trong CloudFormation ở nơi đầu tiên), nhưng nó đã hoàn thành công việc, và, ngoài ra, cho phép tất cả sự linh hoạt của (chọn lựa python / nút / java). Cần lưu ý rằng các hàm lambda tốn tiền, nhưng chúng ta đang nói về đồng xu ở đây trừ khi bạn tạo / xóa ngăn xếp của mình nhiều lần mỗi giờ.

Bước đầu tiên là tạo một hàm lambda trên trang này mà không làm gì ngoài việc lấy giá trị đầu vào và sao chép nó vào đầu ra. Chúng ta có thể có chức năng lambda làm tất cả các loại công cụ điên rồ, nhưng một khi chúng ta có chức năng nhận dạng, mọi thứ khác đều dễ dàng. Ngoài ra, chúng ta có thể có chức năng lambda được tạo trong ngăn xếp. Vì tôi sử dụng nhiều ngăn xếp trong 1 tài khoản, tôi sẽ có cả đống chức năng và vai trò lambda còn sót lại (và tất cả các ngăn xếp cần phải được tạo ra --capabilities=CAPABILITY_IAM, vì nó cũng cần một vai trò.

Tạo chức năng lambda

  • Tới trang chủ lambda và chọn khu vực yêu thích của bạn
  • Chọn "Hàm trống" làm mẫu
  • Nhấp vào "Tiếp theo" (không định cấu hình bất kỳ kích hoạt nào)
  • Điền vào:
    • Tên: CloudFormationIdentity
    • Mô tả: Trả về những gì nó nhận được, hỗ trợ biến trong Cloud Formation
    • Thời gian chạy: python2.7
    • Loại nhập mã: Chỉnh sửa nội tuyến mã
    • Mã: xem bên dưới
    • Xử lý: index.handler
    • Vai trò: Tạo Vai trò Tùy chỉnh. Tại thời điểm này, một cửa sổ bật lên mở ra cho phép bạn tạo một vai trò mới. Chấp nhận mọi thứ trên trang này và nhấp vào "Cho phép". Nó sẽ tạo ra một vai trò với quyền để đăng lên nhật ký đám mây.
    • Bộ nhớ: 128 (đây là mức tối thiểu)
    • Thời gian chờ: 3 giây (nên có nhiều)
    • VPC: Không có VPC

Sau đó sao chép-dán mã dưới đây trong trường mã. Phần trên cùng của hàm là mã từ mô-đun python phản hồi cfn , chỉ được cài đặt tự động nếu chức năng lambda được tạo thông qua CloudFormation, vì một số lý do lạ. Các handlerchức năng là khá tự giải thích.

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")
  • Nhấn tiếp"
  • Nhấp vào "Tạo chức năng"

Bây giờ bạn có thể kiểm tra chức năng lambda bằng cách chọn nút "Kiểm tra" và chọn "Yêu cầu tạo CloudFormation" làm mẫu mẫu. Bạn sẽ thấy trong nhật ký của mình rằng các biến được cung cấp cho nó, được trả về.

Sử dụng biến trong mẫu CloudFormation của bạn

Bây giờ chúng ta có chức năng lambda này, chúng ta có thể sử dụng nó trong các mẫu CloudFormation. Trước tiên hãy ghi chú chức năng lambda Arn (đi đến trang chủ lambda , nhấp vào chức năng vừa tạo, Arn phải ở trên cùng bên phải, đại loại như thế arn:aws:lambda:region:12345:function:CloudFormationIdentity).

Bây giờ trong mẫu của bạn, trong phần tài nguyên, chỉ định các biến của bạn như:

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]]]]

Đầu tiên tôi chỉ định một Identitybiến chứa Arn cho hàm lambda. Đặt cái này vào một biến ở đây, có nghĩa là tôi chỉ phải xác định nó một lần. Tôi thực hiện tất cả các biến của tôi về loại Custom::Variable. CloudFormation cho phép bạn sử dụng bất kỳ tên loại nào bắt đầu bằng Custom::tài nguyên tùy chỉnh.

Lưu ý rằng Identitybiến chứa Arn cho hàm lambda hai lần. Một lần để xác định chức năng lambda để sử dụng. Lần thứ hai là giá trị của biến.

Bây giờ tôi có Identitybiến, tôi có thể định nghĩa các biến mới bằng cách sử dụng ServiceToken: !GetAtt [Identity, Arn](Tôi nghĩ mã JSON phải giống như vậy "ServiceToken": {"Fn::GetAtt": ["Identity", "Arn"]}). Tôi tạo 2 biến mới, mỗi biến có 2 trường: Tên và Arn. Trong phần còn lại của mẫu tôi có thể sử dụng !GetAtt [ClientBucketVar, Name]hoặc !GetAtt [ClientBucketVar, Arn]bất cứ khi nào tôi cần.

Lời cảnh báo

Khi làm việc với tài nguyên tùy chỉnh, nếu chức năng lambda gặp sự cố, bạn sẽ bị kẹt trong khoảng từ 1 đến 2 giờ, vì CloudFormation chờ phản hồi từ chức năng (bị sập) trong một giờ trước khi bỏ cuộc. Do đó, có thể tốt khi chỉ định thời gian chờ ngắn cho ngăn xếp trong khi phát triển chức năng lambda của bạn.


Câu trả lời tuyệt vời! Tôi đã đọc nó và chạy nó trong ngăn xếp của mình, mặc dù đối với tôi, tôi không lo lắng về sự phổ biến của các hàm lambda trong tài khoản của mình và tôi thích các mẫu độc lập (tôi mô đun hóa bằng cloudformation-toolđá quý), vì vậy tôi đóng gói việc tạo lambda vào mẫu và sau đó có thể sử dụng nó trực tiếp thay vì tạo Identitytài nguyên tùy chỉnh. Xem ở đây để biết mã của tôi: gist.github.com/guss77/2471e8789a644cac96992c4102936fb3
Guss

Khi bạn "... bạn bị kẹt trong khoảng từ 1 đến 2 giờ ..." vì lambda bị hỏng và không trả lời lại bằng phản hồi cfn, bạn có thể lấy lại mẫu di chuyển bằng cách sử dụng curl / wget bằng tay URL đã ký. Chỉ cần đảm bảo luôn in ra sự kiện / URL ở đầu lambda để bạn có thể truy cập CloudWatch và nhận URL nếu nó bị treo.
Taylor

12

Tôi không có câu trả lời, nhưng tôi muốn chỉ ra rằng bạn có thể tự cứu mình rất nhiều nỗi đau bằng cách sử dụng Fn::Subthay choFn::Join

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

Thay thế

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

3

Không. Tôi đã thử nó, nhưng đến trống rỗng. Cách có ý nghĩa với tôi là tạo ra một mục Mappings có tên là "CustomVariables" và để ngôi nhà đó có tất cả các biến của tôi. Nó hoạt động cho các Chuỗi đơn giản, nhưng bạn không thể sử dụng Intrinsics (Refs, Fn :: Joins, v.v.) trong Mappings .

Làm:

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

Không làm việc:

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

Đó chỉ là một ví dụ. Bạn sẽ không đặt một Ref độc lập trong một biến.


1
Tài liệu nói rằng các giá trị ánh xạ phải là các chuỗi bằng chữ.
Ivan Anishchuk

3

Bạn có thể sử dụng ngăn xếp lồng nhau để giải quyết tất cả các biến của bạn trong các đầu ra của nó và sau đó sử dụng Fn::GetAttđể đọc các đầu ra từ ngăn xếp đó


2

Bạn có thể sử dụng các mẫu lồng nhau trong đó bạn "giải quyết" tất cả các biến của mình trong mẫu bên ngoài và chuyển chúng sang mẫu khác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.