s3.Bucket: AccessDenied when disabling autoDeleteObjects property
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
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
```typescript
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,
});
```
1. deploy the stack with `autoDeleteObjects: true` and `RemovalPolicy.DESTROY`
2. comment out removalPolicy and autoDeleteObjects (default: false)
3. 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
Contributor guide
Research direction
Start with packages/@aws-cdk/custom-resource-handlers/lib/aws-s3/auto-delete-objects-handler/index.ts, especially onDelete() and isBucketTaggedForDeletion(), then trace where the Lambda execution role permissions are defined. Reproduce the update from autoDeleteObjects: true to false and verify that the role can perform the tagging check after the bucket policy is removed, with no AccessDenied error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, typescript
- Domain
- cloud, infrastructure, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 52/100