aws-cdk icon indicating copy to clipboard operation
aws-cdk copied to clipboard

Custom Resources: Issues Updating with Replacement

Open sbmsr opened this issue 2 years ago • 2 comments

Describe the bug

When updating a custom resource that requires replacement of the underlying resource (ex - need to replace subscription because filter has changed), CloudFormation triggers a delete operation to remove the old resource. This occurs because the update process involves deleting the existing event source mapping and creating a new one, which results in a new PhysicalResourceId.

In my usecase (replace subscription due to filter change), CloudFormation interprets the change in PhysicalResourceId as a resource replacement and sends a delete request for the old resource, even though the deletion has already been handled by my custom resource logic.

Ideally I could update without deletion by instead create a new resource, and letting the CloudFormation delete request handle the deletion later. However, some resources need to be unique (ex - can't have two subscriptions to the same topic and lambda).

How can I use PhysicalResourceId as intended, when replacing a resource for which there can only be one of?

Expected Behavior

I expected CloudFormation not to send a delete request after an update operation when the custom resource logic has already handled the deletion of the old resource.

Current Behavior

CloudFormation sends a delete request for the old resource after an update operation, despite the fact that the custom resource logic has preemptively performed the deletion. This results in errors since the resource to be deleted no longer exists.

Reproduction Steps

  1. Create a CloudFormation stack with a custom resource that manages a Lambda event source mapping.
  2. Update the stack with changes that require the event source mapping to be deleted and recreated.
  3. Observe that after the update, CloudFormation sends a delete request for the old event source mapping.

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

2.81.0 (build bd920f2)

Framework Version

No response

Node.js Version

v18.17.1

OS

MacOS 14.2.1 (23C71)

Language

TypeScript

Language Version

No response

Other information

No response

sbmsr avatar Feb 23 '24 16:02 sbmsr

Hi

It would be helpful if you can provide some minimal self-sustainable sample code so we can discuss the workaround. This seems not a bug but a general guidance request here.

pahud avatar Feb 23 '24 17:02 pahud

Thanks for the quick reply! Below is a simplified code example of the custom resource lambda that captures my use case.

import {
  CloudFormationCustomResourceEvent,
  CloudFormationCustomResourceResponse
} from 'aws-lambda';

const lambdaClient = new LambdaClient();

async function checkEventSourceMappingExists(functionName: string, eventSourceArn: string): Promise<boolean> {
  // Logic to determine if the event source mapping exists;
}

async function deleteEventSourceMapping(uuid: string): Promise<void> {
  // Logic to delete the event source mapping
}

async function createEventSourceMapping(functionName: string, eventSourceArn: string): Promise<string> {
  // Logic to create the event source mapping
}

export const handler = async (event: CloudFormationCustomResourceEvent): Promise<CloudFormationCustomResourceResponse> => {
  const functionName = event.ResourceProperties.FunctionName;
  const eventSourceArn = event.ResourceProperties.EventSourceArn;

  // Handle both create and update events
  if (event.RequestType === 'Create' || event.RequestType === 'Update') {
    const exists = await checkEventSourceMappingExists(functionName, eventSourceArn);

    if (exists) {
      // If the mapping exists, delete it before creating a new one
      await deleteEventSourceMapping(...);
    }

    // Create a new mapping
    const newUuid = await createEventSourceMapping(functionName, eventSourceArn);

    return {
      Status: 'SUCCESS',
      PhysicalResourceId: newUuid, // CF will trigger a delete event since the PhysicalResourceId changed
      StackId: event.StackId,
      RequestId: event.RequestId,
      LogicalResourceId: event.LogicalResourceId,
      Data: {}
    };
  }

  // Handle delete event
  // ...

};

sbmsr avatar Feb 23 '24 19:02 sbmsr

We generally recommend using custom resource provider framework so you can just focus on the onCreate, onUpdate, onDelete event handling. Is there any reason you need to handle that rather than using the framework?

pahud avatar Feb 27 '24 20:02 pahud

This doesn't seem to be a bug and I am moving this to discussion for general guidance.

pahud avatar Feb 27 '24 20:02 pahud