Я искал ту же функциональность. Мне пришло в голову использовать вложенный стек, как предложил SpoonMeiser, но потом я понял, что на самом деле мне нужны пользовательские функции. К счастью, CloudFormation позволяет использовать AWS :: CloudFormation :: CustomResource, который, немного поработав, позволяет сделать именно это. Это похоже на излишество только для переменных (я бы сказал, что это должно было произойти в CloudFormation в первую очередь), но оно выполняет свою работу и, кроме того, учитывает всю гибкость (выбирайте python / node /Джава). Следует отметить, что лямбда-функции стоят денег, но мы говорим здесь ни копейки, если вы не создаете / удаляете свои стеки несколько раз в час.
Первым шагом является создание лямбда-функции на этой странице, которая делает только входные значения и копирует их в выходные данные. Мы могли бы заставить лямбда-функцию делать всякие сумасшедшие вещи, но как только мы получим функцию идентификации, все остальное будет легко. В качестве альтернативы мы можем создать лямбда-функцию в самом стеке. Поскольку я использую много стеков в 1 учетной записи, у меня будет целая куча оставшихся лямбда-функций и ролей (и все стеки должны быть созданы с помощью --capabilities=CAPABILITY_IAM
, так как для этого также нужна роль.
Создать лямбда-функцию
- Перейдите на домашнюю страницу lambda и выберите свой любимый регион
- Выберите «Пустую функцию» в качестве шаблона
- Нажмите «Далее» (не настраивайте триггеры)
- Заполните:
- Имя: CloudFormationIdentity
- Описание: Возвращает то, что получает, поддержка переменных в Cloud Formation.
- Время выполнения: python2.7
- Тип ввода кода: Редактировать код встроенный
- Код: см. Ниже
- Обработчик:
index.handler
- Роль: создание пользовательской роли. В этот момент открывается всплывающее окно, позволяющее создать новую роль. Примите все на этой странице и нажмите «Разрешить». Это создаст роль с разрешениями на публикацию в журналах cloudwatch.
- Память: 128 (это минимум)
- Тайм-аут: 3 секунды (должно быть достаточно)
- VPC: нет VPC
Затем скопируйте и вставьте приведенный ниже код в поле кода. Верхняя часть функции - это код из модуля Python 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 Create Request» в качестве примера шаблона. Вы должны увидеть в своем журнале, что переменные, переданные ему, возвращаются.
Используйте переменную в вашем шаблоне CloudFormation
Теперь, когда у нас есть эта лямбда-функция, мы можем использовать ее в шаблонах CloudFormation. Сначала запишите лямбда-функцию Arn (перейдите на домашнюю страницу lambda , нажмите на только что созданную функцию, 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 для лямбда-функции. Помещение этого в переменную здесь означает, что я должен указать это только один раз. Я делаю все мои переменные типа 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 ждет ответа от (сбойной) функции в течение часа, прежде чем сдаться. Поэтому, возможно, было бы неплохо указать короткое время ожидания для стека при разработке вашей лямбда-функции.