- Notifications
You must be signed in to change notification settings - Fork 4.4k
Description
Describe the bug
When an S3 bucket is created with autoDeleteObjects: true and later updated to disable it (autoDeleteObjects: false), the Custom Resource deletion triggers an AccessDenied error. This is because the Lambda function's execution role does not include the s3:GetBucketTagging permission, which is required by the isBucketTaggedForDeletion() function in the auto-delete-objects handler.
The relevant handler code is here:
https://github.com/aws/aws-cdk/blob/main/packages/%40aws-cdk/custom-resource-handlers/lib/aws-s3/auto-delete-objects-handler/index.ts#L92-L124
async function onDelete(bucketName?: string) { if (!bucketName) { throw new Error('No BucketName was provided.'); } try { if (!await isBucketTaggedForDeletion(bucketName)) { console.log(`Bucket does not have '${AUTO_DELETE_OBJECTS_TAG}' tag, skipping cleaning.`); return; } await denyWrites(bucketName); await emptyBucket(bucketName); } catch (error: any) { // Bucket doesn't exist, all is well if (error.name === 'NoSuchBucket') { console.log(`Bucket '${bucketName}' does not exist.`); return; } throw error; } } /** * The bucket will only be tagged for deletion if it is being deleted in the same * deployment as this Custom Resource. * * If the Custom Resource is ever deleted before the bucket, it must be because * `autoDeleteObjects` has been switched to false, in which case the tag would have * been removed before we get to this Delete event. */ async function isBucketTaggedForDeletion(bucketName: string) { const response = await s3.getBucketTagging({ Bucket: bucketName }); return response.TagSet?.some(tag => tag.Key === AUTO_DELETE_OBJECTS_TAG && tag.Value === 'true'); }During a stack update that disables autoDeleteObjects, CloudFormation removes the bucket policy (which grants s3:GetBucket*) before the Custom Resource's Delete event fires. At that point, the Lambda role only has AWSLambdaBasicExecutionRole permissions , so the getBucketTagging call fails with AccessDenied.
The stack update itself eventually succeeds because the Lambda function and IAM role are deleted afterward, but the AccessDenied error is logged and may cause confusion or delays.
Regression Issue
- Select this option if this issue appears to be a regression.
Last Known Working CDK Library Version
No response
Expected Behavior
The Custom Resource Lambda execution role should include s3:GetBucketTagging permission directly on the IAM role (not only via the bucket policy), so that the isBucketTaggedForDeletion() check succeeds even after the bucket policy has been removed during a stack update.
Current Behavior
When disabling autoDeleteObjects, CloudFormation removes the bucket policy before deleting the Custom Resource. The Lambda then fails with AccessDenied on getBucketTagging because the s3:GetBucket* permission was only granted via the bucket policy.
Reproduction Steps
const bucket = new s3.Bucket(this, 'Bucket', { bucketName: 'egmfjgjfjf', blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, enforceSSL: true, removalPolicy: cdk.RemovalPolicy.DESTROY, autoDeleteObjects: true, }); - deploy the stack with
autoDeleteObjects: trueandRemovalPolicy.DESTROY - comment out removalPolicy and autoDeleteObjects (default: false)
- run
cdk deploy
Possible Solution
Add s3:GetBucketTagging policy with the Lambda function's execution role, rather than relying solely on the bucket policy statement which uses s3:GetBucket*.
Additional Information/Context
No response
AWS CDK Library version (aws-cdk-lib)
2.243.0
AWS CDK CLI version
2.1111.0
Node.js Version
22.9.0
OS
Mac
Language
TypeScript
Language Version
No response
Other information
Related issue: #14649