diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/cdk.out new file mode 100644 index 0000000000000..c6e612584e352 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"38.0.1"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6.assets.json new file mode 100644 index 0000000000000..2b97edf118e7d --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6.assets.json @@ -0,0 +1,19 @@ +{ + "version": "38.0.1", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/integ.json new file mode 100644 index 0000000000000..891f7148a6a18 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "38.0.1", + "testCases": { + "cdk-integ-s3-lifecycle-transitions/DefaultTest": { + "stacks": [ + "s3-lifecycle-transitions" + ], + "assertionStack": "cdk-integ-s3-lifecycle-transitions/DefaultTest/DeployAssert", + "assertionStackName": "cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/manifest.json new file mode 100644 index 0000000000000..a3ae1e633c8fb --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/manifest.json @@ -0,0 +1,121 @@ +{ + "version": "38.0.1", + "artifacts": { + "s3-lifecycle-transitions.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "s3-lifecycle-transitions.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "s3-lifecycle-transitions": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "s3-lifecycle-transitions.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "notificationArns": [], + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/ddad2eaffb71d50ee55436f9aff0d51b46f314389ef9056ee42d2c2cf1e0ca83.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "s3-lifecycle-transitions.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "s3-lifecycle-transitions.assets" + ], + "metadata": { + "/s3-lifecycle-transitions/AllStorageClasses128K/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "AllStorageClasses128KB8241761" + } + ], + "/s3-lifecycle-transitions/VariesByStorageClass/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "VariesByStorageClassCD3C88D5" + } + ], + "/s3-lifecycle-transitions/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/s3-lifecycle-transitions/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "s3-lifecycle-transitions" + }, + "cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "notificationArns": [], + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "cdkintegs3lifecycletransitionsDefaultTestDeployAssert9A611ED6.assets" + ], + "metadata": { + "/cdk-integ-s3-lifecycle-transitions/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/cdk-integ-s3-lifecycle-transitions/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "cdk-integ-s3-lifecycle-transitions/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/s3-lifecycle-transitions.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/s3-lifecycle-transitions.assets.json new file mode 100644 index 0000000000000..be5669105e6a6 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/s3-lifecycle-transitions.assets.json @@ -0,0 +1,19 @@ +{ + "version": "38.0.1", + "files": { + "ddad2eaffb71d50ee55436f9aff0d51b46f314389ef9056ee42d2c2cf1e0ca83": { + "source": { + "path": "s3-lifecycle-transitions.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "ddad2eaffb71d50ee55436f9aff0d51b46f314389ef9056ee42d2c2cf1e0ca83.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/s3-lifecycle-transitions.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/s3-lifecycle-transitions.template.json new file mode 100644 index 0000000000000..920525ca1009d --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/s3-lifecycle-transitions.template.json @@ -0,0 +1,102 @@ +{ + "Resources": { + "AllStorageClasses128KB8241761": { + "Type": "AWS::S3::Bucket", + "Properties": { + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "DEEP_ARCHIVE", + "TransitionInDays": 30 + } + ] + }, + { + "ObjectSizeGreaterThan": 200000, + "ObjectSizeLessThan": 300000, + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "ONEZONE_IA", + "TransitionInDays": 30 + } + ] + } + ], + "TransitionDefaultMinimumObjectSize": "all_storage_classes_128K" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "VariesByStorageClassCD3C88D5": { + "Type": "AWS::S3::Bucket", + "Properties": { + "LifecycleConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "DEEP_ARCHIVE", + "TransitionInDays": 30 + } + ] + }, + { + "ObjectSizeGreaterThan": 200000, + "ObjectSizeLessThan": 300000, + "Status": "Enabled", + "Transitions": [ + { + "StorageClass": "ONEZONE_IA", + "TransitionInDays": 30 + } + ] + } + ], + "TransitionDefaultMinimumObjectSize": "varies_by_storage_class" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/tree.json new file mode 100644 index 0000000000000..de565fbb364c5 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.js.snapshot/tree.json @@ -0,0 +1,197 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "s3-lifecycle-transitions": { + "id": "s3-lifecycle-transitions", + "path": "s3-lifecycle-transitions", + "children": { + "AllStorageClasses128K": { + "id": "AllStorageClasses128K", + "path": "s3-lifecycle-transitions/AllStorageClasses128K", + "children": { + "Resource": { + "id": "Resource", + "path": "s3-lifecycle-transitions/AllStorageClasses128K/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "lifecycleConfiguration": { + "rules": [ + { + "status": "Enabled", + "transitions": [ + { + "storageClass": "DEEP_ARCHIVE", + "transitionInDays": 30 + } + ] + }, + { + "status": "Enabled", + "transitions": [ + { + "storageClass": "ONEZONE_IA", + "transitionInDays": 30 + } + ], + "objectSizeLessThan": 300000, + "objectSizeGreaterThan": 200000 + } + ], + "transitionDefaultMinimumObjectSize": "all_storage_classes_128K" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "VariesByStorageClass": { + "id": "VariesByStorageClass", + "path": "s3-lifecycle-transitions/VariesByStorageClass", + "children": { + "Resource": { + "id": "Resource", + "path": "s3-lifecycle-transitions/VariesByStorageClass/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "lifecycleConfiguration": { + "rules": [ + { + "status": "Enabled", + "transitions": [ + { + "storageClass": "DEEP_ARCHIVE", + "transitionInDays": 30 + } + ] + }, + { + "status": "Enabled", + "transitions": [ + { + "storageClass": "ONEZONE_IA", + "transitionInDays": 30 + } + ], + "objectSizeLessThan": 300000, + "objectSizeGreaterThan": 200000 + } + ], + "transitionDefaultMinimumObjectSize": "varies_by_storage_class" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "s3-lifecycle-transitions/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "s3-lifecycle-transitions/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "cdk-integ-s3-lifecycle-transitions": { + "id": "cdk-integ-s3-lifecycle-transitions", + "path": "cdk-integ-s3-lifecycle-transitions", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "cdk-integ-s3-lifecycle-transitions/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "cdk-integ-s3-lifecycle-transitions/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "cdk-integ-s3-lifecycle-transitions/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "cdk-integ-s3-lifecycle-transitions/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "cdk-integ-s3-lifecycle-transitions/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.ts new file mode 100644 index 0000000000000..fd192ee4531e5 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-s3/test/integ.lifecycle-transitions.ts @@ -0,0 +1,53 @@ +import { App, Duration, RemovalPolicy, Stack } from 'aws-cdk-lib'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import { Bucket, StorageClass, TransitionDefaultMinimumObjectSize } from 'aws-cdk-lib/aws-s3'; + +const app = new App(); + +const stack = new Stack(app, 's3-lifecycle-transitions'); + +new Bucket(stack, 'AllStorageClasses128K', { + lifecycleRules: [ + { + transitions: [{ + storageClass: StorageClass.DEEP_ARCHIVE, + transitionAfter: Duration.days(30), + }], + }, + { + objectSizeLessThan: 300000, + objectSizeGreaterThan: 200000, + transitions: [{ + storageClass: StorageClass.ONE_ZONE_INFREQUENT_ACCESS, + transitionAfter: Duration.days(30), + }], + }, + ], + transitionDefaultMinimumObjectSize: TransitionDefaultMinimumObjectSize.ALL_STORAGE_CLASSES_128_K, + removalPolicy: RemovalPolicy.DESTROY, +}); + +new Bucket(stack, 'VariesByStorageClass', { + lifecycleRules: [ + { + transitions: [{ + storageClass: StorageClass.DEEP_ARCHIVE, + transitionAfter: Duration.days(30), + }], + }, + { + objectSizeLessThan: 300000, + objectSizeGreaterThan: 200000, + transitions: [{ + storageClass: StorageClass.ONE_ZONE_INFREQUENT_ACCESS, + transitionAfter: Duration.days(30), + }], + }, + ], + transitionDefaultMinimumObjectSize: TransitionDefaultMinimumObjectSize.VARIES_BY_STORAGE_CLASS, + removalPolicy: RemovalPolicy.DESTROY, +}); + +new IntegTest(app, 'cdk-integ-s3-lifecycle-transitions', { + testCases: [stack], +}); \ No newline at end of file diff --git a/packages/aws-cdk-lib/aws-s3/README.md b/packages/aws-cdk-lib/aws-s3/README.md index 3c400997ceba9..c93aa152c1d58 100644 --- a/packages/aws-cdk-lib/aws-s3/README.md +++ b/packages/aws-cdk-lib/aws-s3/README.md @@ -862,6 +862,40 @@ const bucket = new s3.Bucket(this, 'MyBucket', { }); ``` +To indicate which default minimum object size behavior is applied to the lifecycle configuration, use the +`transitionDefaultMinimumObjectSize` property. + +The default value of the property before September 2024 is `TransitionDefaultMinimumObjectSize.VARIES_BY_STORAGE_CLASS` +that allows objects smaller than 128 KB to be transitioned only to the S3 Glacier and S3 Glacier Deep Archive storage classes, +otherwise `TransitionDefaultMinimumObjectSize.ALL_STORAGE_CLASSES_128_K` that prevents objects smaller than 128 KB from being +transitioned to any storage class. + +To customize the minimum object size for any transition you +can add a filter that specifies a custom `objectSizeGreaterThan` or `objectSizeLessThan` for `lifecycleRules` +property. Custom filters always take precedence over the default transition behavior. + +```ts +new s3.Bucket(this, 'MyBucket', { + transitionDefaultMinimumObjectSize: s3.TransitionDefaultMinimumObjectSize.VARIES_BY_STORAGE_CLASS, + lifecycleRules: [ + { + transitions: [{ + storageClass: s3.StorageClass.DEEP_ARCHIVE, + transitionAfter: Duration.days(30), + }], + }, + { + objectSizeLessThan: 300000, + objectSizeGreaterThan: 200000, + transitions: [{ + storageClass: s3.StorageClass.ONE_ZONE_INFREQUENT_ACCESS, + transitionAfter: Duration.days(30), + }], + }, + ], +}); +``` + ## Object Lock Configuration [Object Lock](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock-overview.html) diff --git a/packages/aws-cdk-lib/aws-s3/lib/bucket.ts b/packages/aws-cdk-lib/aws-s3/lib/bucket.ts index 6143cea6b6ce1..6ca50b3fbc4fe 100644 --- a/packages/aws-cdk-lib/aws-s3/lib/bucket.ts +++ b/packages/aws-cdk-lib/aws-s3/lib/bucket.ts @@ -1403,6 +1403,24 @@ export abstract class TargetObjectKeyFormat { public abstract _render(): CfnBucket.LoggingConfigurationProperty['targetObjectKeyFormat']; } +/** + * The transition default minimum object size for lifecycle + */ +export enum TransitionDefaultMinimumObjectSize { + /** + * Objects smaller than 128 KB will not transition to any storage class by default. + */ + ALL_STORAGE_CLASSES_128_K = 'all_storage_classes_128K', + + /** + * Objects smaller than 128 KB will transition to Glacier Flexible Retrieval or Glacier + * Deep Archive storage classes. + * + * By default, all other storage classes will prevent transitions smaller than 128 KB. + */ + VARIES_BY_STORAGE_CLASS = 'varies_by_storage_class', +} + export interface BucketProps { /** * The kind of server-side encryption to apply to this bucket. @@ -1528,6 +1546,18 @@ export interface BucketProps { */ readonly lifecycleRules?: LifecycleRule[]; + /** + * Indicates which default minimum object size behavior is applied to the lifecycle configuration. + * + * To customize the minimum object size for any transition you can add a filter that specifies a custom + * `objectSizeGreaterThan` or `objectSizeLessThan` for `lifecycleRules` property. Custom filters always + * take precedence over the default transition behavior. + * + * @default - TransitionDefaultMinimumObjectSize.VARIES_BY_STORAGE_CLASS before September 2024, + * otherwise TransitionDefaultMinimumObjectSize.ALL_STORAGE_CLASSES_128_K. + */ + readonly transitionDefaultMinimumObjectSize?: TransitionDefaultMinimumObjectSize; + /** * The name of the index document (e.g. "index.html") for the website. Enables static website * hosting for this bucket. @@ -1910,6 +1940,7 @@ export class Bucket extends BucketBase { protected disallowPublicAccess?: boolean; private accessControl?: BucketAccessControl; private readonly lifecycleRules: LifecycleRule[] = []; + private readonly transitionDefaultMinimumObjectSize?: TransitionDefaultMinimumObjectSize; private readonly eventBridgeEnabled?: boolean; private readonly metrics: BucketMetrics[] = []; private readonly cors: CorsRule[] = []; @@ -1934,6 +1965,7 @@ export class Bucket extends BucketBase { const objectLockConfiguration = this.parseObjectLockConfig(props); this.objectOwnership = props.objectOwnership; + this.transitionDefaultMinimumObjectSize = props.transitionDefaultMinimumObjectSize; const resource = new CfnBucket(this, 'Resource', { bucketName: this.physicalName, bucketEncryption, @@ -2244,7 +2276,10 @@ export class Bucket extends BucketBase { const self = this; - return { rules: this.lifecycleRules.map(parseLifecycleRule) }; + return { + rules: this.lifecycleRules.map(parseLifecycleRule), + transitionDefaultMinimumObjectSize: this.transitionDefaultMinimumObjectSize, + }; function parseLifecycleRule(rule: LifecycleRule): CfnBucket.RuleProperty { const enabled = rule.enabled ?? true; diff --git a/packages/aws-cdk-lib/aws-s3/test/bucket.test.ts b/packages/aws-cdk-lib/aws-s3/test/bucket.test.ts index e526d6150d195..0ce54d5a999e4 100644 --- a/packages/aws-cdk-lib/aws-s3/test/bucket.test.ts +++ b/packages/aws-cdk-lib/aws-s3/test/bucket.test.ts @@ -3852,5 +3852,29 @@ describe('bucket', () => { }, }); }); + + test.each([ + [s3.TransitionDefaultMinimumObjectSize.ALL_STORAGE_CLASSES_128_K, 'all_storage_classes_128K'], + [s3.TransitionDefaultMinimumObjectSize.VARIES_BY_STORAGE_CLASS, 'varies_by_storage_class'], + ])('transitionDefaultMinimumObjectSize %s can be specified', (key, value) => { + const stack = new cdk.Stack(); + new s3.Bucket(stack, 'MyBucket', { + transitionDefaultMinimumObjectSize: key, + lifecycleRules: [ + { + expiration: cdk.Duration.days(365), + }, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', { + LifecycleConfiguration: { + TransitionDefaultMinimumObjectSize: value, + Rules: [{ + ExpirationInDays: 365, + }], + }, + }); + }); });