Eu estava procurando a mesma funcionalidade. Usando uma pilha aninhada como SpoonMeiser sugerido veio à mente, mas depois percebi que o que eu realmente precisava era de funções personalizadas. Felizmente, o CloudFormation permite o uso de AWS :: CloudFormation :: CustomResource que, com um pouco de trabalho, permite que se faça exatamente isso. Isso parece um exagero para apenas variáveis (algo que eu diria que deveria ter sido no CloudFormation, em primeiro lugar), mas ele faz o trabalho e, além disso, permite toda a flexibilidade de (escolha a opção python / node /Java). Deve-se notar que as funções lambda custam dinheiro, mas estamos falando de tostões aqui, a menos que você crie / apague suas pilhas várias vezes por hora.
O primeiro passo é fazer uma função lambda nesta página que não faz nada além de pegue o valor de entrada e copie-o para a saída. Poderíamos ter a função lambda fazendo todo tipo de coisa maluca, mas uma vez que tenhamos a função de identidade, qualquer outra coisa é fácil. Alternativamente, poderíamos ter a função lambda sendo criada na própria pilha. Como eu uso muitas pilhas em uma conta, eu teria um monte de funções e funções lambda restantes (e todas as pilhas precisam ser criadas com --capabilities=CAPABILITY_IAM
, pois também precisa de um papel.
Criar função lambda
- Acesse a página inicial do lambda e selecione o seu região favorita
- Selecione "Função em branco" como modelo
- Clique em "Próximo" (não configure nenhum acionador)
- Preencha:
- Nome: CloudFormationIdentity
- Descrição: Retorna o que é obtido, suporte a variáveis na Cloud Formation
- Tempo de execução: python2.7
- Tipo de entrada de código: Editar código in-line
- Código: veja abaixo
- Manipulador:
index.handler
- Função: crie uma função personalizada. Nesse ponto, 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
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 de resposta , que só recebe auto-instalado se a função lambda é criada através do CloudFormation, por algum motivo estranho. A função handler
é bastante autoexplicativa.
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 "Próximo"
- Clique em "Criar função"
Agora você pode testar a função lambda selecionando o botão "Test" e selecionando "CloudFormation Create Request" como modelo de amostra. Você deve ver em seu log que as variáveis alimentadas são retornadas.
Use variáveis no seu modelo do CloudFormation
Agora que temos essa função lambda, podemos usá-la nos modelos do CloudFormation. Primeiro tome nota da função lambda Arn (vá para a página inicial do lambda , clique no botão apenas a função criada, o Arn deve estar no canto superior direito, algo como 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]]]]
Primeiramente eu especifico uma variável Identity
que contém o Arn para a função lambda. Colocar isso em uma variável aqui significa que eu só preciso especificá-lo uma vez. Eu faço todas as minhas variáveis do tipo Custom::Variable
. O CloudFormation permite que você use qualquer nome de tipo que comece com Custom::
para recursos personalizados.
Observe que a variável Identity
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 variável Identity
, 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"]}
). Eu crio 2 novas variáveis, cada uma com 2 campos: Name e Arn. No restante do meu modelo, 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ê fica preso entre 1 e 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 enquanto desenvolve sua função lambda.