Upload Files to AWS S3 Buckets

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 FREE

Frequently Asked Questions

Content Upload & Management
What are the ways to provide object content?

You have three options:

  1. File upload - Use source with a FileAsset pointing to your file path
  2. Literal string - Use content for UTF-8 text
  3. Binary data - Use contentBase64 for base64-encoded data (recommended only for small content)

If none are provided, the object will be empty.

Why is contentBase64 not recommended for large objects?
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.
What do I need to know about contentEncoding?
If you specify contentEncoding, you’re responsible for encoding the body appropriately. The source, content, and contentBase64 properties all expect already encoded/compressed bytes.
Encryption & Security
What are my encryption options for S3 objects?

You have two server-side encryption options:

  1. AWS-managed key - Set serverSideEncryption = "AES256"
  2. KMS-managed key - Set serverSideEncryption = "aws:kms" and optionally specify kmsKeyId with your KMS key ARN

If the S3 bucket has server-side encryption enabled, that value will automatically be used.

Should I use etag or sourceHash to trigger updates?
Use 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.
Which KMS key attribute should I reference?
When referencing aws.kms.Key resource, use the arn attribute. When referencing aws.kms.Alias data source or resource, use the targetKeyArn attribute.
Object Lock & Deletion
How do I set up S3 Object Lock?
Enable object lock on the bucket, enable versioning with aws.s3.BucketVersioning, then configure objectLockLegalHoldStatus, objectLockMode, and objectLockRetainUntilDate. Use dependsOn to ensure versioning is enabled before creating the object.
Why can't I delete my object with object lock enabled?
Set 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
What properties can't I change after creation?
The bucket and key properties are immutable. Changing either will force replacement of the object.
Why are my metadata keys being rejected?
Only lowercase labels are currently supported by the AWS Go API for metadata keys. Ensure all keys in your metadata map are lowercase.

Using a different cloud?

Explore storage guides for other cloud providers: