Eu estava procurando a mesma funcionalidade. O uso de uma pilha aninhada como o SpoonMeiser sugeriu me veio à mente, mas então percebi que o que realmente precisava era de funções personalizadas. Felizmente, o CloudFormation permite o uso do AWS :: CloudFormation :: CustomResource que, com um pouco de trabalho, permite fazer exatamente isso. Isso parece um exagero para apenas variáveis (algo que eu diria que deveria estar no CloudFormation em primeiro lugar), mas faz o trabalho e, além disso, permite toda a flexibilidade de (faça a sua escolha em python / nó /Java). Deve-se notar que as funções lambda custam dinheiro, mas estamos falando de centavos aqui, a menos que você crie / exclua suas pilhas várias vezes por hora.
O primeiro passo é criar uma função lambda nesta página que não faça nada além de pegar o valor de entrada e copiá-lo para a saída. Poderíamos fazer com que a função lambda fizesse todo tipo de coisa maluca, mas uma vez que tenhamos a função de identidade, qualquer outra coisa será fácil. Como alternativa, poderíamos ter a função lambda sendo criada na própria pilha. Como eu uso muitas pilhas em uma conta, eu teria várias funções e funções restantes do lambda (e todas as pilhas precisam ser criadas com --capabilities=CAPABILITY_IAM
, pois ela também precisa de uma função).
Criar função lambda
- Vá para a página inicial lambda e selecione sua região favorita
- Selecione "Função em branco" como modelo
- Clique em "Avançar" (não configure nenhum gatilho)
- Preencha:
- Nome: CloudFormationIdentity
- Descrição: retorna o que recebe, suporte variável na formação de nuvens
- Tempo de execução: python2.7
- Tipo de entrada de código: Editar código embutido
- Código: veja abaixo
- Manipulador:
index.handler
- Função: crie uma função personalizada. Nesse momento, um pop-up é aberto, permitindo que você crie uma nova função. Aceite tudo nesta página e clique em "Permitir". Ele criará uma função com permissões para postar nos logs do cloudwatch.
- Memória: 128 (este é o mínimo)
- Tempo limite: 3 segundos (deve ser suficiente)
- VPC: sem VPC
Em seguida, copie e cole o código abaixo no campo de código. A parte superior da função é o código do módulo python cfn-response , que só é instalado automaticamente se a função lambda for criada por meio do CloudFormation, por algum motivo estranho. A handler
função é bastante auto-explicativa.
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")
- Clique em "Next"
- Clique em "Criar Função"
Agora você pode testar a função lambda selecionando o botão "Teste" e selecione "Solicitação de criação do CloudFormation" como modelo de amostra. Você deve ver em seu log que as variáveis alimentadas a ele são retornadas.
Use variável no seu modelo CloudFormation
Agora que temos essa função lambda, podemos usá-la nos modelos do CloudFormation. Primeiro, anote a função lambda Arn (vá para a página inicial lambda , clique na função recém-criada, o Arn deve estar no canto superior direito, algo assim arn:aws:lambda:region:12345:function:CloudFormationIdentity
).
Agora, no seu modelo, na seção de recursos, especifique suas variáveis como:
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]]]]
Primeiro, eu especifico uma Identity
variável que contém o Arn para a função lambda. Colocar isso em uma variável aqui significa que eu tenho que especificá-lo apenas uma vez. Eu faço todas as minhas variáveis do tipo Custom::Variable
. O CloudFormation permite que você use qualquer nome de tipo começando por Custom::
para recursos personalizados.
Observe que a Identity
variável contém o Arn para a função lambda duas vezes. Uma vez para especificar a função lambda a ser usada. A segunda vez como o valor da variável.
Agora que tenho a Identity
variável, posso definir novas variáveis usando ServiceToken: !GetAtt [Identity, Arn]
(acho que o código JSON deve ser algo como "ServiceToken": {"Fn::GetAtt": ["Identity", "Arn"]}
). Crio 2 novas variáveis, cada uma com 2 campos: Nome e Arn. No restante do meu modelo, eu posso usar !GetAtt [ClientBucketVar, Name]
ou !GetAtt [ClientBucketVar, Arn]
sempre que precisar.
Palavra de cautela
Ao trabalhar com recursos personalizados, se a função lambda travar, você ficará preso por 1 a 2 horas, porque o CloudFormation aguarda uma resposta da função (travada) por uma hora antes de desistir. Portanto, pode ser bom especificar um tempo limite curto para a pilha ao desenvolver sua função lambda.