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

aws-cdk-lib/aws-s3: Removing object lock from s3 bucket leads to InternalFailure due to getObjectLockEnabled() is null

Open newton-wi opened this issue 6 months ago • 3 comments

Describe the bug

I have cdk code to create a S3 bucket with ObjectLock optionally turned on, depending on a property in the respective stage. When I deploy the stack with ObjectLock turned off and then change it to on this change can be deployed flawlessly (and ObjectLock then indeed is turned on on the bucket).

However, when I then try to disable ObjectLock again I get: `7:23:45 AM | UPDATE_FAILED | AWS::S3::Bucket | foobucketB1767E36 Resource handler returned message: "Cannot invoke "java.lang.Boolean.booleanValue()" because the return value of "software.amazon.s3.bucket.ResourceModel.getObjectLockEnabled()" is null" (RequestToken: 2b263817-blah-bla-foo-bar, HandlerErrorCode: InternalFailure)

❌ mystack failed: _ToolkitError: The stack named mystack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "Cannot invoke "java.lang.Boolean.booleanValue()" because the return value of "software.amazon.s3.bucket.ResourceModel.getObjectLockEnabled()" is null" (RequestToken: 2b263817-blah-bla-foo-bar, HandlerErrorCode: InternalFailure)`

CDK code looks like: ` this.bucketprops = { removalPolicy: props.removalPolicy, versioned: true, lifecycleRules: [ { //expiration: cdk.Duration.days(1), transitions: [ { storageClass: s3.StorageClass.GLACIER_INSTANT_RETRIEVAL, transitionAfter: cdk.Duration.days(1), } ] } ], };

if (props.objectLockDefaultRetention !== undefined) {
  this.bucketprops = Object.assign(this.bucketprops, {objectLockDefaultRetention: s3.ObjectLockRetention.governance(cdk.Duration.days(props.objectLockDefaultRetention))});
}

this.inbucket = new s3.Bucket(this, 'foobucket', this.bucketprops);`

Regression Issue

  • [ ] Select this option if this issue appears to be a regression.

Last Known Working CDK Library Version

No response

Expected Behavior

When stack deployed and props.objectLockDefaultRetention is some integer, change that prop to undefined and redeploy leads to object lock turned off on bucket.

Current Behavior

When stack deployed and props.objectLockDefaultRetention is some integer, change that prop to undefined and redeploy leads to `7:23:45 AM | UPDATE_FAILED | AWS::S3::Bucket | foobucketB1767E36 Resource handler returned message: "Cannot invoke "java.lang.Boolean.booleanValue()" because the return value of "software.amazon.s3.bucket.ResourceModel.getObjectLockEnabled()" is null" (RequestToken: 2b263817-blah-bla-foo-bar, HandlerErrorCode: InternalFailure)

❌ mystack failed: _ToolkitError: The stack named mystack failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "Cannot invoke "java.lang.Boolean.booleanValue()" because the return value of "software.amazon.s3.bucket.ResourceModel.getObjectLockEnabled()" is null" (RequestToken: 2b263817-blah-bla-foo-bar, HandlerErrorCode: InternalFailure)`

Reproduction Steps

When stack deployed and props.objectLockDefaultRetention is some integer, change that prop to undefined and redeploy.

Possible Solution

"software.amazon.s3.bucket.ResourceModel.getObjectLockEnabled() is null" and "InternalFailure" sounds like a bug to me - should be fixed. If there is some error/mistake/misunderstanding on my side, please bear with me - this are my first steps in cdk... ;-)

Additional Information/Context

No response

AWS CDK Library version (aws-cdk-lib)

[email protected]

AWS CDK CLI version

2.1017.1 (build 60506e5)

Node.js Version

v22.15.0

OS

github devcontainer Ubuntu 20.04.6 LTS

Language

TypeScript

Language Version

TypeScript (5.8.3)

Other information

No response

newton-wi avatar Jun 13 '25 06:06 newton-wi

Hi @newton-wi,

Thank you for reporting this issue! You've encountered a combination of an AWS S3 service limitation and poor error handling in the CDK/CloudFormation stack.

Root Cause Analysis:

The error occurs because you're attempting to remove Object Lock from a bucket that already has it enabled. Here's what's happening:

  1. You initially deployed with props.objectLockDefaultRetention set to an integer value, which enabled Object Lock
  2. You then changed props.objectLockDefaultRetention to undefined, expecting to disable Object Lock
  3. The CDK's parseObjectLockConfig method returns undefined when no retention is specified
  4. This causes CloudFormation to attempt transitioning from Object Lock enabled to disabled
  5. The AWS S3 service rejects this change, but the CloudFormation resource handler fails with a Java NullPointerException instead of a clear error message

AWS S3 Object Lock Limitation:

According to the AWS documentation:

Important: After you enable Object Lock on a bucket, you can't disable Object Lock or suspend versioning for that bucket.

The Bug:

While the AWS limitation is expected, the CDK should provide a clear validation error instead of allowing CloudFormation to fail with this cryptic message:

"Cannot invoke "java.lang.Boolean.booleanValue()" because the return value of "software.amazon.s3.bucket.ResourceModel.getObjectLockEnabled()" is null"

Workarounds:

Keep Object Lock enabled but remove default retention rules:

this.bucketprops = {
  // ... other props
  objectLockEnabled: true,  // Keep this enabled
  // Don't set objectLockDefaultRetention - this removes default retention but keeps Object Lock enabled
};

Proposed Solution: Since the CDK cannot track previous deployment state, the best approach would be to:

  1. Improve validation: Require users to explicitly set objectLockEnabled: false when attempting to disable Object Lock, then throw a clear error message
  2. Better documentation: Add warnings to the objectLockDefaultRetention property documentation about the permanent nature of Object Lock
  3. CloudFormation fix: The underlying issue is that the AWS CloudFormation S3 resource handler should provide a meaningful error instead of a Java NullPointerException

The CDK fix would involve modifying the parseObjectLockConfig method to detect explicit attempts to disable Object Lock and provide clear guidance, rather than trying to track state transitions.

I am making it a p2 and we welcome PRs to improve the user experience!

pahud avatar Jun 13 '25 12:06 pahud

Huh? Using the AWS Console I can disable object lock on a bucket where it was enabled. So, it definitively is possible in principle. (As seen in the code we are using object lock in gouvernance mode - might be a different case in compliance mode. There you should not be able to remove object lock from objects in the bucket - if it is possible to remove the default object lock on the bucket I do not know).

newton-wi avatar Jun 13 '25 12:06 newton-wi

But thanks for the hint with specifying the objectLockEnabled property.

Changing my conditional variable definition to

if (props.objectLockDefaultRetention !== undefined) {
  this.bucketprops = Object.assign(this.bucketprops, {
    objectLockDefaultRetention: s3.ObjectLockRetention.governance(cdk.Duration.days(props.objectLockDefaultRetention)),
    objectLockEnabled: true,
  });
} else {
  this.bucketprops = Object.assign(this.bucketprops, {
    objectLockEnabled: false,
  });
}

I do not longer get an error BUT the default object lock is not removed from the bucket. Probably due to see https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html?icmpid=docs_amazons3_console

To override or remove governance-mode retention settings, you must have the s3:BypassGovernanceRetention permission and must explicitly include x-amz-bypass-governance-retention:true as a request header with any request that requires overriding governance mode.

Note By default, the Amazon S3 console includes the x-amz-bypass-governance-retention:true header. If you try to delete objects protected by governance mode and have the s3:BypassGovernanceRetention permission, the operation will succeed.

I have s3:BypassGovernanceRetention, so doing it via console works, I guess cdk does not set the x-amz-bypass-governance-retention:true header.

newton-wi avatar Jun 13 '25 13:06 newton-wi