Configure AWS S3 Bucket ACLs

The aws:s3/bucketAclV2:BucketAclV2 resource, part of the Pulumi AWS provider, configures access control lists (ACLs) for S3 buckets, defining who can read or write objects. This guide focuses on three capabilities: canned ACLs for common permission patterns, public access configuration, and explicit grant-based permissions.

ACLs require existing buckets and BucketOwnershipControls configuration. Public access requires disabling S3’s default security protections. The examples are intentionally small. Combine them with your own bucket and security configuration.

Apply a canned ACL for private access

Most buckets start with private access, restricting permissions to the bucket owner. Canned ACLs provide predefined permission sets without requiring explicit grants.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.s3.Bucket("example", {bucket: "my-tf-example-bucket"});
const exampleBucketOwnershipControls = new aws.s3.BucketOwnershipControls("example", {
    bucket: example.id,
    rule: {
        objectOwnership: "BucketOwnerPreferred",
    },
});
const exampleBucketAcl = new aws.s3.BucketAcl("example", {
    bucket: example.id,
    acl: "private",
}, {
    dependsOn: [exampleBucketOwnershipControls],
});
import pulumi
import pulumi_aws as aws

example = aws.s3.Bucket("example", bucket="my-tf-example-bucket")
example_bucket_ownership_controls = aws.s3.BucketOwnershipControls("example",
    bucket=example.id,
    rule={
        "object_ownership": "BucketOwnerPreferred",
    })
example_bucket_acl = aws.s3.BucketAcl("example",
    bucket=example.id,
    acl="private",
    opts = pulumi.ResourceOptions(depends_on=[example_bucket_ownership_controls]))
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 {
		example, err := s3.NewBucket(ctx, "example", &s3.BucketArgs{
			Bucket: pulumi.String("my-tf-example-bucket"),
		})
		if err != nil {
			return err
		}
		exampleBucketOwnershipControls, err := s3.NewBucketOwnershipControls(ctx, "example", &s3.BucketOwnershipControlsArgs{
			Bucket: example.ID(),
			Rule: &s3.BucketOwnershipControlsRuleArgs{
				ObjectOwnership: pulumi.String("BucketOwnerPreferred"),
			},
		})
		if err != nil {
			return err
		}
		_, err = s3.NewBucketAcl(ctx, "example", &s3.BucketAclArgs{
			Bucket: example.ID(),
			Acl:    pulumi.String("private"),
		}, pulumi.DependsOn([]pulumi.Resource{
			exampleBucketOwnershipControls,
		}))
		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.Bucket("example", new()
    {
        BucketName = "my-tf-example-bucket",
    });

    var exampleBucketOwnershipControls = new Aws.S3.BucketOwnershipControls("example", new()
    {
        Bucket = example.Id,
        Rule = new Aws.S3.Inputs.BucketOwnershipControlsRuleArgs
        {
            ObjectOwnership = "BucketOwnerPreferred",
        },
    });

    var exampleBucketAcl = new Aws.S3.BucketAcl("example", new()
    {
        Bucket = example.Id,
        Acl = "private",
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            exampleBucketOwnershipControls,
        },
    });

});
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.BucketOwnershipControls;
import com.pulumi.aws.s3.BucketOwnershipControlsArgs;
import com.pulumi.aws.s3.inputs.BucketOwnershipControlsRuleArgs;
import com.pulumi.aws.s3.BucketAcl;
import com.pulumi.aws.s3.BucketAclArgs;
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 example = new Bucket("example", BucketArgs.builder()
            .bucket("my-tf-example-bucket")
            .build());

        var exampleBucketOwnershipControls = new BucketOwnershipControls("exampleBucketOwnershipControls", BucketOwnershipControlsArgs.builder()
            .bucket(example.id())
            .rule(BucketOwnershipControlsRuleArgs.builder()
                .objectOwnership("BucketOwnerPreferred")
                .build())
            .build());

        var exampleBucketAcl = new BucketAcl("exampleBucketAcl", BucketAclArgs.builder()
            .bucket(example.id())
            .acl("private")
            .build(), CustomResourceOptions.builder()
                .dependsOn(exampleBucketOwnershipControls)
                .build());

    }
}
resources:
  example:
    type: aws:s3:Bucket
    properties:
      bucket: my-tf-example-bucket
  exampleBucketOwnershipControls:
    type: aws:s3:BucketOwnershipControls
    name: example
    properties:
      bucket: ${example.id}
      rule:
        objectOwnership: BucketOwnerPreferred
  exampleBucketAcl:
    type: aws:s3:BucketAcl
    name: example
    properties:
      bucket: ${example.id}
      acl: private
    options:
      dependsOn:
        - ${exampleBucketOwnershipControls}

The acl property accepts predefined values like “private”, “public-read”, or “bucket-owner-full-control”. BucketOwnershipControls must be configured first; the dependsOn ensures proper ordering. The “private” ACL grants full control to the bucket owner and denies access to everyone else.

Enable public read access with security overrides

Static website hosting or public datasets require anonymous read access, which means explicitly disabling S3’s default security protections.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.s3.Bucket("example", {bucket: "my-tf-example-bucket"});
const exampleBucketOwnershipControls = new aws.s3.BucketOwnershipControls("example", {
    bucket: example.id,
    rule: {
        objectOwnership: "BucketOwnerPreferred",
    },
});
const exampleBucketPublicAccessBlock = new aws.s3.BucketPublicAccessBlock("example", {
    bucket: example.id,
    blockPublicAcls: false,
    blockPublicPolicy: false,
    ignorePublicAcls: false,
    restrictPublicBuckets: false,
});
const exampleBucketAcl = new aws.s3.BucketAcl("example", {
    bucket: example.id,
    acl: "public-read",
}, {
    dependsOn: [
        exampleBucketOwnershipControls,
        exampleBucketPublicAccessBlock,
    ],
});
import pulumi
import pulumi_aws as aws

example = aws.s3.Bucket("example", bucket="my-tf-example-bucket")
example_bucket_ownership_controls = aws.s3.BucketOwnershipControls("example",
    bucket=example.id,
    rule={
        "object_ownership": "BucketOwnerPreferred",
    })
example_bucket_public_access_block = aws.s3.BucketPublicAccessBlock("example",
    bucket=example.id,
    block_public_acls=False,
    block_public_policy=False,
    ignore_public_acls=False,
    restrict_public_buckets=False)
example_bucket_acl = aws.s3.BucketAcl("example",
    bucket=example.id,
    acl="public-read",
    opts = pulumi.ResourceOptions(depends_on=[
            example_bucket_ownership_controls,
            example_bucket_public_access_block,
        ]))
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 {
		example, err := s3.NewBucket(ctx, "example", &s3.BucketArgs{
			Bucket: pulumi.String("my-tf-example-bucket"),
		})
		if err != nil {
			return err
		}
		exampleBucketOwnershipControls, err := s3.NewBucketOwnershipControls(ctx, "example", &s3.BucketOwnershipControlsArgs{
			Bucket: example.ID(),
			Rule: &s3.BucketOwnershipControlsRuleArgs{
				ObjectOwnership: pulumi.String("BucketOwnerPreferred"),
			},
		})
		if err != nil {
			return err
		}
		exampleBucketPublicAccessBlock, err := s3.NewBucketPublicAccessBlock(ctx, "example", &s3.BucketPublicAccessBlockArgs{
			Bucket:                example.ID(),
			BlockPublicAcls:       pulumi.Bool(false),
			BlockPublicPolicy:     pulumi.Bool(false),
			IgnorePublicAcls:      pulumi.Bool(false),
			RestrictPublicBuckets: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		_, err = s3.NewBucketAcl(ctx, "example", &s3.BucketAclArgs{
			Bucket: example.ID(),
			Acl:    pulumi.String("public-read"),
		}, pulumi.DependsOn([]pulumi.Resource{
			exampleBucketOwnershipControls,
			exampleBucketPublicAccessBlock,
		}))
		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.Bucket("example", new()
    {
        BucketName = "my-tf-example-bucket",
    });

    var exampleBucketOwnershipControls = new Aws.S3.BucketOwnershipControls("example", new()
    {
        Bucket = example.Id,
        Rule = new Aws.S3.Inputs.BucketOwnershipControlsRuleArgs
        {
            ObjectOwnership = "BucketOwnerPreferred",
        },
    });

    var exampleBucketPublicAccessBlock = new Aws.S3.BucketPublicAccessBlock("example", new()
    {
        Bucket = example.Id,
        BlockPublicAcls = false,
        BlockPublicPolicy = false,
        IgnorePublicAcls = false,
        RestrictPublicBuckets = false,
    });

    var exampleBucketAcl = new Aws.S3.BucketAcl("example", new()
    {
        Bucket = example.Id,
        Acl = "public-read",
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            exampleBucketOwnershipControls,
            exampleBucketPublicAccessBlock,
        },
    });

});
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.BucketOwnershipControls;
import com.pulumi.aws.s3.BucketOwnershipControlsArgs;
import com.pulumi.aws.s3.inputs.BucketOwnershipControlsRuleArgs;
import com.pulumi.aws.s3.BucketPublicAccessBlock;
import com.pulumi.aws.s3.BucketPublicAccessBlockArgs;
import com.pulumi.aws.s3.BucketAcl;
import com.pulumi.aws.s3.BucketAclArgs;
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 example = new Bucket("example", BucketArgs.builder()
            .bucket("my-tf-example-bucket")
            .build());

        var exampleBucketOwnershipControls = new BucketOwnershipControls("exampleBucketOwnershipControls", BucketOwnershipControlsArgs.builder()
            .bucket(example.id())
            .rule(BucketOwnershipControlsRuleArgs.builder()
                .objectOwnership("BucketOwnerPreferred")
                .build())
            .build());

        var exampleBucketPublicAccessBlock = new BucketPublicAccessBlock("exampleBucketPublicAccessBlock", BucketPublicAccessBlockArgs.builder()
            .bucket(example.id())
            .blockPublicAcls(false)
            .blockPublicPolicy(false)
            .ignorePublicAcls(false)
            .restrictPublicBuckets(false)
            .build());

        var exampleBucketAcl = new BucketAcl("exampleBucketAcl", BucketAclArgs.builder()
            .bucket(example.id())
            .acl("public-read")
            .build(), CustomResourceOptions.builder()
                .dependsOn(                
                    exampleBucketOwnershipControls,
                    exampleBucketPublicAccessBlock)
                .build());

    }
}
resources:
  example:
    type: aws:s3:Bucket
    properties:
      bucket: my-tf-example-bucket
  exampleBucketOwnershipControls:
    type: aws:s3:BucketOwnershipControls
    name: example
    properties:
      bucket: ${example.id}
      rule:
        objectOwnership: BucketOwnerPreferred
  exampleBucketPublicAccessBlock:
    type: aws:s3:BucketPublicAccessBlock
    name: example
    properties:
      bucket: ${example.id}
      blockPublicAcls: false
      blockPublicPolicy: false
      ignorePublicAcls: false
      restrictPublicBuckets: false
  exampleBucketAcl:
    type: aws:s3:BucketAcl
    name: example
    properties:
      bucket: ${example.id}
      acl: public-read
    options:
      dependsOn:
        - ${exampleBucketOwnershipControls}
        - ${exampleBucketPublicAccessBlock}

BucketPublicAccessBlock controls four security settings that normally prevent public access. Setting all four to false allows the “public-read” ACL to take effect. The dependsOn ensures both ownership controls and public access settings are configured before applying the ACL. This configuration exposes all bucket objects publicly.

Define granular permissions with explicit grants

When canned ACLs don’t provide the right permission mix, explicit grants specify exactly which principals get which permissions.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const current = aws.s3.getCanonicalUserId({});
const example = new aws.s3.Bucket("example", {bucket: "my-tf-example-bucket"});
const exampleBucketOwnershipControls = new aws.s3.BucketOwnershipControls("example", {
    bucket: example.id,
    rule: {
        objectOwnership: "BucketOwnerPreferred",
    },
});
const exampleBucketAcl = new aws.s3.BucketAcl("example", {
    bucket: example.id,
    accessControlPolicy: {
        grants: [
            {
                grantee: {
                    id: current.then(current => current.id),
                    type: "CanonicalUser",
                },
                permission: "READ",
            },
            {
                grantee: {
                    type: "Group",
                    uri: "http://acs.amazonaws.com/groups/s3/LogDelivery",
                },
                permission: "READ_ACP",
            },
        ],
        owner: {
            id: current.then(current => current.id),
        },
    },
}, {
    dependsOn: [exampleBucketOwnershipControls],
});
import pulumi
import pulumi_aws as aws

current = aws.s3.get_canonical_user_id()
example = aws.s3.Bucket("example", bucket="my-tf-example-bucket")
example_bucket_ownership_controls = aws.s3.BucketOwnershipControls("example",
    bucket=example.id,
    rule={
        "object_ownership": "BucketOwnerPreferred",
    })
example_bucket_acl = aws.s3.BucketAcl("example",
    bucket=example.id,
    access_control_policy={
        "grants": [
            {
                "grantee": {
                    "id": current.id,
                    "type": "CanonicalUser",
                },
                "permission": "READ",
            },
            {
                "grantee": {
                    "type": "Group",
                    "uri": "http://acs.amazonaws.com/groups/s3/LogDelivery",
                },
                "permission": "READ_ACP",
            },
        ],
        "owner": {
            "id": current.id,
        },
    },
    opts = pulumi.ResourceOptions(depends_on=[example_bucket_ownership_controls]))
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 {
		current, err := s3.GetCanonicalUserId(ctx, map[string]interface{}{}, nil)
		if err != nil {
			return err
		}
		example, err := s3.NewBucket(ctx, "example", &s3.BucketArgs{
			Bucket: pulumi.String("my-tf-example-bucket"),
		})
		if err != nil {
			return err
		}
		exampleBucketOwnershipControls, err := s3.NewBucketOwnershipControls(ctx, "example", &s3.BucketOwnershipControlsArgs{
			Bucket: example.ID(),
			Rule: &s3.BucketOwnershipControlsRuleArgs{
				ObjectOwnership: pulumi.String("BucketOwnerPreferred"),
			},
		})
		if err != nil {
			return err
		}
		_, err = s3.NewBucketAcl(ctx, "example", &s3.BucketAclArgs{
			Bucket: example.ID(),
			AccessControlPolicy: &s3.BucketAclAccessControlPolicyArgs{
				Grants: s3.BucketAclAccessControlPolicyGrantArray{
					&s3.BucketAclAccessControlPolicyGrantArgs{
						Grantee: &s3.BucketAclAccessControlPolicyGrantGranteeArgs{
							Id:   pulumi.String(current.Id),
							Type: pulumi.String("CanonicalUser"),
						},
						Permission: pulumi.String("READ"),
					},
					&s3.BucketAclAccessControlPolicyGrantArgs{
						Grantee: &s3.BucketAclAccessControlPolicyGrantGranteeArgs{
							Type: pulumi.String("Group"),
							Uri:  pulumi.String("http://acs.amazonaws.com/groups/s3/LogDelivery"),
						},
						Permission: pulumi.String("READ_ACP"),
					},
				},
				Owner: &s3.BucketAclAccessControlPolicyOwnerArgs{
					Id: pulumi.String(current.Id),
				},
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			exampleBucketOwnershipControls,
		}))
		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 current = Aws.S3.GetCanonicalUserId.Invoke();

    var example = new Aws.S3.Bucket("example", new()
    {
        BucketName = "my-tf-example-bucket",
    });

    var exampleBucketOwnershipControls = new Aws.S3.BucketOwnershipControls("example", new()
    {
        Bucket = example.Id,
        Rule = new Aws.S3.Inputs.BucketOwnershipControlsRuleArgs
        {
            ObjectOwnership = "BucketOwnerPreferred",
        },
    });

    var exampleBucketAcl = new Aws.S3.BucketAcl("example", new()
    {
        Bucket = example.Id,
        AccessControlPolicy = new Aws.S3.Inputs.BucketAclAccessControlPolicyArgs
        {
            Grants = new[]
            {
                new Aws.S3.Inputs.BucketAclAccessControlPolicyGrantArgs
                {
                    Grantee = new Aws.S3.Inputs.BucketAclAccessControlPolicyGrantGranteeArgs
                    {
                        Id = current.Apply(getCanonicalUserIdResult => getCanonicalUserIdResult.Id),
                        Type = "CanonicalUser",
                    },
                    Permission = "READ",
                },
                new Aws.S3.Inputs.BucketAclAccessControlPolicyGrantArgs
                {
                    Grantee = new Aws.S3.Inputs.BucketAclAccessControlPolicyGrantGranteeArgs
                    {
                        Type = "Group",
                        Uri = "http://acs.amazonaws.com/groups/s3/LogDelivery",
                    },
                    Permission = "READ_ACP",
                },
            },
            Owner = new Aws.S3.Inputs.BucketAclAccessControlPolicyOwnerArgs
            {
                Id = current.Apply(getCanonicalUserIdResult => getCanonicalUserIdResult.Id),
            },
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            exampleBucketOwnershipControls,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.S3Functions;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.s3.BucketOwnershipControls;
import com.pulumi.aws.s3.BucketOwnershipControlsArgs;
import com.pulumi.aws.s3.inputs.BucketOwnershipControlsRuleArgs;
import com.pulumi.aws.s3.BucketAcl;
import com.pulumi.aws.s3.BucketAclArgs;
import com.pulumi.aws.s3.inputs.BucketAclAccessControlPolicyArgs;
import com.pulumi.aws.s3.inputs.BucketAclAccessControlPolicyOwnerArgs;
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) {
        final var current = S3Functions.getCanonicalUserId(%!v(PANIC=Format method: runtime error: invalid memory address or nil pointer dereference);

        var example = new Bucket("example", BucketArgs.builder()
            .bucket("my-tf-example-bucket")
            .build());

        var exampleBucketOwnershipControls = new BucketOwnershipControls("exampleBucketOwnershipControls", BucketOwnershipControlsArgs.builder()
            .bucket(example.id())
            .rule(BucketOwnershipControlsRuleArgs.builder()
                .objectOwnership("BucketOwnerPreferred")
                .build())
            .build());

        var exampleBucketAcl = new BucketAcl("exampleBucketAcl", BucketAclArgs.builder()
            .bucket(example.id())
            .accessControlPolicy(BucketAclAccessControlPolicyArgs.builder()
                .grants(                
                    BucketAclAccessControlPolicyGrantArgs.builder()
                        .grantee(BucketAclAccessControlPolicyGrantGranteeArgs.builder()
                            .id(current.id())
                            .type("CanonicalUser")
                            .build())
                        .permission("READ")
                        .build(),
                    BucketAclAccessControlPolicyGrantArgs.builder()
                        .grantee(BucketAclAccessControlPolicyGrantGranteeArgs.builder()
                            .type("Group")
                            .uri("http://acs.amazonaws.com/groups/s3/LogDelivery")
                            .build())
                        .permission("READ_ACP")
                        .build())
                .owner(BucketAclAccessControlPolicyOwnerArgs.builder()
                    .id(current.id())
                    .build())
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(exampleBucketOwnershipControls)
                .build());

    }
}
resources:
  example:
    type: aws:s3:Bucket
    properties:
      bucket: my-tf-example-bucket
  exampleBucketOwnershipControls:
    type: aws:s3:BucketOwnershipControls
    name: example
    properties:
      bucket: ${example.id}
      rule:
        objectOwnership: BucketOwnerPreferred
  exampleBucketAcl:
    type: aws:s3:BucketAcl
    name: example
    properties:
      bucket: ${example.id}
      accessControlPolicy:
        grants:
          - grantee:
              id: ${current.id}
              type: CanonicalUser
            permission: READ
          - grantee:
              type: Group
              uri: http://acs.amazonaws.com/groups/s3/LogDelivery
            permission: READ_ACP
        owner:
          id: ${current.id}
    options:
      dependsOn:
        - ${exampleBucketOwnershipControls}
variables:
  current:
    fn::invoke:
      function: aws:s3:getCanonicalUserId
      arguments: {}

The accessControlPolicy property replaces the acl property when you need fine-grained control. Each grant specifies a grantee (user or group) and permission (READ, WRITE, READ_ACP, WRITE_ACP, FULL_CONTROL). The owner field identifies the bucket owner using their canonical user ID. The getCanonicalUserId function retrieves the current account’s ID.

Beyond these examples

These snippets focus on specific ACL-level features: canned ACLs for common permission patterns, public access configuration, and explicit grant-based permissions. They’re intentionally minimal rather than full access control solutions.

The examples require pre-existing infrastructure such as S3 buckets (created separately) and BucketOwnershipControls configuration. They focus on configuring ACLs rather than provisioning buckets or managing lifecycle.

To keep things focused, common ACL patterns are omitted, including:

  • Cross-account access (expectedBucketOwner)
  • ACL removal and state management
  • Directory bucket compatibility

These omissions are intentional: the goal is to illustrate how each ACL feature is wired, not provide drop-in access control modules. See the S3 BucketAclV2 resource reference for all available configuration options.

Let's configure AWS S3 Bucket ACLs

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Resource Lifecycle & Limitations
What happens when I destroy this resource?
Destroying the resource removes it from Pulumi state but does not delete the ACL configuration from the S3 bucket. The ACL persists on the bucket after destruction.
Can I use this resource with S3 directory buckets?
No, this resource cannot be used with S3 directory buckets. Use standard S3 buckets only.
Can I change the bucket after creating the ACL?
No, the bucket property is immutable and cannot be changed after creation.
ACL Configuration
What's the difference between acl and accessControlPolicy?
Use acl for canned (predefined) ACLs like private or public-read. Use accessControlPolicy for custom grants with specific grantees and permissions. You typically use one or the other, not both.
What are the valid canned ACL values?
Valid values are: private, public-read, public-read-write, aws-exec-read, authenticated-read, bucket-owner-read, bucket-owner-full-control, and log-delivery-write.
How do I make my bucket publicly readable?
Set acl to public-read and configure BucketPublicAccessBlock with all blocks disabled (blockPublicAcls, blockPublicPolicy, ignorePublicAcls, restrictPublicBuckets all set to false). This disables default S3 security settings and makes all bucket objects publicly exposed, so use with caution.
Dependencies & Prerequisites
Why do I need to depend on BucketOwnershipControls?
All examples show dependsOn: BucketOwnershipControls to ensure bucket ownership settings (like objectOwnership: "BucketOwnerPreferred") are configured before applying ACLs.
Import
How do I import an existing bucket ACL?

Import syntax depends on your scenario:

  • Same account, no canned ACL: bucket-name
  • Same account, with canned ACL: bucket-name,private
  • Different account, no canned ACL: bucket-name,123456789012
  • Different account, with canned ACL: bucket-name,123456789012,private

Note: expectedBucketOwner is deprecated and will be removed in a future version.

Using a different cloud?

Explore storage guides for other cloud providers: