Configure AWS S3 Access Points

The aws:s3/accessPoint:AccessPoint resource, part of the Pulumi AWS provider, creates named network endpoints for S3 buckets that simplify permission management and network access control. This guide focuses on three capabilities: general purpose bucket access points, VPC-restricted access, and directory bucket access points.

Access points attach to existing S3 buckets and can restrict traffic to specific VPCs. The examples are intentionally small. Combine them with your own bucket policies, public access blocks, and network configuration.

Create an access point for a general purpose bucket

Most deployments start with a general purpose bucket in a standard AWS region, using access points to delegate access through named endpoints.

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

const example = new aws.s3.Bucket("example", {bucket: "example"});
const exampleAccessPoint = new aws.s3.AccessPoint("example", {
    bucket: example.id,
    name: "example",
});
import pulumi
import pulumi_aws as aws

example = aws.s3.Bucket("example", bucket="example")
example_access_point = aws.s3.AccessPoint("example",
    bucket=example.id,
    name="example")
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("example"),
		})
		if err != nil {
			return err
		}
		_, err = s3.NewAccessPoint(ctx, "example", &s3.AccessPointArgs{
			Bucket: example.ID(),
			Name:   pulumi.String("example"),
		})
		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 = "example",
    });

    var exampleAccessPoint = new Aws.S3.AccessPoint("example", new()
    {
        Bucket = example.Id,
        Name = "example",
    });

});
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.AccessPoint;
import com.pulumi.aws.s3.AccessPointArgs;
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("example")
            .build());

        var exampleAccessPoint = new AccessPoint("exampleAccessPoint", AccessPointArgs.builder()
            .bucket(example.id())
            .name("example")
            .build());

    }
}
resources:
  example:
    type: aws:s3:Bucket
    properties:
      bucket: example
  exampleAccessPoint:
    type: aws:s3:AccessPoint
    name: example
    properties:
      bucket: ${example.id}
      name: example

The bucket property references an S3 bucket by ID. The name property assigns a unique identifier for the access point. Without additional configuration, the access point inherits the bucket’s network origin (Internet or VPC) and allows access subject to bucket and access point policies.

Restrict access to requests from a VPC

Applications in private subnets often need S3 access without traversing the public internet. VPC-restricted access points keep traffic within your network boundary.

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

const example = new aws.s3control.Bucket("example", {bucket: "example"});
const exampleVpc = new aws.ec2.Vpc("example", {cidrBlock: "10.0.0.0/16"});
const exampleAccessPoint = new aws.s3.AccessPoint("example", {
    bucket: example.arn,
    name: "example",
    vpcConfiguration: {
        vpcId: exampleVpc.id,
    },
});
import pulumi
import pulumi_aws as aws

example = aws.s3control.Bucket("example", bucket="example")
example_vpc = aws.ec2.Vpc("example", cidr_block="10.0.0.0/16")
example_access_point = aws.s3.AccessPoint("example",
    bucket=example.arn,
    name="example",
    vpc_configuration={
        "vpc_id": example_vpc.id,
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3control"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := s3control.NewBucket(ctx, "example", &s3control.BucketArgs{
			Bucket: pulumi.String("example"),
		})
		if err != nil {
			return err
		}
		exampleVpc, err := ec2.NewVpc(ctx, "example", &ec2.VpcArgs{
			CidrBlock: pulumi.String("10.0.0.0/16"),
		})
		if err != nil {
			return err
		}
		_, err = s3.NewAccessPoint(ctx, "example", &s3.AccessPointArgs{
			Bucket: example.Arn,
			Name:   pulumi.String("example"),
			VpcConfiguration: &s3.AccessPointVpcConfigurationArgs{
				VpcId: exampleVpc.ID(),
			},
		})
		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.S3Control.Bucket("example", new()
    {
        BucketName = "example",
    });

    var exampleVpc = new Aws.Ec2.Vpc("example", new()
    {
        CidrBlock = "10.0.0.0/16",
    });

    var exampleAccessPoint = new Aws.S3.AccessPoint("example", new()
    {
        Bucket = example.Arn,
        Name = "example",
        VpcConfiguration = new Aws.S3.Inputs.AccessPointVpcConfigurationArgs
        {
            VpcId = exampleVpc.Id,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3control.Bucket;
import com.pulumi.aws.s3control.BucketArgs;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.s3.AccessPoint;
import com.pulumi.aws.s3.AccessPointArgs;
import com.pulumi.aws.s3.inputs.AccessPointVpcConfigurationArgs;
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("example")
            .build());

        var exampleVpc = new Vpc("exampleVpc", VpcArgs.builder()
            .cidrBlock("10.0.0.0/16")
            .build());

        var exampleAccessPoint = new AccessPoint("exampleAccessPoint", AccessPointArgs.builder()
            .bucket(example.arn())
            .name("example")
            .vpcConfiguration(AccessPointVpcConfigurationArgs.builder()
                .vpcId(exampleVpc.id())
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:s3control:Bucket
    properties:
      bucket: example
  exampleAccessPoint:
    type: aws:s3:AccessPoint
    name: example
    properties:
      bucket: ${example.arn}
      name: example
      vpcConfiguration:
        vpcId: ${exampleVpc.id}
  exampleVpc:
    type: aws:ec2:Vpc
    name: example
    properties:
      cidrBlock: 10.0.0.0/16

The vpcConfiguration block restricts access to requests originating from the specified VPC. The vpcId property identifies which VPC can reach this access point. For S3 on Outposts, VPC configuration is required; the access point uses an s3control.Bucket rather than a standard s3.Bucket.

Create an access point for a directory bucket

Directory buckets provide single-digit millisecond latency for performance-sensitive workloads. Access points for directory buckets follow zone-specific naming conventions.

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

const available = aws.getAvailabilityZones({
    state: "available",
});
const example = new aws.s3.DirectoryBucket("example", {
    bucket: "example--zoneId--x-s3",
    location: {
        name: available.then(available => available.zoneIds?.[0]),
    },
});
const exampleAccessPoint = new aws.s3.AccessPoint("example", {
    bucket: test.bucket,
    name: "example--zoneId--xa-s3",
});
import pulumi
import pulumi_aws as aws

available = aws.get_availability_zones(state="available")
example = aws.s3.DirectoryBucket("example",
    bucket="example--zoneId--x-s3",
    location={
        "name": available.zone_ids[0],
    })
example_access_point = aws.s3.AccessPoint("example",
    bucket=test["bucket"],
    name="example--zoneId--xa-s3")
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws"
	"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 {
		available, err := aws.GetAvailabilityZones(ctx, &aws.GetAvailabilityZonesArgs{
			State: pulumi.StringRef("available"),
		}, nil)
		if err != nil {
			return err
		}
		_, err = s3.NewDirectoryBucket(ctx, "example", &s3.DirectoryBucketArgs{
			Bucket: pulumi.String("example--zoneId--x-s3"),
			Location: &s3.DirectoryBucketLocationArgs{
				Name: pulumi.String(available.ZoneIds[0]),
			},
		})
		if err != nil {
			return err
		}
		_, err = s3.NewAccessPoint(ctx, "example", &s3.AccessPointArgs{
			Bucket: pulumi.Any(test.Bucket),
			Name:   pulumi.String("example--zoneId--xa-s3"),
		})
		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 available = Aws.GetAvailabilityZones.Invoke(new()
    {
        State = "available",
    });

    var example = new Aws.S3.DirectoryBucket("example", new()
    {
        Bucket = "example--zoneId--x-s3",
        Location = new Aws.S3.Inputs.DirectoryBucketLocationArgs
        {
            Name = available.Apply(getAvailabilityZonesResult => getAvailabilityZonesResult.ZoneIds[0]),
        },
    });

    var exampleAccessPoint = new Aws.S3.AccessPoint("example", new()
    {
        Bucket = test.Bucket,
        Name = "example--zoneId--xa-s3",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.AwsFunctions;
import com.pulumi.aws.inputs.GetAvailabilityZonesArgs;
import com.pulumi.aws.s3.DirectoryBucket;
import com.pulumi.aws.s3.DirectoryBucketArgs;
import com.pulumi.aws.s3.inputs.DirectoryBucketLocationArgs;
import com.pulumi.aws.s3.AccessPoint;
import com.pulumi.aws.s3.AccessPointArgs;
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 available = AwsFunctions.getAvailabilityZones(GetAvailabilityZonesArgs.builder()
            .state("available")
            .build());

        var example = new DirectoryBucket("example", DirectoryBucketArgs.builder()
            .bucket("example--zoneId--x-s3")
            .location(DirectoryBucketLocationArgs.builder()
                .name(available.zoneIds()[0])
                .build())
            .build());

        var exampleAccessPoint = new AccessPoint("exampleAccessPoint", AccessPointArgs.builder()
            .bucket(test.bucket())
            .name("example--zoneId--xa-s3")
            .build());

    }
}
resources:
  example:
    type: aws:s3:DirectoryBucket
    properties:
      bucket: example--zoneId--x-s3
      location:
        name: ${available.zoneIds[0]}
  exampleAccessPoint:
    type: aws:s3:AccessPoint
    name: example
    properties:
      bucket: ${test.bucket}
      name: example--zoneId--xa-s3
variables:
  available:
    fn::invoke:
      function: aws:getAvailabilityZones
      arguments:
        state: available

Directory bucket access points require names that include the zone ID suffix (e.g., --zoneId--xa-s3). The bucket property references a DirectoryBucket resource. Directory buckets are tied to specific availability zones for consistent low-latency access.

Beyond these examples

These snippets focus on specific access point features: general purpose and directory bucket access points, and VPC-restricted access. They’re intentionally minimal rather than full access control solutions.

The examples reference pre-existing infrastructure such as S3 buckets (general purpose, Outposts, or directory) and VPCs for network-restricted access points. They focus on creating the access point rather than provisioning buckets or network infrastructure.

To keep things focused, common access point patterns are omitted, including:

  • Access point policies (policy property or aws.s3control.AccessPointPolicy)
  • Public access block configuration (publicAccessBlockConfiguration)
  • Cross-account access and bucket ownership
  • Tags for organization and cost tracking

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

Let's configure AWS S3 Access Points

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Policy Management
Why am I getting policy conflicts with my access point?
You can’t use an inline policy on the AccessPoint resource together with a separate aws.s3control.AccessPointPolicy resource. Choose one approach or the other to avoid conflicts and policy overwrites.
How do I remove an access point policy?
Set policy to "{}" (an empty JSON document). Setting it to null or an empty string won’t delete the policy, since it may have been set by aws.s3control.AccessPointPolicy.
Configuration & Setup
Which endpoint provider should I use for custom API endpoints?
Use the s3control endpoint provider configuration, not the s3 endpoint provider. Using the wrong endpoint will cause API errors.
What properties are immutable after creating an access point?
The following properties can’t be changed after creation: accountId, bucket, bucketAccountId, name, publicAccessBlockConfiguration, and vpcConfiguration.
What's required to create an access point for S3 on Outposts?
You must configure vpcConfiguration with a VPC ID to restrict access to the specified VPC. This is required for S3 on Outposts buckets.
Bucket Types & Compatibility
Can I use access points with S3 directory buckets?
Yes, access points support S3 directory buckets. See the directory bucket example in the schema for configuration details.
Security & Access
Why can't I connect to my access point via HTTP?
S3 access points only support HTTPS connections. HTTP is not supported, so you must use HTTPS URLs.
How do I import an existing access point?
For AWS Partition buckets, use the format accountId:name (e.g., 123456789012:example). For S3 on Outposts, use the full ARN.

Using a different cloud?

Explore storage guides for other cloud providers: