Estaba buscando la misma funcionalidad. El uso de una pila anidada como SpoonMeiser sugirió vino a mi mente, pero luego me di cuenta de que lo que realmente necesitaba eran funciones personalizadas. Afortunadamente, CloudFormation permite el uso de AWS :: CloudFormation :: CustomResource que, con un poco de trabajo, le permite a uno hacer exactamente eso. Esto se siente como una exageración solo para variables (algo que diría que debería haber estado en CloudFormation en primer lugar), pero hace el trabajo y, además, permite toda la flexibilidad de (elija Python / node /Java). Cabe señalar que las funciones lambda cuestan dinero, pero aquí estamos hablando de centavos a menos que cree / elimine sus pilas varias veces por hora.
El primer paso es hacer que una función lambda en esta página no haga nada más que tomar el valor de entrada y copiarlo a la salida. Podríamos hacer que la función lambda haga todo tipo de locuras, pero una vez que tenemos la función de identidad, cualquier otra cosa es fácil. Alternativamente, podríamos crear la función lambda en la pila misma. Como uso muchas pilas en 1 cuenta, tendría un montón de funciones y roles lambda sobrantes (y todas las pilas deben crearse --capabilities=CAPABILITY_IAM
, ya que también necesita un rol.
Crear función lambda
- Vaya a la página de inicio de lambda y seleccione su región favorita
- Seleccione "Función en blanco" como plantilla
- Haga clic en "Siguiente" (no configure ningún desencadenante)
- Llenar:
- Nombre: CloudFormationIdentity
- Descripción: Devuelve lo que se obtiene, soporte variable en la formación en la nube
- Tiempo de ejecución: python2.7
- Tipo de entrada de código: Editar código en línea
- Código: ver abajo
- Manipulador:
index.handler
- Rol: crear un rol personalizado. En este punto, se abre una ventana emergente que le permite crear una nueva función. Acepte todo en esta página y haga clic en "Permitir". Creará un rol con permisos para publicar en los registros de cloudwatch.
- Memoria: 128 (este es el mínimo)
- Tiempo de espera: 3 segundos (debería ser suficiente)
- VPC: sin VPC
Luego copie y pegue el código a continuación en el campo de código. La parte superior de la función es el código del módulo de python cfn-response , que solo se instala automáticamente si la función lambda se crea a través de CloudFormation, por alguna extraña razón. La handler
función es 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")
- Haga clic en Siguiente"
- Haga clic en "Crear función"
Ahora puede probar la función lambda seleccionando el botón "Prueba" y seleccione "Solicitud de creación de CloudFormation" como plantilla de muestra. Debería ver en su registro que se devuelven las variables alimentadas.
Usar variable en su plantilla de CloudFormation
Ahora que tenemos esta función lambda, podemos usarla en las plantillas de CloudFormation. Primero tome nota de la función lambda Arn (vaya a la página de inicio lambda , haga clic en la función recién creada, el Arn debe estar en la parte superior derecha, algo así como arn:aws:lambda:region:12345:function:CloudFormationIdentity
).
Ahora en su plantilla, en la sección de recursos, especifique sus variables 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]]]]
Primero especifico una Identity
variable que contiene el Arn para la función lambda. Poner esto en una variable aquí, significa que solo tengo que especificarlo una vez. Hago todas mis variables de tipo Custom::Variable
. CloudFormation le permite usar cualquier tipo de nombre que comience con Custom::
recursos personalizados.
Tenga en cuenta que la Identity
variable contiene el Arn para la función lambda dos veces. Una vez para especificar la función lambda a utilizar. La segunda vez como el valor de la variable.
Ahora que tengo la Identity
variable, puedo definir nuevas variables usando ServiceToken: !GetAtt [Identity, Arn]
(creo que el código JSON debería ser algo así como "ServiceToken": {"Fn::GetAtt": ["Identity", "Arn"]}
). Creo 2 nuevas variables, cada una con 2 campos: Nombre y Arn. En el resto de mi plantilla puedo usar !GetAtt [ClientBucketVar, Name]
o !GetAtt [ClientBucketVar, Arn]
cuando lo necesite.
Palabra de precaución
Al trabajar con recursos personalizados, si la función lambda se bloquea, está atascado entre 1 y 2 horas, porque CloudFormation espera una respuesta de la función (bloqueada) durante una hora antes de darse por vencido. Por lo tanto, podría ser bueno especificar un tiempo de espera corto para la pila mientras se desarrolla la función lambda.