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 uploads read a local file and track changes through an MD5 hash, ensuring Pulumi re-uploads when content changes.
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 an MD5 hash of the file content. When the file changes, the hash changes, triggering Pulumi to upload the new version. The bucket and key properties specify where the object lives in S3.
Encrypt objects with a customer-managed KMS key
Applications with strict encryption requirements use customer-managed KMS keys to control rotation, access policies, and audit trails.
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 the KMS key’s ARN. S3 encrypts the object at rest using this key. When you reference an aws.kms.Key resource, use its arn attribute. This gives you full control over key policies, rotation schedules, and CloudTrail logging.
Encrypt objects with AWS-managed AES256
For simpler encryption needs, S3 provides AWS-managed keys that handle rotation automatically.
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 all key management operations. This provides encryption at rest without requiring KMS resources or additional IAM permissions.
Apply retention policies with Object Lock
Compliance workloads require immutable storage where objects cannot be deleted or modified for a specified 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 requires bucket versioning and objectLockEnabled set at bucket creation time. The objectLockMode property sets the retention mode (GOVERNANCE or COMPLIANCE). The objectLockRetainUntilDate specifies when the lock expires. The objectLockLegalHoldStatus property adds an additional hold that persists until explicitly removed. Set forceDestroy to true only if you need to delete objects with active locks during stack teardown.
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 retention and legal holds. They’re intentionally minimal rather than full storage solutions.
The examples assume pre-existing infrastructure such as S3 buckets (which must exist before uploading objects) and KMS keys for customer-managed encryption. They focus on configuring the object rather than provisioning buckets or access policies.
To keep things focused, common object patterns are omitted, including:
- Content type and encoding (contentType, contentEncoding)
- Storage class selection (storageClass)
- Cache control and metadata (cacheControl, metadata)
- ACLs and access control (acl property)
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 & Management
You have three options:
- File upload - Use
sourcewith a FileAsset pointing to your file path - Literal string - Use
contentfor UTF-8 text - Binary data - Use
contentBase64for base64-encoded data (recommended only for small content)
If none are provided, the object will be empty.
contentBase64 is recommended 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 for better performance.contentEncoding, you’re responsible for encoding the body appropriately. The source, content, and contentBase64 properties all expect already encoded/compressed bytes.Encryption & Security
You have two server-side encryption options:
- AWS-managed key - Set
serverSideEncryption = "AES256" - KMS-managed key - Set
serverSideEncryption = "aws:kms"and optionally specifykmsKeyIdwith your KMS key ARN
If the S3 bucket has server-side encryption enabled, that value will automatically be used.
sourceHash when using KMS encryption. The etag attribute is not compatible with KMS encryption (kmsKeyId or serverSideEncryption = "aws:kms"). For non-KMS scenarios, either works.aws.kms.Key resource, use the arn attribute. When referencing aws.kms.Alias data source or resource, use the targetKeyArn attribute.Object Lock & Deletion
aws.s3.BucketVersioning, then configure objectLockLegalHoldStatus, objectLockMode, and objectLockRetainUntilDate. Use dependsOn to ensure versioning is enabled before creating the object.forceDestroy = true to allow deletion by removing any legal hold on object versions. This should only be set to true if the bucket has S3 object lock enabled.Configuration & Immutability
bucket and key properties are immutable. Changing either will force replacement of the object.metadata map are lowercase.