The aws:s3/bucketLifecycleConfigurationV2:BucketLifecycleConfigurationV2 resource, part of the Pulumi AWS provider, defines lifecycle rules that automatically transition objects to cheaper storage classes or delete them after specified periods. This guide focuses on three capabilities: filtering objects by prefix, tags, and size; configuring transitions and expiration; and managing versioned object lifecycles.
Lifecycle configurations attach to existing S3 buckets. Rules for noncurrent versions require BucketVersioning to be enabled first. The examples are intentionally small. Combine them with your own bucket resources and retention policies.
Apply rules to objects by prefix
Most lifecycle policies target specific paths where predictable data accumulates, like log files or temporary uploads.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.s3.BucketLifecycleConfiguration("example", {
bucket: bucket.id,
rules: [{
id: "rule-1",
filter: {
prefix: "logs/",
},
status: "Enabled",
}],
});
import pulumi
import pulumi_aws as aws
example = aws.s3.BucketLifecycleConfiguration("example",
bucket=bucket["id"],
rules=[{
"id": "rule-1",
"filter": {
"prefix": "logs/",
},
"status": "Enabled",
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := s3.NewBucketLifecycleConfiguration(ctx, "example", &s3.BucketLifecycleConfigurationArgs{
Bucket: pulumi.Any(bucket.Id),
Rules: s3.BucketLifecycleConfigurationRuleArray{
&s3.BucketLifecycleConfigurationRuleArgs{
Id: pulumi.String("rule-1"),
Filter: &s3.BucketLifecycleConfigurationRuleFilterArgs{
Prefix: pulumi.String("logs/"),
},
Status: pulumi.String("Enabled"),
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.S3.BucketLifecycleConfiguration("example", new()
{
Bucket = bucket.Id,
Rules = new[]
{
new Aws.S3.Inputs.BucketLifecycleConfigurationRuleArgs
{
Id = "rule-1",
Filter = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleFilterArgs
{
Prefix = "logs/",
},
Status = "Enabled",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.BucketLifecycleConfiguration;
import com.pulumi.aws.s3.BucketLifecycleConfigurationArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleFilterArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var example = new BucketLifecycleConfiguration("example", BucketLifecycleConfigurationArgs.builder()
.bucket(bucket.id())
.rules(BucketLifecycleConfigurationRuleArgs.builder()
.id("rule-1")
.filter(BucketLifecycleConfigurationRuleFilterArgs.builder()
.prefix("logs/")
.build())
.status("Enabled")
.build())
.build());
}
}
resources:
example:
type: aws:s3:BucketLifecycleConfiguration
properties:
bucket: ${bucket.id}
rules:
- id: rule-1
filter:
prefix: logs/
status: Enabled
The filter property narrows which objects the rule affects. Setting prefix to “logs/” means only objects whose keys start with that path match this rule. The id property names the rule for tracking, and status must be “Enabled” for the rule to take effect.
Target objects by tag key and value
Applications that tag objects with metadata can apply lifecycle rules based on those tags instead of path structure.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.s3.BucketLifecycleConfiguration("example", {
bucket: bucket.id,
rules: [{
id: "rule-1",
filter: {
tag: {
key: "Name",
value: "Staging",
},
},
status: "Enabled",
}],
});
import pulumi
import pulumi_aws as aws
example = aws.s3.BucketLifecycleConfiguration("example",
bucket=bucket["id"],
rules=[{
"id": "rule-1",
"filter": {
"tag": {
"key": "Name",
"value": "Staging",
},
},
"status": "Enabled",
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := s3.NewBucketLifecycleConfiguration(ctx, "example", &s3.BucketLifecycleConfigurationArgs{
Bucket: pulumi.Any(bucket.Id),
Rules: s3.BucketLifecycleConfigurationRuleArray{
&s3.BucketLifecycleConfigurationRuleArgs{
Id: pulumi.String("rule-1"),
Filter: &s3.BucketLifecycleConfigurationRuleFilterArgs{
Tag: &s3.BucketLifecycleConfigurationRuleFilterTagArgs{
Key: pulumi.String("Name"),
Value: pulumi.String("Staging"),
},
},
Status: pulumi.String("Enabled"),
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.S3.BucketLifecycleConfiguration("example", new()
{
Bucket = bucket.Id,
Rules = new[]
{
new Aws.S3.Inputs.BucketLifecycleConfigurationRuleArgs
{
Id = "rule-1",
Filter = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleFilterArgs
{
Tag = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleFilterTagArgs
{
Key = "Name",
Value = "Staging",
},
},
Status = "Enabled",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.BucketLifecycleConfiguration;
import com.pulumi.aws.s3.BucketLifecycleConfigurationArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleFilterArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleFilterTagArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var example = new BucketLifecycleConfiguration("example", BucketLifecycleConfigurationArgs.builder()
.bucket(bucket.id())
.rules(BucketLifecycleConfigurationRuleArgs.builder()
.id("rule-1")
.filter(BucketLifecycleConfigurationRuleFilterArgs.builder()
.tag(BucketLifecycleConfigurationRuleFilterTagArgs.builder()
.key("Name")
.value("Staging")
.build())
.build())
.status("Enabled")
.build())
.build());
}
}
resources:
example:
type: aws:s3:BucketLifecycleConfiguration
properties:
bucket: ${bucket.id}
rules:
- id: rule-1
filter:
tag:
key: Name
value: Staging
status: Enabled
The tag property inside filter specifies both a key and value that objects must have. This rule applies only to objects tagged with Name=Staging, regardless of their location in the bucket.
Combine prefix and tag filters with AND logic
When objects must match multiple criteria, the and block requires both a prefix and one or more tags.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.s3.BucketLifecycleConfiguration("example", {
bucket: bucket.id,
rules: [{
id: "rule-1",
filter: {
and: {
prefix: "logs/",
tags: {
Key1: "Value1",
Key2: "Value2",
},
},
},
status: "Enabled",
}],
});
import pulumi
import pulumi_aws as aws
example = aws.s3.BucketLifecycleConfiguration("example",
bucket=bucket["id"],
rules=[{
"id": "rule-1",
"filter": {
"and_": {
"prefix": "logs/",
"tags": {
"Key1": "Value1",
"Key2": "Value2",
},
},
},
"status": "Enabled",
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := s3.NewBucketLifecycleConfiguration(ctx, "example", &s3.BucketLifecycleConfigurationArgs{
Bucket: pulumi.Any(bucket.Id),
Rules: s3.BucketLifecycleConfigurationRuleArray{
&s3.BucketLifecycleConfigurationRuleArgs{
Id: pulumi.String("rule-1"),
Filter: &s3.BucketLifecycleConfigurationRuleFilterArgs{
And: &s3.BucketLifecycleConfigurationRuleFilterAndArgs{
Prefix: pulumi.String("logs/"),
Tags: pulumi.StringMap{
"Key1": pulumi.String("Value1"),
"Key2": pulumi.String("Value2"),
},
},
},
Status: pulumi.String("Enabled"),
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.S3.BucketLifecycleConfiguration("example", new()
{
Bucket = bucket.Id,
Rules = new[]
{
new Aws.S3.Inputs.BucketLifecycleConfigurationRuleArgs
{
Id = "rule-1",
Filter = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleFilterArgs
{
And = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleFilterAndArgs
{
Prefix = "logs/",
Tags =
{
{ "Key1", "Value1" },
{ "Key2", "Value2" },
},
},
},
Status = "Enabled",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.BucketLifecycleConfiguration;
import com.pulumi.aws.s3.BucketLifecycleConfigurationArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleFilterArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleFilterAndArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var example = new BucketLifecycleConfiguration("example", BucketLifecycleConfigurationArgs.builder()
.bucket(bucket.id())
.rules(BucketLifecycleConfigurationRuleArgs.builder()
.id("rule-1")
.filter(BucketLifecycleConfigurationRuleFilterArgs.builder()
.and(BucketLifecycleConfigurationRuleFilterAndArgs.builder()
.prefix("logs/")
.tags(Map.ofEntries(
Map.entry("Key1", "Value1"),
Map.entry("Key2", "Value2")
))
.build())
.build())
.status("Enabled")
.build())
.build());
}
}
resources:
example:
type: aws:s3:BucketLifecycleConfiguration
properties:
bucket: ${bucket.id}
rules:
- id: rule-1
filter:
and:
prefix: logs/
tags:
Key1: Value1
Key2: Value2
status: Enabled
The and block wraps both prefix and tags, creating a compound filter. Objects must be under “logs/” AND have both Key1=Value1 and Key2=Value2 to match. This extends basic filtering by combining location and metadata requirements.
Override default size thresholds for transitions
S3 prevents objects smaller than 128 KB from transitioning by default. You can override this with a minimum size filter.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.s3.BucketLifecycleConfiguration("example", {
bucket: bucket.id,
rules: [{
id: "Allow small object transitions",
filter: {
objectSizeGreaterThan: 1,
},
status: "Enabled",
transitions: [{
days: 365,
storageClass: "GLACIER_IR",
}],
}],
});
import pulumi
import pulumi_aws as aws
example = aws.s3.BucketLifecycleConfiguration("example",
bucket=bucket["id"],
rules=[{
"id": "Allow small object transitions",
"filter": {
"object_size_greater_than": 1,
},
"status": "Enabled",
"transitions": [{
"days": 365,
"storage_class": "GLACIER_IR",
}],
}])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := s3.NewBucketLifecycleConfiguration(ctx, "example", &s3.BucketLifecycleConfigurationArgs{
Bucket: pulumi.Any(bucket.Id),
Rules: s3.BucketLifecycleConfigurationRuleArray{
&s3.BucketLifecycleConfigurationRuleArgs{
Id: pulumi.String("Allow small object transitions"),
Filter: &s3.BucketLifecycleConfigurationRuleFilterArgs{
ObjectSizeGreaterThan: pulumi.Int(1),
},
Status: pulumi.String("Enabled"),
Transitions: s3.BucketLifecycleConfigurationRuleTransitionArray{
&s3.BucketLifecycleConfigurationRuleTransitionArgs{
Days: pulumi.Int(365),
StorageClass: pulumi.String("GLACIER_IR"),
},
},
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.S3.BucketLifecycleConfiguration("example", new()
{
Bucket = bucket.Id,
Rules = new[]
{
new Aws.S3.Inputs.BucketLifecycleConfigurationRuleArgs
{
Id = "Allow small object transitions",
Filter = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleFilterArgs
{
ObjectSizeGreaterThan = 1,
},
Status = "Enabled",
Transitions = new[]
{
new Aws.S3.Inputs.BucketLifecycleConfigurationRuleTransitionArgs
{
Days = 365,
StorageClass = "GLACIER_IR",
},
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.BucketLifecycleConfiguration;
import com.pulumi.aws.s3.BucketLifecycleConfigurationArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleFilterArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var example = new BucketLifecycleConfiguration("example", BucketLifecycleConfigurationArgs.builder()
.bucket(bucket.id())
.rules(BucketLifecycleConfigurationRuleArgs.builder()
.id("Allow small object transitions")
.filter(BucketLifecycleConfigurationRuleFilterArgs.builder()
.objectSizeGreaterThan(1)
.build())
.status("Enabled")
.transitions(BucketLifecycleConfigurationRuleTransitionArgs.builder()
.days(365)
.storageClass("GLACIER_IR")
.build())
.build())
.build());
}
}
resources:
example:
type: aws:s3:BucketLifecycleConfiguration
properties:
bucket: ${bucket.id}
rules:
- id: Allow small object transitions
filter:
objectSizeGreaterThan: 1
status: Enabled
transitions:
- days: 365
storageClass: GLACIER_IR
Setting objectSizeGreaterThan to 1 byte allows even tiny objects to transition. The transitions array defines when objects move to cheaper storage classes. Here, objects transition to GLACIER_IR after 365 days, regardless of size.
Manage current and noncurrent object versions
Versioned buckets accumulate old versions over time. Lifecycle rules can manage noncurrent versions separately from current ones.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const bucket = new aws.s3.Bucket("bucket", {bucket: "my-bucket"});
const bucketAcl = new aws.s3.BucketAcl("bucket_acl", {
bucket: bucket.id,
acl: "private",
});
const bucket_config = new aws.s3.BucketLifecycleConfiguration("bucket-config", {
bucket: bucket.id,
rules: [
{
id: "log",
expiration: {
days: 90,
},
filter: {
and: {
prefix: "log/",
tags: {
rule: "log",
autoclean: "true",
},
},
},
status: "Enabled",
transitions: [
{
days: 30,
storageClass: "STANDARD_IA",
},
{
days: 60,
storageClass: "GLACIER",
},
],
},
{
id: "tmp",
filter: {
prefix: "tmp/",
},
expiration: {
date: "2023-01-13T00:00:00Z",
},
status: "Enabled",
},
],
});
const versioningBucket = new aws.s3.Bucket("versioning_bucket", {bucket: "my-versioning-bucket"});
const versioningBucketAcl = new aws.s3.BucketAcl("versioning_bucket_acl", {
bucket: versioningBucket.id,
acl: "private",
});
const versioning = new aws.s3.BucketVersioning("versioning", {
bucket: versioningBucket.id,
versioningConfiguration: {
status: "Enabled",
},
});
const versioning_bucket_config = new aws.s3.BucketLifecycleConfiguration("versioning-bucket-config", {
bucket: versioningBucket.id,
rules: [{
id: "config",
filter: {
prefix: "config/",
},
noncurrentVersionExpiration: {
noncurrentDays: 90,
},
noncurrentVersionTransitions: [
{
noncurrentDays: 30,
storageClass: "STANDARD_IA",
},
{
noncurrentDays: 60,
storageClass: "GLACIER",
},
],
status: "Enabled",
}],
}, {
dependsOn: [versioning],
});
import pulumi
import pulumi_aws as aws
bucket = aws.s3.Bucket("bucket", bucket="my-bucket")
bucket_acl = aws.s3.BucketAcl("bucket_acl",
bucket=bucket.id,
acl="private")
bucket_config = aws.s3.BucketLifecycleConfiguration("bucket-config",
bucket=bucket.id,
rules=[
{
"id": "log",
"expiration": {
"days": 90,
},
"filter": {
"and_": {
"prefix": "log/",
"tags": {
"rule": "log",
"autoclean": "true",
},
},
},
"status": "Enabled",
"transitions": [
{
"days": 30,
"storage_class": "STANDARD_IA",
},
{
"days": 60,
"storage_class": "GLACIER",
},
],
},
{
"id": "tmp",
"filter": {
"prefix": "tmp/",
},
"expiration": {
"date": "2023-01-13T00:00:00Z",
},
"status": "Enabled",
},
])
versioning_bucket = aws.s3.Bucket("versioning_bucket", bucket="my-versioning-bucket")
versioning_bucket_acl = aws.s3.BucketAcl("versioning_bucket_acl",
bucket=versioning_bucket.id,
acl="private")
versioning = aws.s3.BucketVersioning("versioning",
bucket=versioning_bucket.id,
versioning_configuration={
"status": "Enabled",
})
versioning_bucket_config = aws.s3.BucketLifecycleConfiguration("versioning-bucket-config",
bucket=versioning_bucket.id,
rules=[{
"id": "config",
"filter": {
"prefix": "config/",
},
"noncurrent_version_expiration": {
"noncurrent_days": 90,
},
"noncurrent_version_transitions": [
{
"noncurrent_days": 30,
"storage_class": "STANDARD_IA",
},
{
"noncurrent_days": 60,
"storage_class": "GLACIER",
},
],
"status": "Enabled",
}],
opts = pulumi.ResourceOptions(depends_on=[versioning]))
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
bucket, err := s3.NewBucket(ctx, "bucket", &s3.BucketArgs{
Bucket: pulumi.String("my-bucket"),
})
if err != nil {
return err
}
_, err = s3.NewBucketAcl(ctx, "bucket_acl", &s3.BucketAclArgs{
Bucket: bucket.ID(),
Acl: pulumi.String("private"),
})
if err != nil {
return err
}
_, err = s3.NewBucketLifecycleConfiguration(ctx, "bucket-config", &s3.BucketLifecycleConfigurationArgs{
Bucket: bucket.ID(),
Rules: s3.BucketLifecycleConfigurationRuleArray{
&s3.BucketLifecycleConfigurationRuleArgs{
Id: pulumi.String("log"),
Expiration: &s3.BucketLifecycleConfigurationRuleExpirationArgs{
Days: pulumi.Int(90),
},
Filter: &s3.BucketLifecycleConfigurationRuleFilterArgs{
And: &s3.BucketLifecycleConfigurationRuleFilterAndArgs{
Prefix: pulumi.String("log/"),
Tags: pulumi.StringMap{
"rule": pulumi.String("log"),
"autoclean": pulumi.String("true"),
},
},
},
Status: pulumi.String("Enabled"),
Transitions: s3.BucketLifecycleConfigurationRuleTransitionArray{
&s3.BucketLifecycleConfigurationRuleTransitionArgs{
Days: pulumi.Int(30),
StorageClass: pulumi.String("STANDARD_IA"),
},
&s3.BucketLifecycleConfigurationRuleTransitionArgs{
Days: pulumi.Int(60),
StorageClass: pulumi.String("GLACIER"),
},
},
},
&s3.BucketLifecycleConfigurationRuleArgs{
Id: pulumi.String("tmp"),
Filter: &s3.BucketLifecycleConfigurationRuleFilterArgs{
Prefix: pulumi.String("tmp/"),
},
Expiration: &s3.BucketLifecycleConfigurationRuleExpirationArgs{
Date: pulumi.String("2023-01-13T00:00:00Z"),
},
Status: pulumi.String("Enabled"),
},
},
})
if err != nil {
return err
}
versioningBucket, err := s3.NewBucket(ctx, "versioning_bucket", &s3.BucketArgs{
Bucket: pulumi.String("my-versioning-bucket"),
})
if err != nil {
return err
}
_, err = s3.NewBucketAcl(ctx, "versioning_bucket_acl", &s3.BucketAclArgs{
Bucket: versioningBucket.ID(),
Acl: pulumi.String("private"),
})
if err != nil {
return err
}
versioning, err := s3.NewBucketVersioning(ctx, "versioning", &s3.BucketVersioningArgs{
Bucket: versioningBucket.ID(),
VersioningConfiguration: &s3.BucketVersioningVersioningConfigurationArgs{
Status: pulumi.String("Enabled"),
},
})
if err != nil {
return err
}
_, err = s3.NewBucketLifecycleConfiguration(ctx, "versioning-bucket-config", &s3.BucketLifecycleConfigurationArgs{
Bucket: versioningBucket.ID(),
Rules: s3.BucketLifecycleConfigurationRuleArray{
&s3.BucketLifecycleConfigurationRuleArgs{
Id: pulumi.String("config"),
Filter: &s3.BucketLifecycleConfigurationRuleFilterArgs{
Prefix: pulumi.String("config/"),
},
NoncurrentVersionExpiration: &s3.BucketLifecycleConfigurationRuleNoncurrentVersionExpirationArgs{
NoncurrentDays: pulumi.Int(90),
},
NoncurrentVersionTransitions: s3.BucketLifecycleConfigurationRuleNoncurrentVersionTransitionArray{
&s3.BucketLifecycleConfigurationRuleNoncurrentVersionTransitionArgs{
NoncurrentDays: pulumi.Int(30),
StorageClass: pulumi.String("STANDARD_IA"),
},
&s3.BucketLifecycleConfigurationRuleNoncurrentVersionTransitionArgs{
NoncurrentDays: pulumi.Int(60),
StorageClass: pulumi.String("GLACIER"),
},
},
Status: pulumi.String("Enabled"),
},
},
}, pulumi.DependsOn([]pulumi.Resource{
versioning,
}))
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var bucket = new Aws.S3.Bucket("bucket", new()
{
BucketName = "my-bucket",
});
var bucketAcl = new Aws.S3.BucketAcl("bucket_acl", new()
{
Bucket = bucket.Id,
Acl = "private",
});
var bucket_config = new Aws.S3.BucketLifecycleConfiguration("bucket-config", new()
{
Bucket = bucket.Id,
Rules = new[]
{
new Aws.S3.Inputs.BucketLifecycleConfigurationRuleArgs
{
Id = "log",
Expiration = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleExpirationArgs
{
Days = 90,
},
Filter = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleFilterArgs
{
And = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleFilterAndArgs
{
Prefix = "log/",
Tags =
{
{ "rule", "log" },
{ "autoclean", "true" },
},
},
},
Status = "Enabled",
Transitions = new[]
{
new Aws.S3.Inputs.BucketLifecycleConfigurationRuleTransitionArgs
{
Days = 30,
StorageClass = "STANDARD_IA",
},
new Aws.S3.Inputs.BucketLifecycleConfigurationRuleTransitionArgs
{
Days = 60,
StorageClass = "GLACIER",
},
},
},
new Aws.S3.Inputs.BucketLifecycleConfigurationRuleArgs
{
Id = "tmp",
Filter = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleFilterArgs
{
Prefix = "tmp/",
},
Expiration = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleExpirationArgs
{
Date = "2023-01-13T00:00:00Z",
},
Status = "Enabled",
},
},
});
var versioningBucket = new Aws.S3.Bucket("versioning_bucket", new()
{
BucketName = "my-versioning-bucket",
});
var versioningBucketAcl = new Aws.S3.BucketAcl("versioning_bucket_acl", new()
{
Bucket = versioningBucket.Id,
Acl = "private",
});
var versioning = new Aws.S3.BucketVersioning("versioning", new()
{
Bucket = versioningBucket.Id,
VersioningConfiguration = new Aws.S3.Inputs.BucketVersioningVersioningConfigurationArgs
{
Status = "Enabled",
},
});
var versioning_bucket_config = new Aws.S3.BucketLifecycleConfiguration("versioning-bucket-config", new()
{
Bucket = versioningBucket.Id,
Rules = new[]
{
new Aws.S3.Inputs.BucketLifecycleConfigurationRuleArgs
{
Id = "config",
Filter = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleFilterArgs
{
Prefix = "config/",
},
NoncurrentVersionExpiration = new Aws.S3.Inputs.BucketLifecycleConfigurationRuleNoncurrentVersionExpirationArgs
{
NoncurrentDays = 90,
},
NoncurrentVersionTransitions = new[]
{
new Aws.S3.Inputs.BucketLifecycleConfigurationRuleNoncurrentVersionTransitionArgs
{
NoncurrentDays = 30,
StorageClass = "STANDARD_IA",
},
new Aws.S3.Inputs.BucketLifecycleConfigurationRuleNoncurrentVersionTransitionArgs
{
NoncurrentDays = 60,
StorageClass = "GLACIER",
},
},
Status = "Enabled",
},
},
}, new CustomResourceOptions
{
DependsOn =
{
versioning,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.s3.BucketAcl;
import com.pulumi.aws.s3.BucketAclArgs;
import com.pulumi.aws.s3.BucketLifecycleConfiguration;
import com.pulumi.aws.s3.BucketLifecycleConfigurationArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleExpirationArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleFilterArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleFilterAndArgs;
import com.pulumi.aws.s3.BucketVersioning;
import com.pulumi.aws.s3.BucketVersioningArgs;
import com.pulumi.aws.s3.inputs.BucketVersioningVersioningConfigurationArgs;
import com.pulumi.aws.s3.inputs.BucketLifecycleConfigurationRuleNoncurrentVersionExpirationArgs;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var bucket = new Bucket("bucket", BucketArgs.builder()
.bucket("my-bucket")
.build());
var bucketAcl = new BucketAcl("bucketAcl", BucketAclArgs.builder()
.bucket(bucket.id())
.acl("private")
.build());
var bucket_config = new BucketLifecycleConfiguration("bucket-config", BucketLifecycleConfigurationArgs.builder()
.bucket(bucket.id())
.rules(
BucketLifecycleConfigurationRuleArgs.builder()
.id("log")
.expiration(BucketLifecycleConfigurationRuleExpirationArgs.builder()
.days(90)
.build())
.filter(BucketLifecycleConfigurationRuleFilterArgs.builder()
.and(BucketLifecycleConfigurationRuleFilterAndArgs.builder()
.prefix("log/")
.tags(Map.ofEntries(
Map.entry("rule", "log"),
Map.entry("autoclean", "true")
))
.build())
.build())
.status("Enabled")
.transitions(
BucketLifecycleConfigurationRuleTransitionArgs.builder()
.days(30)
.storageClass("STANDARD_IA")
.build(),
BucketLifecycleConfigurationRuleTransitionArgs.builder()
.days(60)
.storageClass("GLACIER")
.build())
.build(),
BucketLifecycleConfigurationRuleArgs.builder()
.id("tmp")
.filter(BucketLifecycleConfigurationRuleFilterArgs.builder()
.prefix("tmp/")
.build())
.expiration(BucketLifecycleConfigurationRuleExpirationArgs.builder()
.date("2023-01-13T00:00:00Z")
.build())
.status("Enabled")
.build())
.build());
var versioningBucket = new Bucket("versioningBucket", BucketArgs.builder()
.bucket("my-versioning-bucket")
.build());
var versioningBucketAcl = new BucketAcl("versioningBucketAcl", BucketAclArgs.builder()
.bucket(versioningBucket.id())
.acl("private")
.build());
var versioning = new BucketVersioning("versioning", BucketVersioningArgs.builder()
.bucket(versioningBucket.id())
.versioningConfiguration(BucketVersioningVersioningConfigurationArgs.builder()
.status("Enabled")
.build())
.build());
var versioning_bucket_config = new BucketLifecycleConfiguration("versioning-bucket-config", BucketLifecycleConfigurationArgs.builder()
.bucket(versioningBucket.id())
.rules(BucketLifecycleConfigurationRuleArgs.builder()
.id("config")
.filter(BucketLifecycleConfigurationRuleFilterArgs.builder()
.prefix("config/")
.build())
.noncurrentVersionExpiration(BucketLifecycleConfigurationRuleNoncurrentVersionExpirationArgs.builder()
.noncurrentDays(90)
.build())
.noncurrentVersionTransitions(
BucketLifecycleConfigurationRuleNoncurrentVersionTransitionArgs.builder()
.noncurrentDays(30)
.storageClass("STANDARD_IA")
.build(),
BucketLifecycleConfigurationRuleNoncurrentVersionTransitionArgs.builder()
.noncurrentDays(60)
.storageClass("GLACIER")
.build())
.status("Enabled")
.build())
.build(), CustomResourceOptions.builder()
.dependsOn(versioning)
.build());
}
}
resources:
bucket:
type: aws:s3:Bucket
properties:
bucket: my-bucket
bucketAcl:
type: aws:s3:BucketAcl
name: bucket_acl
properties:
bucket: ${bucket.id}
acl: private
bucket-config:
type: aws:s3:BucketLifecycleConfiguration
properties:
bucket: ${bucket.id}
rules:
- id: log
expiration:
days: 90
filter:
and:
prefix: log/
tags:
rule: log
autoclean: 'true'
status: Enabled
transitions:
- days: 30
storageClass: STANDARD_IA
- days: 60
storageClass: GLACIER
- id: tmp
filter:
prefix: tmp/
expiration:
date: 2023-01-13T00:00:00Z
status: Enabled
versioningBucket:
type: aws:s3:Bucket
name: versioning_bucket
properties:
bucket: my-versioning-bucket
versioningBucketAcl:
type: aws:s3:BucketAcl
name: versioning_bucket_acl
properties:
bucket: ${versioningBucket.id}
acl: private
versioning:
type: aws:s3:BucketVersioning
properties:
bucket: ${versioningBucket.id}
versioningConfiguration:
status: Enabled
versioning-bucket-config:
type: aws:s3:BucketLifecycleConfiguration
properties:
bucket: ${versioningBucket.id}
rules:
- id: config
filter:
prefix: config/
noncurrentVersionExpiration:
noncurrentDays: 90
noncurrentVersionTransitions:
- noncurrentDays: 30
storageClass: STANDARD_IA
- noncurrentDays: 60
storageClass: GLACIER
status: Enabled
options:
dependsOn:
- ${versioning}
The noncurrentVersionExpiration property deletes old versions after a specified number of days. The noncurrentVersionTransitions array moves old versions through storage classes before deletion. Current versions use expiration and transitions properties instead. The dependsOn ensures versioning is enabled before applying lifecycle rules.
Beyond these examples
These snippets focus on specific lifecycle rule features: prefix, tag, and size-based filtering; storage class transitions and expiration; and versioned object lifecycle management. They’re intentionally minimal rather than full data retention policies.
The examples reference pre-existing infrastructure such as S3 buckets and BucketVersioning resources for noncurrent version examples. They focus on configuring lifecycle rules rather than provisioning buckets.
To keep things focused, common lifecycle patterns are omitted, including:
- Expiration dates vs day counts (date property)
- Incomplete multipart upload cleanup (abortIncompleteMultipartUpload)
- Multiple rules with different filters in one configuration
- Object size range filters (objectSizeLessThan)
These omissions are intentional: the goal is to illustrate how each lifecycle feature is wired, not provide drop-in retention modules. See the S3 Bucket Lifecycle Configuration resource reference for all available configuration options.
Let's configure AWS S3 Bucket Lifecycle Rules
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Common Pitfalls & Configuration Issues
BucketLifecycleConfiguration resources for the same bucket, it will cause a perpetual difference in configuration. Use only one resource per bucket and define multiple rules within it.objectSizeGreaterThan set to a small value like 1 byte, or configure transitionDefaultMinimumObjectSize to varies_by_storage_class.Rule Filtering & Targeting
filter property entirely, or set filter to an empty object {}. Both approaches have the same effect and apply the rule to all objects in the bucket.filter.prefix to target objects with a specific key prefix (e.g., logs/). If you need to apply different lifecycle actions to multiple prefixes, create separate rules with different filter.prefix values.filter.tag with key and value properties. For multiple tags, wrap them in filter.and.tags as a map of key-value pairs.prefix and tags in a filter.and configuration block. This applies the rule only to objects matching both the prefix and all specified tags.Versioning & Advanced Filtering
noncurrentVersionExpiration and noncurrentVersionTransitions properties to manage noncurrent object versions. Add dependsOn to ensure the bucket versioning configuration is created before the lifecycle configuration.objectSizeGreaterThan or objectSizeLessThan in the filter, with values in bytes (maximum 5TB). When using both to create a size range, objectSizeGreaterThan must be less than objectSizeLessThan. For multiple size-based criteria, wrap them in a filter.and block.Import & Cross-Account Access
bucket-name,account-id when importing. For buckets in the same account as your provider configuration, you can import using just the bucket name.