Create AWS CloudFront Origin Access Identities

The aws:cloudfront/originAccessIdentity:OriginAccessIdentity resource, part of the Pulumi AWS provider, creates a CloudFront origin access identity that allows distributions to access private S3 content without making buckets public. This guide focuses on three capabilities: identity creation, distribution integration, and S3 bucket policy configuration.

Origin access identities work with CloudFront distributions and S3 buckets to establish secure access patterns. The examples are intentionally small. Combine them with your own distribution and bucket configuration.

Create an origin access identity

CloudFront distributions serving content from private S3 buckets need an origin access identity to authenticate requests.

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

const example = new aws.cloudfront.OriginAccessIdentity("example", {comment: "Some comment"});
import pulumi
import pulumi_aws as aws

example = aws.cloudfront.OriginAccessIdentity("example", comment="Some comment")
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudfront"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := cloudfront.NewOriginAccessIdentity(ctx, "example", &cloudfront.OriginAccessIdentityArgs{
			Comment: pulumi.String("Some comment"),
		})
		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.CloudFront.OriginAccessIdentity("example", new()
    {
        Comment = "Some comment",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudfront.OriginAccessIdentity;
import com.pulumi.aws.cloudfront.OriginAccessIdentityArgs;
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 OriginAccessIdentity("example", OriginAccessIdentityArgs.builder()
            .comment("Some comment")
            .build());

    }
}
resources:
  example:
    type: aws:cloudfront:OriginAccessIdentity
    properties:
      comment: Some comment

The comment property adds descriptive metadata to help identify the identity’s purpose. CloudFront generates a unique ID and canonical user ID that you’ll reference in distribution and bucket configurations.

Reference the identity in a CloudFront distribution

Once created, the identity must be referenced in the distribution’s S3 origin configuration.

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

const example = new aws.cloudfront.Distribution("example", {origins: [{
    s3OriginConfig: {
        originAccessIdentity: exampleAwsCloudfrontOriginAccessIdentity.cloudfrontAccessIdentityPath,
    },
}]});
import pulumi
import pulumi_aws as aws

example = aws.cloudfront.Distribution("example", origins=[{
    "s3_origin_config": {
        "origin_access_identity": example_aws_cloudfront_origin_access_identity["cloudfrontAccessIdentityPath"],
    },
}])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudfront"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := cloudfront.NewDistribution(ctx, "example", &cloudfront.DistributionArgs{
			Origins: cloudfront.DistributionOriginArray{
				&cloudfront.DistributionOriginArgs{
					S3OriginConfig: &cloudfront.DistributionOriginS3OriginConfigArgs{
						OriginAccessIdentity: pulumi.Any(exampleAwsCloudfrontOriginAccessIdentity.CloudfrontAccessIdentityPath),
					},
				},
			},
		})
		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.CloudFront.Distribution("example", new()
    {
        Origins = new[]
        {
            new Aws.CloudFront.Inputs.DistributionOriginArgs
            {
                S3OriginConfig = new Aws.CloudFront.Inputs.DistributionOriginS3OriginConfigArgs
                {
                    OriginAccessIdentity = exampleAwsCloudfrontOriginAccessIdentity.CloudfrontAccessIdentityPath,
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudfront.Distribution;
import com.pulumi.aws.cloudfront.DistributionArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionOriginArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionOriginS3OriginConfigArgs;
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 Distribution("example", DistributionArgs.builder()
            .origins(DistributionOriginArgs.builder()
                .s3OriginConfig(DistributionOriginS3OriginConfigArgs.builder()
                    .originAccessIdentity(exampleAwsCloudfrontOriginAccessIdentity.cloudfrontAccessIdentityPath())
                    .build())
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:cloudfront:Distribution
    properties:
      origins:
        - s3OriginConfig:
            originAccessIdentity: ${exampleAwsCloudfrontOriginAccessIdentity.cloudfrontAccessIdentityPath}

The cloudfrontAccessIdentityPath property provides the correctly formatted path for CloudFront’s s3OriginConfig. This establishes the trust relationship between the distribution and the identity, allowing CloudFront to present credentials when requesting objects from S3.

Grant S3 bucket access using the identity

The S3 bucket policy must explicitly allow the origin access identity to read objects.

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

const s3Policy = aws.iam.getPolicyDocument({
    statements: [{
        actions: ["s3:GetObject"],
        resources: [`${exampleAwsS3Bucket.arn}/*`],
        principals: [{
            type: "AWS",
            identifiers: [exampleAwsCloudfrontOriginAccessIdentity.iamArn],
        }],
    }],
});
const example = new aws.s3.BucketPolicy("example", {
    bucket: exampleAwsS3Bucket.id,
    policy: s3Policy.then(s3Policy => s3Policy.json),
});
import pulumi
import pulumi_aws as aws

s3_policy = aws.iam.get_policy_document(statements=[{
    "actions": ["s3:GetObject"],
    "resources": [f"{example_aws_s3_bucket['arn']}/*"],
    "principals": [{
        "type": "AWS",
        "identifiers": [example_aws_cloudfront_origin_access_identity["iamArn"]],
    }],
}])
example = aws.s3.BucketPolicy("example",
    bucket=example_aws_s3_bucket["id"],
    policy=s3_policy.json)
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
	"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 {
s3Policy, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement{
{
Actions: []string{
"s3:GetObject",
},
Resources: []string{
fmt.Sprintf("%v/*", exampleAwsS3Bucket.Arn),
},
Principals: []iam.GetPolicyDocumentStatementPrincipal{
{
Type: "AWS",
Identifiers: interface{}{
exampleAwsCloudfrontOriginAccessIdentity.IamArn,
},
},
},
},
},
}, nil);
if err != nil {
return err
}
_, err = s3.NewBucketPolicy(ctx, "example", &s3.BucketPolicyArgs{
Bucket: pulumi.Any(exampleAwsS3Bucket.Id),
Policy: pulumi.String(s3Policy.Json),
})
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 s3Policy = Aws.Iam.GetPolicyDocument.Invoke(new()
    {
        Statements = new[]
        {
            new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
            {
                Actions = new[]
                {
                    "s3:GetObject",
                },
                Resources = new[]
                {
                    $"{exampleAwsS3Bucket.Arn}/*",
                },
                Principals = new[]
                {
                    new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
                    {
                        Type = "AWS",
                        Identifiers = new[]
                        {
                            exampleAwsCloudfrontOriginAccessIdentity.IamArn,
                        },
                    },
                },
            },
        },
    });

    var example = new Aws.S3.BucketPolicy("example", new()
    {
        Bucket = exampleAwsS3Bucket.Id,
        Policy = s3Policy.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.iam.IamFunctions;
import com.pulumi.aws.iam.inputs.GetPolicyDocumentArgs;
import com.pulumi.aws.s3.BucketPolicy;
import com.pulumi.aws.s3.BucketPolicyArgs;
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 s3Policy = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
            .statements(GetPolicyDocumentStatementArgs.builder()
                .actions("s3:GetObject")
                .resources(String.format("%s/*", exampleAwsS3Bucket.arn()))
                .principals(GetPolicyDocumentStatementPrincipalArgs.builder()
                    .type("AWS")
                    .identifiers(exampleAwsCloudfrontOriginAccessIdentity.iamArn())
                    .build())
                .build())
            .build());

        var example = new BucketPolicy("example", BucketPolicyArgs.builder()
            .bucket(exampleAwsS3Bucket.id())
            .policy(s3Policy.json())
            .build());

    }
}
resources:
  example:
    type: aws:s3:BucketPolicy
    properties:
      bucket: ${exampleAwsS3Bucket.id}
      policy: ${s3Policy.json}
variables:
  s3Policy:
    fn::invoke:
      function: aws:iam:getPolicyDocument
      arguments:
        statements:
          - actions:
              - s3:GetObject
            resources:
              - ${exampleAwsS3Bucket.arn}/*
            principals:
              - type: AWS
                identifiers:
                  - ${exampleAwsCloudfrontOriginAccessIdentity.iamArn}

The iamArn property provides the principal identifier for bucket policies. Using iamArn instead of s3CanonicalUserId avoids spurious diffs caused by AWS API translations. The policy grants s3:GetObject permission, allowing CloudFront to retrieve objects on behalf of viewers.

Beyond these examples

These snippets focus on specific origin access identity features: identity creation and CloudFront integration, and S3 bucket policy authorization. They’re intentionally minimal rather than complete CDN configurations.

The examples reference pre-existing infrastructure such as S3 buckets and CloudFront distributions. They focus on configuring the identity and its integration points rather than provisioning the full CDN stack.

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

  • Distribution configuration beyond origin settings
  • S3 bucket creation and versioning
  • Alternative access methods (CloudFront Functions, Lambda@Edge)

These omissions are intentional: the goal is to illustrate how the origin access identity is wired, not provide drop-in CDN modules. See the CloudFront OriginAccessIdentity resource reference for all available configuration options.

Let's create AWS CloudFront Origin Access Identities

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

S3 Bucket Policies
Why am I seeing spurious diffs in my S3 bucket policy?
The AWS API translates the s3CanonicalUserId CanonicalUser principal into an AWS IAM ARN principal in bucket policies, causing spurious diffs. Use iamArn instead of s3CanonicalUserId in your bucket policy to avoid this issue.
How do I grant an origin access identity access to my S3 bucket?
Create an S3 bucket policy using the origin access identity’s iamArn property as the principal identifier. This avoids spurious diffs that occur when using s3CanonicalUserId.
What's the difference between s3CanonicalUserId and iamArn?
Both identify the origin access identity, but s3CanonicalUserId is the canonical user ID while iamArn is a pre-generated ARN. Use iamArn in bucket policies to prevent AWS from translating the principal and causing spurious diffs.
CloudFront Integration
How do I reference an origin access identity in a CloudFront distribution?
Use the cloudfrontAccessIdentityPath property in the s3OriginConfig of your CloudFront distribution. This property provides the full path with the required origin-access-identity/cloudfront/ prefix automatically.

Using a different cloud?

Explore networking guides for other cloud providers: