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 CloudFront to access private S3 content without making buckets publicly readable. This guide focuses on three capabilities: creating origin access identities, connecting them to CloudFront distributions, and configuring S3 bucket policies for access.

Origin access identities establish a trust relationship between CloudFront and S3. The examples are intentionally small. Combine them with your own CloudFront distributions and S3 buckets to build complete content delivery workflows.

Create an origin access identity

CloudFront origin access identities enable CloudFront to serve private S3 content securely without exposing buckets to the public internet.

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 human-readable metadata to identify the identity’s purpose. Once created, the identity generates several output properties including cloudfrontAccessIdentityPath and iamArn, which you’ll use in subsequent configuration steps.

Reference the identity in CloudFront distributions

After creating the identity, connect it to your CloudFront 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, allowing CloudFront to request objects from S3 using the identity’s credentials rather than requiring public bucket access.

Grant S3 bucket access using IAM ARN

The S3 bucket policy must explicitly grant the origin access identity permission 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 a stable IAM principal format for bucket policies. Using iamArn instead of s3CanonicalUserId avoids spurious diffs caused by AWS API translations. The policy grants s3:GetObject permission on all objects in the bucket to the CloudFront identity.

Beyond these examples

These snippets focus on specific origin access identity features: identity creation, CloudFront distribution integration, and S3 bucket policy configuration. They’re intentionally minimal rather than complete content delivery setups.

The examples reference pre-existing infrastructure such as S3 buckets for origin content and CloudFront distributions for integration examples. They focus on configuring the origin access identity rather than provisioning the surrounding infrastructure.

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

  • CloudFront distribution configuration beyond origin settings
  • S3 bucket creation and versioning
  • Alternative authentication methods (S3 bucket policies with canonical user ID)
  • Multi-origin CloudFront setups

These omissions are intentional: the goal is to illustrate how origin access identities are 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

CloudFront Integration
How do I use an origin access identity with a CloudFront distribution?
Reference the cloudfrontAccessIdentityPath property in your distribution’s s3OriginConfig.originAccessIdentity field. This provides the correctly formatted path without needing to manually add the origin-access-identity/cloudfront/ prefix.
What's the purpose of cloudfrontAccessIdentityPath?
It’s a shortcut that provides the full path for the origin access identity with the required origin-access-identity/cloudfront/ prefix already included, so you don’t have to construct it manually.
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 Pulumi to detect changes on every run. Use iamArn instead of s3CanonicalUserId in your bucket policy principals to avoid this issue.
Which property should I use in S3 bucket policies?
Use iamArn in the bucket policy’s principal field. While s3CanonicalUserId is available, it causes spurious diffs because AWS translates it to an IAM ARN format.
Properties & Outputs
What's the difference between s3CanonicalUserId and iamArn?
Both identify the origin access identity for S3 permissions, but s3CanonicalUserId is the canonical user ID format while iamArn is the pre-generated IAM ARN format (e.g., arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E2QWRUHAPOMQZL). Use iamArn in bucket policies to avoid spurious diffs.

Using a different cloud?

Explore networking guides for other cloud providers: