example code cloudformation amazon-web-services aws-lambda amazon-cloudformation
http://serverless-arch-eu-west-1.s3.amazonaws.com/serverless.zip

amazon-web-services - code - cloudformation lambda zipfile example



¿Cómo crear una nueva versión de una función Lambda usando CloudFormation? (7)

Estoy tratando de crear una nueva versión de una función Lambda usando CloudFormation.

Quiero tener varias versiones de la misma función de Lambda para poder (a) apuntar alias a diferentes versiones, como DEV y PROD, y (b) poder revertir a una versión anterior

Esta es la definición de mi versión Lambda:

LambdaVersion: Type: AWS::Lambda::Version Properties: FunctionName: Ref: LambdaFunction

Una versión se crea cuando se ejecuta "aws cloudformation create-stack" pero los comandos subsiguientes de "aws cloudformation update-stack" no hacen nada. No hay nuevas versiones de Lambda creadas.

Estoy intentando obtener una nueva versión de la función Lambda creada después de cargar un nuevo archivo zip en S3 y luego ejecutar "update-stack". ¿Puedo hacerlo con CloudFormation? ¿Está AWS :: Lambda :: Version realmente dañada (como se menciona aquí https://github.com/hashicorp/terraform/issues/6067#issuecomment-211708071 ) o simplemente no estoy obteniendo algo?

Actualización 11/01/17 Respuesta oficial del soporte de Amazon: "... para cualquier nueva versión que se publique, debe definir una adición (sic) AWS :: Lambda :: Version resource ..."

El equipo de AWS CloudFormation / Lambda, si está leyendo esto, esto es inaceptable. Arreglalo.


  1. Podemos hacer un paquete de despliegue Lambda;
  2. Pase el paquete Lambda con la versión como uno de los parámetros de formación de la nube, por ejemplo, "LambdaPakcageNameWithVersion";
  3. Utilice "LambdaPakcageNameWithVersion" como clave de código Lambda s3;
  4. El nuevo paquete Lamdba se implementará cuando se ejecute el comando aws-cli para actualizar la pila de información en la nube o cuando se ejecute CI / CD.

MyLambda: Type: AWS::Lambda::Function Properties: Role: LambdaRole Code: S3Bucket: LambdaPackageS3Bucket S3Key: !Sub "${LambdaPakcageNameWithVersion}" FunctionName: LambdaFunctionName Handler: lambda_function.lambda_handler Runtime: python3.6 Timeout: 60


AWS :: Lambda :: La versión no es útil. Tienes que agregar un nuevo recurso para cada versión Lambda. Si desea publicar una nueva versión para cada actualización de Cloudformation, debe piratear el sistema.

Resolví este problema creando un recurso personalizado con respaldo Lambda que se activa para cada implementación. Dentro de este Lambda, estoy creando una nueva versión para la función Lambda dada en el parámetro.

Para la fuente de Lambda puede consultar http://serverless-arch-eu-west-1.s3.amazonaws.com/serverless.zip

Aquí está el ejemplo de Cloud Formation que utiliza esta función de implementación de Lambda (es posible que necesite alguna modificación):

{ "AWSTemplateFormatVersion": "2010-09-09", "Parameters": { "DeploymentTime": { "Type": "String", "Description": "It is a timestamp value which shows the deployment time. Used to rotate sources." } }, "Resources": { "LambdaFunctionToBeVersioned": { "Type": "HERE_DEFINE_YOUR_LAMBDA" }, "DeploymentLambdaRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" ] }, "Action": [ "sts:AssumeRole" ] } ] }, "Path": "/", "ManagedPolicyArns": [ "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" ], "Policies": [ { "PolicyName": "LambdaExecutionPolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:PublishVersion" ], "Resource": [ "*" ] } ] } } ] } }, "DeploymentLambda": { "Type": "AWS::Lambda::Function", "Properties": { "Role": { "Fn::GetAtt": [ "DeploymentLambdaRole", "Arn" ] }, "Handler": "serverless.handler", "Runtime": "nodejs4.3", "Code": { "S3Bucket": { "Fn::Sub": "serverless-arch-${AWS::Region}" }, "S3Key": "serverless.zip" } } }, "LambdaVersion": { "Type": "Custom::LambdaVersion", "Properties": { "ServiceToken": { "Fn::GetAtt": [ "DeploymentLambda", "Arn" ] }, "FunctionName": { "Ref": "LambdaFunctionToBeVersioned" }, "DeploymentTime": { "Ref": "DeploymentTime" } } } } }

(Descargo de responsabilidad: este código forma parte de mi libro; para más información sobre Lambda y API Gateway, puede consultar: https://www.amazon.com/Building-Serverless-Architectures-Cagatay-Gurturk/dp/1787129195 )


Buscando algo similar que funcione con las funciones Lambda implementadas desde S3.

Mi caso de uso fue este:

  • Tiene una plantilla de formación de nubes que crea una función Lambda desde una ubicación de cubeta S3
  • Debe actualizar esta función para realizar cambios en el código localmente y enviar los cambios a S3
  • Ahora desea enviar estos cambios a Lambda, por lo que intenta actualizar la pila y la información de la nube indica que no hay cambios para actualizar, por lo que debe recurrir a la actualización manual del código mediante la consola de AWS Lambda.

No contento con esto busqué una alternativa y encontré esta pregunta. Ninguna de las respuestas funcionó exactamente para mí, así que tomé algunas ideas y adapté las respuestas aquí e hice mi propia versión escrita en Python.

Este código está adaptado de la respuesta de @wjordan, por lo que se le atribuye la idea y la respuesta original. Las diferencias son:

  • Esto está escrito en Python.
  • Funciona con el código Lambda desplegado desde un cubo S3
  • Actualiza el código y publica una nueva versión.

Necesita un parámetro nonce. Cambia el valor de este parámetro cuando el código debe volver a publicarse en Lambda. Esto es para garantizar que la formación de nube actualizará su recurso personalizado. Cuando se actualice el recurso personalizado, ejecutará el código Python que en última instancia actualiza su código Lambda.

Espero que esto ayude a alguien.

Description: Publish a new version of a Lambda function whenever the code is updated. Parameters: Nonce: Description: Change this string when code is updated. Type: String Default: "Test" Resources: MyCustomResource: Type: Custom::Resource Properties: ServiceToken: !GetAtt MyFunction.Arn Nonce: !Ref Nonce MyFunction: Type: AWS::Lambda::Function Properties: Handler: index.handler Role: !GetAtt LambdaExecutionRole.Arn Code: S3Bucket: BucketContainingYourLambdaFunction S3Key: KeyToYourLambdaFunction.zip Runtime: "python3.6" LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: ''2012-10-17'' Statement: - Effect: Allow Principal: {Service: [lambda.amazonaws.com]} Action: [''sts:AssumeRole''] Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole LambdaDeployCustomResource: Type: Custom::LambdaVersion Properties: ServiceToken: !GetAtt LambdaDeployFunction.Arn FunctionName: !Ref MyFunction S3Bucket: BucketContainingYourLambdaFunction S3Key: KeyToYourLambdaFunction.zip Nonce: !Ref Nonce LambdaDeployFunction: Type: AWS::Lambda::Function DependsOn: LambdaDeployFunctionExecutionRole Properties: Handler: "index.handler" Role: !GetAtt LambdaDeployFunctionExecutionRole.Arn Code: ZipFile: !Sub | import boto3 import json import logging import cfnresponse import time from botocore.exceptions import ClientError def handler(event, context): logger = logging.getLogger() logger.setLevel(logging.INFO) logger.info (f"Input parameters from cloud formation: {event}") responseData = {} if (event["RequestType"] == ''Delete''): logger.info("Responding to delete event...") cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) try: lambdaClient = boto3.client(''lambda'') s3Bucket = event[''ResourceProperties''][''S3Bucket''] s3Key = event[''ResourceProperties''][''S3Key''] functionName = event[''ResourceProperties''][''FunctionName''] logger.info("Updating the function code for Lambda function ''{}'' to use the code stored in S3 bucket ''{}'' at key location ''{}''".format(functionName, s3Bucket, s3Key)) logger.info("Sleeping for 5 seconds to allow IAM permisisons to take effect") time.sleep(5) response = lambdaClient.update_function_code( FunctionName=functionName, S3Bucket=''{}''.format(s3Bucket), S3Key=''{}''.format(s3Key), Publish=True) responseValue = "Function: {}, Version: {}, Last Modified: {}".format(response["FunctionName"],response["Version"],response["LastModified"]) responseData[''Data''] = responseValue cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, response["FunctionArn"]) except ClientError as e: errorMessage = e.response[''Error''][''Message''] logger.error(errorMessage) cfnresponse.send(event, context, cfnresponse.FAILED, responseData) Runtime: "python3.6" Timeout: "30" LambdaDeployFunctionExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: ''2012-10-17'' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: ReadS3BucketContainingLambdaCode PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - s3:GetObject Resource: ArnOfS3BucketContainingLambdaCode/* - PolicyName: UpdateCodeAndPublishVersion PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - lambda:UpdateFunctionCode - lambda:PublishVersion Resource: ''*'' Outputs: LambdaVersion: Value: !GetAtt LambdaDeploy.Version CustomResourceResult: Value: !GetAtt MyCustomResource.Result


Desafortunadamente, esto no es posible hacerlo usando CloudFormation. Deberá agregar nuevas secciones de AWS::Lambda::Version en su plantilla de CloudFormation para cada versión.

La solución más cercana sería crear plantillas .erb y hacer que genere plantillas de CloudFormation con todas las versiones.


El recurso AWS::Lambda::Version solo representa una única AWS::Lambda::Version función Lambda publicada; no publicará automáticamente nuevas versiones en cada actualización de su código. Para lograr esto, tienes dos opciones:

1. recurso personalizado

Puede implementar su propio recurso personalizado que llama a PublishVersion en cada actualización.

Para este enfoque, aún tendrá que cambiar al menos un Parámetro cada vez que actualice su pila, para activar una actualización en el Recurso Personalizado que activará la acción PublishVersion. (Sin embargo, no tendrá que actualizar la plantilla).

Aquí hay un ejemplo completo de trabajo:

Description: Publish a new version of a Lambda function whenever the code is updated. Parameters: Nonce: Description: Change this string when code is updated. Type: String Default: "Test" Resources: MyCustomResource: Type: Custom::Resource Properties: ServiceToken: !GetAtt MyFunction.Arn Nonce: !Ref Nonce MyFunction: Type: AWS::Lambda::Function Properties: Handler: index.handler Role: !GetAtt LambdaExecutionRole.Arn Code: ZipFile: !Sub | var response = require(''cfn-response''); exports.handler = function(event, context) { return response.send(event, context, response.SUCCESS, {Result: ''${Nonce}''}); }; Runtime: nodejs4.3 LambdaDeploy: Type: Custom::LambdaVersion Properties: ServiceToken: !GetAtt LambdaDeployFunction.Arn FunctionName: !Ref MyFunction Nonce: !Ref Nonce LambdaDeployFunction: Type: AWS::Lambda::Function Properties: Handler: "index.handler" Role: !GetAtt LambdaExecutionRole.Arn Code: ZipFile: !Sub | var AWS = require(''aws-sdk''); var response = require(''cfn-response''); exports.handler = (event, context) => { console.log("Request received:/n", JSON.stringify(event)); if (event.RequestType == ''Delete'') { return response.send(event, context, response.SUCCESS); } var lambda = new AWS.Lambda(); lambda.publishVersion({FunctionName: event.ResourceProperties.FunctionName}).promise().then((data) => { return response.send(event, context, response.SUCCESS, {Version: data.Version}, data.FunctionArn); }).catch((e) => { return response.send(event, context, response.FAILED, e); }); }; Runtime: nodejs4.3 LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: ''2012-10-17'' Statement: - Effect: Allow Principal: {Service: [lambda.amazonaws.com]} Action: [''sts:AssumeRole''] Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: PublishVersion PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: [''lambda:PublishVersion''] Resource: ''*'' Outputs: LambdaVersion: Value: !GetAtt LambdaDeploy.Version CustomResourceResult: Value: !GetAtt MyCustomResource.Result

2. preprocesador de plantillas

Puede usar un preprocesador de plantillas como Ruby incorporado (o simplemente actualizar manualmente su plantilla en cada implementación) para publicar una nueva versión en cada actualización de su código cambiando la identificación lógica del recurso de AWS::Lambda::Version cada vez que se actualice su código.

Ejemplo:

# template.yml Description: Publish a new version of a Lambda function whenever the code is updated. <%nonce = rand 10000%> Resources: LambdaVersion<%=nonce%>: Type: AWS::Lambda::Version Properties: FunctionName: !Ref MyFunction MyCustomResource: Type: Custom::Resource Properties: ServiceToken: !GetAtt MyFunction.Arn Nonce: <%=nonce%> MyFunction: Type: AWS::Lambda::Function Properties: Handler: index.handler Role: !GetAtt LambdaExecutionRole.Arn Code: ZipFile: !Sub | var response = require(''cfn-response''); exports.handler = function(event, context) { return response.send(event, context, response.SUCCESS, {Result: ''<%=nonce%>''}); }; Runtime: nodejs4.3 LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: ''2012-10-17'' Statement: - Effect: Allow Principal: {Service: [lambda.amazonaws.com]} Action: [''sts:AssumeRole''] Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Outputs: LambdaVersion: Value: !GetAtt LambdaVersion<%=nonce%>.Version CustomResourceResult: Value: !GetAtt MyCustomResource.Result

Para crear / actualizar la pila mientras pasa template.yml través del preprocesador de plantillas erb , ejecute:

aws cloudformation [create|update]-stack / --stack-name [stack_name] / --template-body file://<(ruby -rerb -e "puts ERB.new(ARGF.read).result" < template.yml) / --capabilities CAPABILITY_IAM


Tengo un caso de uso similar (necesito usar CloudFormation para administrar una función lambda para ser usada @edge en CloudFront, para lo cual siempre se requiere una versión específica de la función lambda, no $LATEST ) y mis búsquedas primero me permitieron esta pregunta, pero después de un poco más de excavación, me alegró descubrir que ahora hay soporte nativo para el control automático de versiones lambda con la nueva función AutoPublishAlias del Modelo de Aplicación Sin AutoPublishAlias de AWS (básicamente un conjunto adicional opcional de construcciones de nivel superior para sus plantillas de CloudFormation).

Anunciado aquí: https://github.com/awslabs/serverless-application-model/issues/41#issuecomment-347723981

Para más detalles ver:

Esencialmente incluye AutoPublishAlias en su definición de AWS::Serverless::Function AutoPublishAlias

MyFunction: Type: "AWS::Serverless::Function" Properties: # ... AutoPublishAlias: MyAlias

Y luego en otra parte de la plantilla de CloudFormation puede hacer referencia a la última versión publicada como !Ref MyFunction.Version (sintaxis yaml).


Respuesta actualizada para febrero de 2018

Puede usar AWS SAM (Modelo de aplicación sin servidor) , y su sam package sam deploy y los comandos de sam deploy para actualizar Lambda . Son similares al aws cloudformation package y aws cloudformation deploy , pero también le permiten actualizar las versiones Lambda automáticamente.

SAM puede empaquetar su código (o tomar el paquete ZIP que creó de otra manera), cargarlo en S3 y actualizar la versión más $LATEST de Lambda de $LATEST . (Si esto es todo lo que necesita, también se puede hacer con aws cloudformation , sin SAM; los ejemplos de código son los mismos que a continuación, pero solo utilizan las declaraciones estándar de CloudFormation ). Luego, con SAM, si está configurado en consecuencia, también puede publicar automáticamente una Versión y actualizar un Alias ​​para que apunte a ella. También puede, opcionalmente, usar AWS CodeDeploy para mover gradualmente el tráfico de la versión anterior a la nueva, y revertir en caso de errores. Todo esto se explica en las implementaciones de Safe Lambda .

Técnicamente, la idea es que cada vez que actualice la pila, necesite el Code AWS::Lambda::Function para apuntar al nuevo paquete en S3. Esto asegurará que cuando actualice la pila, la versión de $ LATEST de Lambda se actualizará del nuevo paquete. Luego, también puede automatizar la publicación de una nueva versión y cambiarle un alias.

Para ello, cree una plantilla SAM, que es similar a (un superconjunto de) plantilla de CloudFormation. Puede incluir declaraciones específicas de SAM, como la de AWS::Serverless::Function continuación. Apunte el Code al directorio del código fuente (o un ZIP preempaquetado), y establezca la propiedad AutoPublishAlias .

... MyFunction: Type: AWS::Serverless::Function Properties: ... # all usual CloudFormation properties are accepted AutoPublishAlias: dev # will publish a Version and create/update Alias `dev` to point to it Code: ./my/lambda/src ...

Correr:

$ sam package --template-file template.yaml --output-template-file packaged.yaml --s3-bucket my-bucket

Este paquete contiene el contenido del directorio como un ZIP (si el Code no es un ZIP ya), lo carga en S3 con una nueva clave generada automáticamente, y genera la plantilla final de CloudFormation en packaged.yaml , colocando en usted la referencia de Code adecuada; Me gusta esto:

... MyFunction: Properties: Code: S3Bucket: my-bucket S3Key: ddeeaacc44ddee33ddaaee223344 ...

Ahora puede usar packaged.yaml generado con SAM, para crear la versión de la función:

sam deploy --template-file packaged.yaml --stack-name my-stack [--capabilities ...]

Esto actualizará la versión $LATEST AutoPublishAlias Lambda y, si se definió AutoPublishAlias , lo publicará como una nueva versión y actualizará el alias para que apunte a la versión recién publicada.

Vea los ejemplos en el repositorio de SAM GitHub para un código de plantilla completo.