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? Можливо, можна використовувати "Вуса" для включення змінних у ваш шаблон і перетворення їх у JSON перед тим, як відправити його в Cloudformation. Залежить, як виглядає процес забезпечення.
Канутесон

Відповіді:


5

Я шукав однакового функціоналу. Використовуючи вкладений стек, як запропонував SpoonMeiser, прийшло в голову, але потім я зрозумів, що те, що мені насправді потрібно, - це власні функції. На щастя CloudFormation дозволяє використовувати AWS :: CloudFormation :: CustomResource, який, трохи працюючи, дозволяє зробити саме це. Це здається, що надмірна кількість лише змінних (я б заперечував, що це повинно було бути в CloudFormation в першу чергу), але це виконує роботу, і, крім того, дозволяє забезпечити всю гнучкість (виберіть пітон / вузол / java). Слід зазначити, що функції лямбда коштують грошей, але ми говоримо тут про копійки, якщо ви не створюєте / видаляєте свої стеки кілька разів на годину.

Перший крок - зробити на цій сторінці лямбда-функцію, яка не робить нічого, окрім як взяти вхідне значення та скопіювати його на вихід. Ми могли б змусити лямбда-функцію робити всілякі божевільні речі, але як тільки ми отримаємо функцію ідентичності, все інше легко. Крім того, ми могли б створити лямбда-функцію, що створюється в самій стеці. Оскільки я використовую багато стеків в одному обліковому записі, у мене буде ціла купа залишків лямбда-функцій та ролей (і всі стеки потрібно створювати за допомогою --capabilities=CAPABILITY_IAM, оскільки вона також потребує ролі.

Створіть лямбда-функцію

  • Перейдіть на домашню сторінку лямбда та виберіть улюблений регіон
  • Виберіть "Пусту функцію" як шаблон
  • Натисніть "Далі" (не налаштовуйте жодних тригерів)
  • Заповнити:
    • Назва: CloudFormationIdentity
    • Опис: Повертає отриману змінну підтримку у формуванні хмар
    • Час виконання: python2.7
    • Тип введення коду: редагування вбудованого коду
    • Код: див. Нижче
    • Обробник: index.handler
    • Роль: Створіть власну роль. У цей момент відкривається спливаюче вікно, що дозволяє створити нову роль. Прийміть усе на цій сторінці та натисніть «Дозволити». Це створить роль з дозволами на публікацію журналів хмарного перегляду.
    • Пам'ять: 128 (це мінімум)
    • Час очікування: 3 секунди (має бути багато)
    • VPC: немає VPC

Потім скопіюйте і вставте код нижче в поле коду. Вгорі функції - код з модуля python-відповіді cfn , який автоматично встановлюється лише в тому випадку, якщо лямбда-функція створена через 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змінну, яка містить арн для функції лямбда. Якщо помістити цю змінну тут, це означає, що я повинен лише вказати її один раз. Я роблю всі свої змінні типу Custom::Variable. CloudFormation дозволяє використовувати будь-яке ім’я типу, починаючи з Custom::користувацьких ресурсів.

Зауважте, що Identityзмінна містить Arn для лямбда-функції двічі. Один раз, щоб вказати функцію лямбда, яку потрібно використовувати. Другий раз як значення змінної.

Тепер, коли у мене є Identityзмінна, я можу визначити нові змінні за допомогою ServiceToken: !GetAtt [Identity, Arn](я думаю, що код JSON повинен бути чимось на зразок "ServiceToken": {"Fn::GetAtt": ["Identity", "Arn"]}). Я створюю 2 нові змінні, кожна з яких має 2 поля: Ім'я та Арн. У решті мого шаблону я можу використовувати !GetAtt [ClientBucketVar, Name]або !GetAtt [ClientBucketVar, Arn]коли мені це потрібно.

Слово обережності

Під час роботи з користувацькими ресурсами, якщо функція лямбда виходить з ладу, ви затримуєтесь від 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

Ні. Я спробував це, але вийшов порожнім. Для мене сенс мав сенс створити запис Mappings під назвою "CustomVariables" і мати в цьому будинку всі мої змінні. Він працює для простих рядків, але ви не можете використовувати Intrinsics (Refs, Fn :: Joins тощо) всередині Mappings .

Працює:

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

Не буде працювати:

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

Це лише приклад. Ви б не ставили автономний Ref у змінну.


1
Документація говорить, що значення для відображення повинні бути буквальними рядками.
Іван Аніщук

3

Ви можете використовувати вкладений стек, який вирішує всі ваші змінні в його виведеннях, а потім використовувати Fn::GetAttдля читання виходів з цього стека


2

Ви можете використовувати вкладені шаблони, в яких ви «розв’язуєте» всі свої змінні у зовнішньому шаблоні та передаєте їх іншому шаблону.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.