¿Cómo actualizo el módulo cfn-response de AWS CloudFormation para las funciones de Lambda de AWS que se ejecutan en Python 2.7/3.6/3.7?

5 minutos de lectura
0

Quiero actualizar el módulo cfn-response de AWS CloudFormation para las funciones de AWS Lambda que se ejecutan en Python 2.7/3.6/3.7.

Resolución

Nota: Los siguientes pasos son pertinentes solo para las funciones de Lambda que se ejecutan en Python 2.7/3.6/3.7. Los siguientes comandos son válidos para entornos Linux y macOS. La sintaxis puede variar en Windows PowerShell.

Nota: Si recibe errores al ejecutar los comandos de la interfaz de la línea de comandos de AWS (AWS CLI), asegúrese de usar la versión más reciente de AWS CLI.

1.    Para encontrar las pilas que contienen recursos personalizados, ejecute el siguiente comando:

aws cloudformation list-stacks --region us-east-1 | grep -oE 'arn:[^"]+' | while read arn; do aws cloudformation list-stack-resources --stack-name $arn --region us-east-1 | grep -E '(Custom::)|(::CustomResource)' | awk '{print $2}' | while read resource; do if [[ -n $resource ]]; then echo $arn; echo $resource; fi; done; done

Debería ver un resultado similar al del siguiente ejemplo:

arn:aws:cloudformation:us-east-1:123456789012:stack/TestStack/3497b950-55f1-11eb-aad4-124a026c8667
"ResourceType": "AWS::CloudFormation::CustomResource",

2.    Para encontrar la función de Lambda asociada al recurso personalizado, ejecute el siguiente comando para comprobar la propiedad ServiceToken del recurso personalizado desde la plantilla de la pila:

aws cloudformation get-template --stack-name TestStack | jq -r .TemplateBody

Nota: El comando del paso 2 muestra una vista previa de la plantilla de la pila utilizando la opción jq (de la página web de jq) para dar formato a la respuesta.

Debería ver un resultado similar al del siguiente ejemplo:

Resources:
  MyCustomResource:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt MyFunction.Arn
      Name: "John"
  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt MyRole.Arn
      Runtime: python3.7
      Code:
        ZipFile: |
          import cfnresponse
          def handler(event, context):
            responseData = {'Message': 'Hello {}!'.format(event['ResourceProperties']['Name'])}
            cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
  MyRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
Outputs:
  Result:
    Value: !GetAtt MyCustomResource.Message

Nota: La plantilla que se obtiene del resultado del paso 2 es un ejemplo de plantilla básica para un recurso personalizado basado en Lambda. La propiedad ServiceToken: !GetAtt MyFunction.Arn se encuentra en la sección MyCustomResource. El valor obtenido por el método !GetAtt MyFunction**.Arn** de la propiedad ServiceToken es el nombre de recurso de Amazon (ARN) del tema de Amazon Simple Notification Service (Amazon SNS) o de la función de Lambda.

3.    En la plantilla del paso 2, identifique dónde está definida su función de Lambda.

Si su función de Lambda se encuentra en la misma pila que el recurso personalizado, vaya al paso 4. Por ejemplo, la función Fn::GetAtt del paso 2 muestra que la función de Lambda está definida en la misma plantilla que el recurso personalizado.

Si la propiedad ServiceToken dirige a un ARN con código rígido, la función de Lambda podría estar en otra pila. Si la propiedad ServiceToken se resuelve a través de Fn::Import, utilice la API list-exports de AWS CloudFormation para buscar el valor. Por ejemplo:

aws cloudformation list-exports --region us-east-1
{
    "Exports": [
        {
            "ExportingStackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/SomeOtherStack/481dc040-b283-11e9-b1bd-12d607a4fd1c",
            "Value": "arn:aws:lambda:us-east-1:123456789012:function:SomeOtherStack-MyFunction-5ZE2CQO8RAA9",
            "Name": "MyExport"
        }
    ]
}

A continuación, compruebe si hay etiquetas de función ubicadas en una pila independiente utilizando list-tags para localizar el ARN de la pila de AWS CloudFormation. Por ejemplo:

aws lambda list-tags --resource arn:aws:lambda:us-east-1:123456789012:function:TestStack-MyFunction-5ZE2CQO8RAA9 | grep stack-id

Recibirá un resultado similar al siguiente:

"aws:cloudformation:stack-id": "arn:aws:cloudformation:us-east-1:123456789012:stack/TestStack/3497b950-55f1-11eb-aad4-124a026c8667"

Nota: También puede encontrar etiquetas de función en la consola de AWS Lambda.

4.    Para permitir que AWS CloudFormation cargue el módulo cfn-response más reciente en su función de Lambda, actualice el código fuente integrado de su función de Lambda. Por ejemplo:

Code:
        ZipFile: |
          import cfnresponse
          def handler(event, context):
            responseData = {'Message': 'Hello {}!'.format(event['ResourceProperties']['Name'])}
            cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")

Nota: Consulte el paso 2 para ver una plantilla de ejemplo que tiene una función de Lambda con código fuente integrado.

Ahora, AWS CloudFormation carga el siguiente ejemplo de código del módulo cfn-response en su función de Lambda. Por ejemplo:

from botocore.vendored import requests
import json

SUCCESS = "SUCCESS"
FAILED = "FAILED"

def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False):
    responseUrl = event['ResponseURL']

    print(responseUrl)

    responseBody = {}
    responseBody['Status'] = responseStatus
    responseBody['Reason'] = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name
    responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name
    responseBody['StackId'] = event['StackId']
    responseBody['RequestId'] = event['RequestId']
    responseBody['LogicalResourceId'] = event['LogicalResourceId']
    responseBody['NoEcho'] = noEcho
    responseBody['Data'] = responseData

Nota: Para obtener más información, consulte los ejemplos de código en la sección «Código fuente del módulo» del módulo cfn-response.

El ejemplo de código del módulo cfn-response utiliza botocore.requests en el paquete de despliegue de la función de Lambda.

Para actualizar el módulo cfn-response a la última versión que utiliza urllib3, actualice el código integrado de la función en la plantilla de AWS CloudFormation. Para ello, añada un comentario al código integrado de la función de Lambda. Por ejemplo:

ZipFile: |
           import cfnresponse
           def handler(event, context):
+            # This comment was added to force an update on this function's code
             responseData = {'Message': 'Hello {}!'.format(event['ResourceProperties']['Name'])}
             cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
   MyRole:

5.    Guarde los cambios realizados en la plantilla que contiene su función de Lambda.

6.    Actualice su pila.

El módulo cfn-response se modifica una vez que la pila ha terminado de actualizarse.

Nota: Si el código de su función está ubicado en un bucket de Amazon Simple Storage Service (Amazon S3) o en una imagen de Amazon Elastic Container Registry (Amazon ECR), deberá actualizar el módulo usted mismo para incluir la versión con urllib3. Para obtener el código fuente de la última versión del módulo cfn-response, consulte cfn-response module.

Nota: Si un tiempo de ejecución nuevo de Python o JavaScript introduce un cambio disruptivo, debe actualizar el módulo cfn-response. En lugar de actualizar de nuevo el archivo zip, puede adjuntar automáticamente la versión más reciente del módulo cfn-response cada vez que se actualice la propiedad Runtime de una función.


OFICIAL DE AWS
OFICIAL DE AWSActualizada hace 4 años