The aws:s3/bucketAclV2:BucketAclV2 resource, part of the Pulumi AWS provider, configures access control lists (ACLs) for S3 buckets, defining who can read, write, or manage bucket permissions. This guide focuses on three capabilities: canned ACLs for common permission patterns, public access configuration, and explicit grant-based permissions.
ACLs require an existing bucket and BucketOwnershipControls resource. The resource manages ACL configuration but does not delete ACLs on destroy (only removes from state). The examples are intentionally small. Combine them with your own bucket and ownership control configuration.
Apply a canned ACL for private access
Most buckets start with private access, restricting read and write permissions to the bucket owner. Canned ACLs provide predefined permission sets without requiring explicit grant configuration.
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”. The dependsOn ensures BucketOwnershipControls is created first, as S3 requires ownership controls before applying ACLs. The objectOwnership setting of “BucketOwnerPreferred” allows the bucket owner to automatically own uploaded objects.
Enable public read access with security overrides
Some use cases require public read access for static website hosting or content distribution. This configuration explicitly disables S3’s default security settings to allow public access.
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}
The BucketPublicAccessBlock resource disables all four public access protections (blockPublicAcls, blockPublicPolicy, ignorePublicAcls, restrictPublicBuckets). The acl property then applies “public-read”, making all bucket objects publicly readable. The dependsOn array ensures both ownership controls and public access settings are configured before the ACL.
Define granular permissions with explicit grants
When canned ACLs don’t provide the right permission mix, explicit grants let you specify exactly which principals can read, write, or manage ACLs.
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 simple acl string with a grants array. Each grant specifies a grantee (by canonical user ID or predefined group URI) and a permission level. The getCanonicalUserId function retrieves your AWS account’s canonical user ID for the owner field. This example grants READ permission to the current user and READ_ACP (read ACL permissions) to the S3 log delivery group.
Beyond these examples
These snippets focus on specific ACL configuration features: canned ACLs for common permission patterns, public access configuration, and explicit grant-based permissions. They’re intentionally minimal rather than full bucket access control solutions.
The examples reference pre-existing infrastructure such as S3 buckets (created separately) and BucketOwnershipControls (required dependency). They focus on ACL configuration rather than bucket provisioning.
To keep things focused, common ACL patterns are omitted, including:
- Cross-account bucket access (expectedBucketOwner)
- ACL removal and state management (destroy behavior)
- Directory bucket compatibility (not supported)
- Import scenarios for existing ACLs
These omissions are intentional: the goal is to illustrate how ACL configuration 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 FREEFrequently Asked Questions
Resource Lifecycle & Limitations
bucket and expectedBucketOwner are immutable and cannot be changed after the resource is created.Security & Public Access
public-read or similar public ACLs disables default S3 bucket security settings and makes all bucket objects publicly accessible. This should be done with caution and only when absolutely necessary.BucketOwnershipControls (with objectOwnership set to BucketOwnerPreferred) and BucketPublicAccessBlock (with all four block settings set to false).Configuration & Dependencies
BucketOwnershipControls to be set up before applying ACLs. Use dependsOn to ensure proper ordering, as shown in all examples.acl for canned (predefined) ACLs like private or public-read. Use accessControlPolicy for custom grants with specific grantees and permissions. You can use one or the other, but not both.Cross-Account & Import
expectedBucketOwner property to the account ID of the bucket owner. For import, use the format bucket-name,account-id or bucket-name,account-id,acl if a canned ACL is configured.CanonicalUser (with an id) or Group (with a uri like http://acs.amazonaws.com/groups/s3/LogDelivery), as shown in the grants example.