自定义资源 CloudFormation 以标记事件规则

Custom Resource CloudFormation to tag Event Rules

我正在尝试在 CloudFormation 中创建自定义资源来标记事件规则。 这是 lambda:

from json import dumps
import sys
import traceback
import urllib.request
import boto3


def send_response(event, context, response):
    """Send a response to CloudFormation to handle the custom resource lifecycle"""
    response_body = {
        'Status': response,
        'StackId': event['StackId'],
        'RequestId': event['RequestId'],
        'LogicalResourceId': event['LogicalResourceId'],
    }
    print('RESPONSE BODY: \n' + dumps(response_body))
    data = dumps(response_body).encode('utf-8')
    req = urllib.request.Request(
        event['ResponseURL'],
        data,
        headers={'Content-Length': len(data), 'Content-Type': ''})
    req.get_method = lambda: 'PUT'
    try:
        with urllib.request.urlopen(req) as resp:
            print(f'response.status: {resp.status}, ' +
                  f'response.reason: {resp.reason}')
            print('response from cfn: ' + resp.read().decode('utf-8'))
    except Exception as e:
        print(e)
        raise Exception('Received non-200 response while sending response to AWS CloudFormation')
    return True


def custom_resource_handler(event, context):
    print("Event JSON: \n" + dumps(event))
    ResourceARN = event['ResourceProperties']['ResourceARN']
    tags = event['ResourceProperties']['Tags']
    response = 'FAILED'
    client = boto3.client('events')
    if event['RequestType'] == 'Create':
        try:
            response = client.tag_resource(
                ResourceARN=ResourceARN,
                Tags=tags)
            response = 'SUCCESS'
        except Exception as e:
            print(e)
        send_response(event, context, response)
        return

    if event['RequestType'] == 'Update':
        # Do nothing and send a success immediately
        send_response(event, context, response)
        return

    if event['RequestType'] == 'Delete':
        try:
            response = client.untag_resource(
                ResourceARN = ResourceARN,
                TagKeys = tags['Key']
            )
            response = 'SUCCESS'
        except Exception as e:
            print(e)
        send_response(event, context, response)


def lambda_handler(event, context):
    """Lambda handler for the custom resource"""
    try:
        return custom_resource_handler(event, context)
    except Exception as e:
        print(e)
        raise

这是 CFN 块:

CustomTagEvent:
    Type: Custom::TagEventRule
    Version: "1.0"
    DependsOn: EventRule
    Properties:
      ServiceToken: "LAMBDA_ARN"
      ResourceARN: 
        Fn::GetAtt:
          - "EventRule"
          - "Arn"
      Tags: 
        - 
          Key: Name

创建 CLoudFormation 时出现错误“CREATE FAILED”。

“无效的 PhysicalResourceId”

但是,不知何故设法创建了标签。 需要帮助来理解为什么它在创建标签时会出现 CloudFormation 错误?

自定义资源需要 specific output 其中包含 PhysicalResourceId:

{
   "Status" : "SUCCESS",
   "PhysicalResourceId" : "TestResource1",
   "StackId" : "arn:aws:cloudformation:us-west-2:123456789012:stack/stack-name/guid",
   "RequestId" : "unique id for this create request",
   "LogicalResourceId" : "MyTestResource",
   "Data" : {
      "OutputName1" : "Value1",
      "OutputName2" : "Value2",
   }
}

您的 lambda 函数不会生成这样的响应,因此会出错。

创建自定义资源的最佳方法是使用 AWS 专门为此目的提供的 cfn-response

这解决了标记问题:

Code:
        ZipFile: |
          import boto3
          import botocore
          from botocore.exceptions import ClientError
          import cfnresponse
          import json
          client = boto3.client("events")
          def handler(event, context):
            ResourceARN = event['ResourceProperties']['ResourceARN']
            tags = event['ResourceProperties']['Tags']
            if event['RequestType'] == 'Create':
              response = client.tag_resource(
                ResourceARN = ResourceARN,
                Tags = tags
              )
              response = 'SUCCESS'
              responseData = {}
              responseData['Data'] = response
              cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
            
            if event['RequestType'] == 'Update':
              response = 'SUCCESS'
              responseData = {}
              responseData['Data'] = response
              cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")

            if event['RequestType'] == 'Delete':
              for i in tags:
                response = client.untag_resource(
                  ResourceARN = ResourceARN,
                  TagKeys = [i['Key']]
                )
                response = 'SUCCESS'
                responseData = {}
                responseData['Data'] = response
                cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")