The aws:s3/bucketObject:BucketObject resource, part of the Pulumi AWS provider, uploads and manages individual objects within S3 buckets: their content, encryption, and retention policies. This guide focuses on three capabilities: file upload with change detection, encryption options, and Object Lock for compliance.
Objects require an existing S3 bucket and may reference KMS keys for encryption. The examples are intentionally small. Combine them with your own bucket configuration, access policies, and lifecycle rules.
Upload a file with content change detection
Most deployments start by uploading a local file and tracking when it needs re-uploading.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as std from "@pulumi/std";
const object = new aws.s3.BucketObject("object", {
bucket: "your_bucket_name",
key: "new_object_key",
source: new pulumi.asset.FileAsset("path/to/file"),
etag: std.filemd5({
input: "path/to/file",
}).then(invoke => invoke.result),
});
import pulumi
import pulumi_aws as aws
import pulumi_std as std
object = aws.s3.BucketObject("object",
bucket="your_bucket_name",
key="new_object_key",
source=pulumi.FileAsset("path/to/file"),
etag=std.filemd5(input="path/to/file").result)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
"github.com/pulumi/pulumi-std/sdk/go/std"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
invokeFilemd5, err := std.Filemd5(ctx, &std.Filemd5Args{
Input: "path/to/file",
}, nil)
if err != nil {
return err
}
_, err = s3.NewBucketObject(ctx, "object", &s3.BucketObjectArgs{
Bucket: pulumi.Any("your_bucket_name"),
Key: pulumi.String("new_object_key"),
Source: pulumi.NewFileAsset("path/to/file"),
Etag: pulumi.String(invokeFilemd5.Result),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
using Std = Pulumi.Std;
return await Deployment.RunAsync(() =>
{
var @object = new Aws.S3.BucketObject("object", new()
{
Bucket = "your_bucket_name",
Key = "new_object_key",
Source = new FileAsset("path/to/file"),
Etag = Std.Filemd5.Invoke(new()
{
Input = "path/to/file",
}).Apply(invoke => invoke.Result),
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.BucketObject;
import com.pulumi.aws.s3.BucketObjectArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.Filemd5Args;
import com.pulumi.asset.FileAsset;
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 object = new BucketObject("object", BucketObjectArgs.builder()
.bucket("your_bucket_name")
.key("new_object_key")
.source(new FileAsset("path/to/file"))
.etag(StdFunctions.filemd5(Filemd5Args.builder()
.input("path/to/file")
.build()).result())
.build());
}
}
resources:
object:
type: aws:s3:BucketObject
properties:
bucket: your_bucket_name
key: new_object_key
source:
fn::FileAsset: path/to/file
etag:
fn::invoke:
function: std:filemd5
arguments:
input: path/to/file
return: result
The source property points to your local file path. The etag property uses filemd5 to compute a hash of the file content; when the file changes, Pulumi detects the new hash and re-uploads the object. The key property sets the object’s name in the bucket.
Encrypt objects with a customer-managed KMS key
Applications with strict encryption requirements use customer-managed KMS keys to control rotation and access.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const examplekms = new aws.kms.Key("examplekms", {
description: "KMS key 1",
deletionWindowInDays: 7,
});
const examplebucket = new aws.s3.Bucket("examplebucket", {bucket: "examplebuckettftest"});
const example = new aws.s3.BucketAcl("example", {
bucket: examplebucket.id,
acl: "private",
});
const exampleBucketObject = new aws.s3.BucketObject("example", {
key: "someobject",
bucket: examplebucket.id,
source: new pulumi.asset.FileAsset("index.html"),
kmsKeyId: examplekms.arn,
});
import pulumi
import pulumi_aws as aws
examplekms = aws.kms.Key("examplekms",
description="KMS key 1",
deletion_window_in_days=7)
examplebucket = aws.s3.Bucket("examplebucket", bucket="examplebuckettftest")
example = aws.s3.BucketAcl("example",
bucket=examplebucket.id,
acl="private")
example_bucket_object = aws.s3.BucketObject("example",
key="someobject",
bucket=examplebucket.id,
source=pulumi.FileAsset("index.html"),
kms_key_id=examplekms.arn)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/kms"
"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 {
examplekms, err := kms.NewKey(ctx, "examplekms", &kms.KeyArgs{
Description: pulumi.String("KMS key 1"),
DeletionWindowInDays: pulumi.Int(7),
})
if err != nil {
return err
}
examplebucket, err := s3.NewBucket(ctx, "examplebucket", &s3.BucketArgs{
Bucket: pulumi.String("examplebuckettftest"),
})
if err != nil {
return err
}
_, err = s3.NewBucketAcl(ctx, "example", &s3.BucketAclArgs{
Bucket: examplebucket.ID(),
Acl: pulumi.String("private"),
})
if err != nil {
return err
}
_, err = s3.NewBucketObject(ctx, "example", &s3.BucketObjectArgs{
Key: pulumi.String("someobject"),
Bucket: examplebucket.ID(),
Source: pulumi.NewFileAsset("index.html"),
KmsKeyId: examplekms.Arn,
})
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 examplekms = new Aws.Kms.Key("examplekms", new()
{
Description = "KMS key 1",
DeletionWindowInDays = 7,
});
var examplebucket = new Aws.S3.Bucket("examplebucket", new()
{
BucketName = "examplebuckettftest",
});
var example = new Aws.S3.BucketAcl("example", new()
{
Bucket = examplebucket.Id,
Acl = "private",
});
var exampleBucketObject = new Aws.S3.BucketObject("example", new()
{
Key = "someobject",
Bucket = examplebucket.Id,
Source = new FileAsset("index.html"),
KmsKeyId = examplekms.Arn,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.kms.Key;
import com.pulumi.aws.kms.KeyArgs;
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.BucketObject;
import com.pulumi.aws.s3.BucketObjectArgs;
import com.pulumi.asset.FileAsset;
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 examplekms = new Key("examplekms", KeyArgs.builder()
.description("KMS key 1")
.deletionWindowInDays(7)
.build());
var examplebucket = new Bucket("examplebucket", BucketArgs.builder()
.bucket("examplebuckettftest")
.build());
var example = new BucketAcl("example", BucketAclArgs.builder()
.bucket(examplebucket.id())
.acl("private")
.build());
var exampleBucketObject = new BucketObject("exampleBucketObject", BucketObjectArgs.builder()
.key("someobject")
.bucket(examplebucket.id())
.source(new FileAsset("index.html"))
.kmsKeyId(examplekms.arn())
.build());
}
}
resources:
examplekms:
type: aws:kms:Key
properties:
description: KMS key 1
deletionWindowInDays: 7
examplebucket:
type: aws:s3:Bucket
properties:
bucket: examplebuckettftest
example:
type: aws:s3:BucketAcl
properties:
bucket: ${examplebucket.id}
acl: private
exampleBucketObject:
type: aws:s3:BucketObject
name: example
properties:
key: someobject
bucket: ${examplebucket.id}
source:
fn::FileAsset: index.html
kmsKeyId: ${examplekms.arn}
The kmsKeyId property references a KMS key ARN for encryption. When you upload the object, S3 encrypts it using the specified key. This gives you control over key policies, rotation schedules, and audit trails through CloudTrail.
Encrypt objects with AWS-managed encryption
For simpler encryption needs, S3 provides AWS-managed keys that require no provisioning.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const examplebucket = new aws.s3.Bucket("examplebucket", {bucket: "examplebuckettftest"});
const example = new aws.s3.BucketAcl("example", {
bucket: examplebucket.id,
acl: "private",
});
const exampleBucketObject = new aws.s3.BucketObject("example", {
key: "someobject",
bucket: examplebucket.id,
source: new pulumi.asset.FileAsset("index.html"),
serverSideEncryption: "AES256",
});
import pulumi
import pulumi_aws as aws
examplebucket = aws.s3.Bucket("examplebucket", bucket="examplebuckettftest")
example = aws.s3.BucketAcl("example",
bucket=examplebucket.id,
acl="private")
example_bucket_object = aws.s3.BucketObject("example",
key="someobject",
bucket=examplebucket.id,
source=pulumi.FileAsset("index.html"),
server_side_encryption="AES256")
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 {
examplebucket, err := s3.NewBucket(ctx, "examplebucket", &s3.BucketArgs{
Bucket: pulumi.String("examplebuckettftest"),
})
if err != nil {
return err
}
_, err = s3.NewBucketAcl(ctx, "example", &s3.BucketAclArgs{
Bucket: examplebucket.ID(),
Acl: pulumi.String("private"),
})
if err != nil {
return err
}
_, err = s3.NewBucketObject(ctx, "example", &s3.BucketObjectArgs{
Key: pulumi.String("someobject"),
Bucket: examplebucket.ID(),
Source: pulumi.NewFileAsset("index.html"),
ServerSideEncryption: pulumi.String("AES256"),
})
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 examplebucket = new Aws.S3.Bucket("examplebucket", new()
{
BucketName = "examplebuckettftest",
});
var example = new Aws.S3.BucketAcl("example", new()
{
Bucket = examplebucket.Id,
Acl = "private",
});
var exampleBucketObject = new Aws.S3.BucketObject("example", new()
{
Key = "someobject",
Bucket = examplebucket.Id,
Source = new FileAsset("index.html"),
ServerSideEncryption = "AES256",
});
});
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.BucketObject;
import com.pulumi.aws.s3.BucketObjectArgs;
import com.pulumi.asset.FileAsset;
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 examplebucket = new Bucket("examplebucket", BucketArgs.builder()
.bucket("examplebuckettftest")
.build());
var example = new BucketAcl("example", BucketAclArgs.builder()
.bucket(examplebucket.id())
.acl("private")
.build());
var exampleBucketObject = new BucketObject("exampleBucketObject", BucketObjectArgs.builder()
.key("someobject")
.bucket(examplebucket.id())
.source(new FileAsset("index.html"))
.serverSideEncryption("AES256")
.build());
}
}
resources:
examplebucket:
type: aws:s3:Bucket
properties:
bucket: examplebuckettftest
example:
type: aws:s3:BucketAcl
properties:
bucket: ${examplebucket.id}
acl: private
exampleBucketObject:
type: aws:s3:BucketObject
name: example
properties:
key: someobject
bucket: ${examplebucket.id}
source:
fn::FileAsset: index.html
serverSideEncryption: AES256
The serverSideEncryption property set to “AES256” enables encryption with AWS-managed keys. S3 handles key management automatically; you don’t provision or rotate keys yourself. This provides encryption at rest without the overhead of managing KMS resources.
Apply retention policies with Object Lock
Compliance workloads require immutable storage where objects cannot be deleted or modified for a period.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const examplebucket = new aws.s3.Bucket("examplebucket", {
bucket: "examplebuckettftest",
objectLockEnabled: true,
});
const example = new aws.s3.BucketAcl("example", {
bucket: examplebucket.id,
acl: "private",
});
const exampleBucketVersioning = new aws.s3.BucketVersioning("example", {
bucket: examplebucket.id,
versioningConfiguration: {
status: "Enabled",
},
});
const exampleBucketObject = new aws.s3.BucketObject("example", {
key: "someobject",
bucket: examplebucket.id,
source: new pulumi.asset.FileAsset("important.txt"),
objectLockLegalHoldStatus: "ON",
objectLockMode: "GOVERNANCE",
objectLockRetainUntilDate: "2021-12-31T23:59:60Z",
forceDestroy: true,
}, {
dependsOn: [exampleBucketVersioning],
});
import pulumi
import pulumi_aws as aws
examplebucket = aws.s3.Bucket("examplebucket",
bucket="examplebuckettftest",
object_lock_enabled=True)
example = aws.s3.BucketAcl("example",
bucket=examplebucket.id,
acl="private")
example_bucket_versioning = aws.s3.BucketVersioning("example",
bucket=examplebucket.id,
versioning_configuration={
"status": "Enabled",
})
example_bucket_object = aws.s3.BucketObject("example",
key="someobject",
bucket=examplebucket.id,
source=pulumi.FileAsset("important.txt"),
object_lock_legal_hold_status="ON",
object_lock_mode="GOVERNANCE",
object_lock_retain_until_date="2021-12-31T23:59:60Z",
force_destroy=True,
opts = pulumi.ResourceOptions(depends_on=[example_bucket_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 {
examplebucket, err := s3.NewBucket(ctx, "examplebucket", &s3.BucketArgs{
Bucket: pulumi.String("examplebuckettftest"),
ObjectLockEnabled: pulumi.Bool(true),
})
if err != nil {
return err
}
_, err = s3.NewBucketAcl(ctx, "example", &s3.BucketAclArgs{
Bucket: examplebucket.ID(),
Acl: pulumi.String("private"),
})
if err != nil {
return err
}
exampleBucketVersioning, err := s3.NewBucketVersioning(ctx, "example", &s3.BucketVersioningArgs{
Bucket: examplebucket.ID(),
VersioningConfiguration: &s3.BucketVersioningVersioningConfigurationArgs{
Status: pulumi.String("Enabled"),
},
})
if err != nil {
return err
}
_, err = s3.NewBucketObject(ctx, "example", &s3.BucketObjectArgs{
Key: pulumi.String("someobject"),
Bucket: examplebucket.ID(),
Source: pulumi.NewFileAsset("important.txt"),
ObjectLockLegalHoldStatus: pulumi.String("ON"),
ObjectLockMode: pulumi.String("GOVERNANCE"),
ObjectLockRetainUntilDate: pulumi.String("2021-12-31T23:59:60Z"),
ForceDestroy: pulumi.Bool(true),
}, pulumi.DependsOn([]pulumi.Resource{
exampleBucketVersioning,
}))
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 examplebucket = new Aws.S3.Bucket("examplebucket", new()
{
BucketName = "examplebuckettftest",
ObjectLockEnabled = true,
});
var example = new Aws.S3.BucketAcl("example", new()
{
Bucket = examplebucket.Id,
Acl = "private",
});
var exampleBucketVersioning = new Aws.S3.BucketVersioning("example", new()
{
Bucket = examplebucket.Id,
VersioningConfiguration = new Aws.S3.Inputs.BucketVersioningVersioningConfigurationArgs
{
Status = "Enabled",
},
});
var exampleBucketObject = new Aws.S3.BucketObject("example", new()
{
Key = "someobject",
Bucket = examplebucket.Id,
Source = new FileAsset("important.txt"),
ObjectLockLegalHoldStatus = "ON",
ObjectLockMode = "GOVERNANCE",
ObjectLockRetainUntilDate = "2021-12-31T23:59:60Z",
ForceDestroy = true,
}, new CustomResourceOptions
{
DependsOn =
{
exampleBucketVersioning,
},
});
});
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.BucketVersioning;
import com.pulumi.aws.s3.BucketVersioningArgs;
import com.pulumi.aws.s3.inputs.BucketVersioningVersioningConfigurationArgs;
import com.pulumi.aws.s3.BucketObject;
import com.pulumi.aws.s3.BucketObjectArgs;
import com.pulumi.asset.FileAsset;
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 examplebucket = new Bucket("examplebucket", BucketArgs.builder()
.bucket("examplebuckettftest")
.objectLockEnabled(true)
.build());
var example = new BucketAcl("example", BucketAclArgs.builder()
.bucket(examplebucket.id())
.acl("private")
.build());
var exampleBucketVersioning = new BucketVersioning("exampleBucketVersioning", BucketVersioningArgs.builder()
.bucket(examplebucket.id())
.versioningConfiguration(BucketVersioningVersioningConfigurationArgs.builder()
.status("Enabled")
.build())
.build());
var exampleBucketObject = new BucketObject("exampleBucketObject", BucketObjectArgs.builder()
.key("someobject")
.bucket(examplebucket.id())
.source(new FileAsset("important.txt"))
.objectLockLegalHoldStatus("ON")
.objectLockMode("GOVERNANCE")
.objectLockRetainUntilDate("2021-12-31T23:59:60Z")
.forceDestroy(true)
.build(), CustomResourceOptions.builder()
.dependsOn(exampleBucketVersioning)
.build());
}
}
resources:
examplebucket:
type: aws:s3:Bucket
properties:
bucket: examplebuckettftest
objectLockEnabled: true
example:
type: aws:s3:BucketAcl
properties:
bucket: ${examplebucket.id}
acl: private
exampleBucketVersioning:
type: aws:s3:BucketVersioning
name: example
properties:
bucket: ${examplebucket.id}
versioningConfiguration:
status: Enabled
exampleBucketObject:
type: aws:s3:BucketObject
name: example
properties:
key: someobject
bucket: ${examplebucket.id}
source:
fn::FileAsset: important.txt
objectLockLegalHoldStatus: ON
objectLockMode: GOVERNANCE
objectLockRetainUntilDate: 2021-12-31T23:59:60Z
forceDestroy: true
options:
dependsOn:
- ${exampleBucketVersioning}
Object Lock prevents deletion or modification until the retention period expires. The objectLockMode property sets the retention type (GOVERNANCE allows privileged users to override; COMPLIANCE is immutable). The objectLockRetainUntilDate property specifies when the lock expires. The forceDestroy property must be true to allow Pulumi to remove the object during stack deletion. Object Lock requires versioning enabled on the bucket and objectLockEnabled set at bucket creation time.
Beyond these examples
These snippets focus on specific object-level features: file upload with change detection, encryption (KMS and AWS-managed keys), and Object Lock for compliance. They’re intentionally minimal rather than full storage solutions.
The examples reference pre-existing infrastructure such as S3 buckets (which must exist before creating objects) and KMS keys for customer-managed encryption. They focus on object configuration rather than provisioning buckets or access policies.
To keep things focused, common object patterns are omitted, including:
- Storage class selection (STANDARD, GLACIER, etc.)
- Content metadata (contentType, cacheControl, contentDisposition)
- Access control (ACLs and tags)
- Website redirects and content encoding
These omissions are intentional: the goal is to illustrate how each object feature is wired, not provide drop-in storage modules. See the S3 BucketObject resource reference for all available configuration options.
Let's upload Files to AWS S3 Buckets
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Content & Upload
source (path to a local file), content (literal string uploaded as UTF-8), or contentBase64 (base64-encoded binary data). If none are provided, the object will be empty.source property with a FileAsset pointing to your local file path, as shown in the basic upload example.contentBase64 only for small content like the result of gzipbase64 with small text strings. For larger objects, use source to stream content from a disk file.source, content, and contentBase64 properties all expect already encoded/compressed bytes.Encryption & Security
sourceHash instead of etag. The etag attribute is incompatible with KMS encryption (kmsKeyId or serverSideEncryption = "aws:kms").serverSideEncryption with “AES256” (AWS-managed key) or “aws:kms” (S3 default master key). For custom KMS keys, set kmsKeyId to your KMS key ARN.arn attribute when referencing aws.kms.Key resource, or targetKeyArn when referencing aws.kms.Alias.Object Lock & Deletion
objectLockLegalHoldStatus (ON/OFF), objectLockMode (GOVERNANCE/COMPLIANCE), and objectLockRetainUntilDate (RFC3339 format). Use dependsOn to ensure versioning is enabled first.forceDestroy: true only if the bucket has S3 object lock enabled. This allows deletion of objects by removing any legal hold on object versions.Configuration & Immutability
bucket and key properties are immutable. Changing either will force resource replacement.storageClass.