The aws:cloudfront/distribution:Distribution resource, part of the Pulumi AWS provider, defines a CloudFront distribution that serves content from origins through AWS edge locations. This guide focuses on four capabilities: S3 origin configuration with SSL, origin failover for availability, managed caching policies, and standard logging V2 to S3 and Firehose.
Distributions reference S3 buckets, ACM certificates, origin access controls, and optionally Route53 zones or Kinesis Firehose streams. The examples are intentionally small. Combine them with your own origin infrastructure, SSL certificates, and logging destinations.
Serve S3 content with custom domains and SSL
Most deployments distribute static content from S3 buckets using custom domains and SSL certificates for branded HTTPS URLs.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const b = new aws.s3.Bucket("b", {
bucket: "mybucket",
tags: {
Name: "My bucket",
},
});
const s3OriginId = "myS3Origin";
const myDomain = "mydomain.com";
const myDomainGetCertificate = aws.acm.getCertificate({
region: "us-east-1",
domain: `*.${myDomain}`,
statuses: ["ISSUED"],
});
const _default = new aws.cloudfront.OriginAccessControl("default", {
name: "default-oac",
originAccessControlOriginType: "s3",
signingBehavior: "always",
signingProtocol: "sigv4",
});
const s3Distribution = new aws.cloudfront.Distribution("s3_distribution", {
origins: [{
domainName: b.bucketRegionalDomainName,
originAccessControlId: _default.id,
originId: s3OriginId,
}],
enabled: true,
isIpv6Enabled: true,
comment: "Some comment",
defaultRootObject: "index.html",
aliases: [
`mysite.${myDomain}`,
`yoursite.${myDomain}`,
],
defaultCacheBehavior: {
allowedMethods: [
"DELETE",
"GET",
"HEAD",
"OPTIONS",
"PATCH",
"POST",
"PUT",
],
cachedMethods: [
"GET",
"HEAD",
],
targetOriginId: s3OriginId,
forwardedValues: {
queryString: false,
cookies: {
forward: "none",
},
},
viewerProtocolPolicy: "allow-all",
minTtl: 0,
defaultTtl: 3600,
maxTtl: 86400,
},
orderedCacheBehaviors: [
{
pathPattern: "/content/immutable/*",
allowedMethods: [
"GET",
"HEAD",
"OPTIONS",
],
cachedMethods: [
"GET",
"HEAD",
"OPTIONS",
],
targetOriginId: s3OriginId,
forwardedValues: {
queryString: false,
headers: ["Origin"],
cookies: {
forward: "none",
},
},
minTtl: 0,
defaultTtl: 86400,
maxTtl: 31536000,
compress: true,
viewerProtocolPolicy: "redirect-to-https",
},
{
pathPattern: "/content/*",
allowedMethods: [
"GET",
"HEAD",
"OPTIONS",
],
cachedMethods: [
"GET",
"HEAD",
],
targetOriginId: s3OriginId,
forwardedValues: {
queryString: false,
cookies: {
forward: "none",
},
},
minTtl: 0,
defaultTtl: 3600,
maxTtl: 86400,
compress: true,
viewerProtocolPolicy: "redirect-to-https",
},
],
priceClass: "PriceClass_200",
restrictions: {
geoRestriction: {
restrictionType: "whitelist",
locations: [
"US",
"CA",
"GB",
"DE",
],
},
},
tags: {
Environment: "production",
},
viewerCertificate: {
acmCertificateArn: myDomainGetCertificate.then(myDomainGetCertificate => myDomainGetCertificate.arn),
sslSupportMethod: "sni-only",
},
});
// See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
const originBucketPolicy = aws.iam.getPolicyDocumentOutput({
statements: [{
sid: "AllowCloudFrontServicePrincipalReadWrite",
effect: "Allow",
principals: [{
type: "Service",
identifiers: ["cloudfront.amazonaws.com"],
}],
actions: [
"s3:GetObject",
"s3:PutObject",
],
resources: [pulumi.interpolate`${b.arn}/*`],
conditions: [{
test: "StringEquals",
variable: "AWS:SourceArn",
values: [s3Distribution.arn],
}],
}],
});
const bBucketPolicy = new aws.s3.BucketPolicy("b", {
bucket: b.bucket,
policy: originBucketPolicy.apply(originBucketPolicy => originBucketPolicy.json),
});
// Create Route53 records for the CloudFront distribution aliases
const myDomainGetZone = aws.route53.getZone({
name: myDomain,
});
const cloudfront: aws.route53.Record[] = [];
s3Distribution.aliases.apply(rangeBody => {
for (const range of rangeBody.map((v, k) => ({key: k, value: v}))) {
cloudfront.push(new aws.route53.Record(`cloudfront-${range.key}`, {
zoneId: myDomainGetZone.then(myDomainGetZone => myDomainGetZone.zoneId),
name: range.value,
type: aws.route53.RecordType.A,
aliases: [{
name: s3Distribution.domainName,
zoneId: s3Distribution.hostedZoneId,
evaluateTargetHealth: false,
}],
}));
}
});
import pulumi
import pulumi_aws as aws
b = aws.s3.Bucket("b",
bucket="mybucket",
tags={
"Name": "My bucket",
})
s3_origin_id = "myS3Origin"
my_domain = "mydomain.com"
my_domain_get_certificate = aws.acm.get_certificate(region="us-east-1",
domain=f"*.{my_domain}",
statuses=["ISSUED"])
default = aws.cloudfront.OriginAccessControl("default",
name="default-oac",
origin_access_control_origin_type="s3",
signing_behavior="always",
signing_protocol="sigv4")
s3_distribution = aws.cloudfront.Distribution("s3_distribution",
origins=[{
"domain_name": b.bucket_regional_domain_name,
"origin_access_control_id": default.id,
"origin_id": s3_origin_id,
}],
enabled=True,
is_ipv6_enabled=True,
comment="Some comment",
default_root_object="index.html",
aliases=[
f"mysite.{my_domain}",
f"yoursite.{my_domain}",
],
default_cache_behavior={
"allowed_methods": [
"DELETE",
"GET",
"HEAD",
"OPTIONS",
"PATCH",
"POST",
"PUT",
],
"cached_methods": [
"GET",
"HEAD",
],
"target_origin_id": s3_origin_id,
"forwarded_values": {
"query_string": False,
"cookies": {
"forward": "none",
},
},
"viewer_protocol_policy": "allow-all",
"min_ttl": 0,
"default_ttl": 3600,
"max_ttl": 86400,
},
ordered_cache_behaviors=[
{
"path_pattern": "/content/immutable/*",
"allowed_methods": [
"GET",
"HEAD",
"OPTIONS",
],
"cached_methods": [
"GET",
"HEAD",
"OPTIONS",
],
"target_origin_id": s3_origin_id,
"forwarded_values": {
"query_string": False,
"headers": ["Origin"],
"cookies": {
"forward": "none",
},
},
"min_ttl": 0,
"default_ttl": 86400,
"max_ttl": 31536000,
"compress": True,
"viewer_protocol_policy": "redirect-to-https",
},
{
"path_pattern": "/content/*",
"allowed_methods": [
"GET",
"HEAD",
"OPTIONS",
],
"cached_methods": [
"GET",
"HEAD",
],
"target_origin_id": s3_origin_id,
"forwarded_values": {
"query_string": False,
"cookies": {
"forward": "none",
},
},
"min_ttl": 0,
"default_ttl": 3600,
"max_ttl": 86400,
"compress": True,
"viewer_protocol_policy": "redirect-to-https",
},
],
price_class="PriceClass_200",
restrictions={
"geo_restriction": {
"restriction_type": "whitelist",
"locations": [
"US",
"CA",
"GB",
"DE",
],
},
},
tags={
"Environment": "production",
},
viewer_certificate={
"acm_certificate_arn": my_domain_get_certificate.arn,
"ssl_support_method": "sni-only",
})
# See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
origin_bucket_policy = aws.iam.get_policy_document_output(statements=[{
"sid": "AllowCloudFrontServicePrincipalReadWrite",
"effect": "Allow",
"principals": [{
"type": "Service",
"identifiers": ["cloudfront.amazonaws.com"],
}],
"actions": [
"s3:GetObject",
"s3:PutObject",
],
"resources": [b.arn.apply(lambda arn: f"{arn}/*")],
"conditions": [{
"test": "StringEquals",
"variable": "AWS:SourceArn",
"values": [s3_distribution.arn],
}],
}])
b_bucket_policy = aws.s3.BucketPolicy("b",
bucket=b.bucket,
policy=origin_bucket_policy.json)
# Create Route53 records for the CloudFront distribution aliases
my_domain_get_zone = aws.route53.get_zone(name=my_domain)
cloudfront = []
def create_cloudfront(range_body):
for range in [{"key": k, "value": v} for [k, v] in enumerate(range_body)]:
cloudfront.append(aws.route53.Record(f"cloudfront-{range['key']}",
zone_id=my_domain_get_zone.zone_id,
name=range["value"],
type=aws.route53.RecordType.A,
aliases=[{
"name": s3_distribution.domain_name.apply(lambda domain_name: domain_name),
"zone_id": s3_distribution.hosted_zone_id.apply(lambda hosted_zone_id: hosted_zone_id),
"evaluate_target_health": False,
}]))
s3_distribution.aliases.apply(create_cloudfront)
package main
import (
"fmt"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/acm"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudfront"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/route53"
"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 {
b, err := s3.NewBucket(ctx, "b", &s3.BucketArgs{
Bucket: pulumi.String("mybucket"),
Tags: pulumi.StringMap{
"Name": pulumi.String("My bucket"),
},
})
if err != nil {
return err
}
s3OriginId := "myS3Origin"
myDomain := "mydomain.com"
myDomainGetCertificate, err := acm.LookupCertificate(ctx, &acm.LookupCertificateArgs{
Region: pulumi.StringRef("us-east-1"),
Domain: pulumi.StringRef(fmt.Sprintf("*.%v", myDomain)),
Statuses: []string{
"ISSUED",
},
}, nil)
if err != nil {
return err
}
_default, err := cloudfront.NewOriginAccessControl(ctx, "default", &cloudfront.OriginAccessControlArgs{
Name: pulumi.String("default-oac"),
OriginAccessControlOriginType: pulumi.String("s3"),
SigningBehavior: pulumi.String("always"),
SigningProtocol: pulumi.String("sigv4"),
})
if err != nil {
return err
}
s3Distribution, err := cloudfront.NewDistribution(ctx, "s3_distribution", &cloudfront.DistributionArgs{
Origins: cloudfront.DistributionOriginArray{
&cloudfront.DistributionOriginArgs{
DomainName: b.BucketRegionalDomainName,
OriginAccessControlId: _default.ID(),
OriginId: pulumi.String(s3OriginId),
},
},
Enabled: pulumi.Bool(true),
IsIpv6Enabled: pulumi.Bool(true),
Comment: pulumi.String("Some comment"),
DefaultRootObject: pulumi.String("index.html"),
Aliases: pulumi.StringArray{
pulumi.Sprintf("mysite.%v", myDomain),
pulumi.Sprintf("yoursite.%v", myDomain),
},
DefaultCacheBehavior: &cloudfront.DistributionDefaultCacheBehaviorArgs{
AllowedMethods: pulumi.StringArray{
pulumi.String("DELETE"),
pulumi.String("GET"),
pulumi.String("HEAD"),
pulumi.String("OPTIONS"),
pulumi.String("PATCH"),
pulumi.String("POST"),
pulumi.String("PUT"),
},
CachedMethods: pulumi.StringArray{
pulumi.String("GET"),
pulumi.String("HEAD"),
},
TargetOriginId: pulumi.String(s3OriginId),
ForwardedValues: &cloudfront.DistributionDefaultCacheBehaviorForwardedValuesArgs{
QueryString: pulumi.Bool(false),
Cookies: &cloudfront.DistributionDefaultCacheBehaviorForwardedValuesCookiesArgs{
Forward: pulumi.String("none"),
},
},
ViewerProtocolPolicy: pulumi.String("allow-all"),
MinTtl: pulumi.Int(0),
DefaultTtl: pulumi.Int(3600),
MaxTtl: pulumi.Int(86400),
},
OrderedCacheBehaviors: cloudfront.DistributionOrderedCacheBehaviorArray{
&cloudfront.DistributionOrderedCacheBehaviorArgs{
PathPattern: pulumi.String("/content/immutable/*"),
AllowedMethods: pulumi.StringArray{
pulumi.String("GET"),
pulumi.String("HEAD"),
pulumi.String("OPTIONS"),
},
CachedMethods: pulumi.StringArray{
pulumi.String("GET"),
pulumi.String("HEAD"),
pulumi.String("OPTIONS"),
},
TargetOriginId: pulumi.String(s3OriginId),
ForwardedValues: &cloudfront.DistributionOrderedCacheBehaviorForwardedValuesArgs{
QueryString: pulumi.Bool(false),
Headers: pulumi.StringArray{
pulumi.String("Origin"),
},
Cookies: &cloudfront.DistributionOrderedCacheBehaviorForwardedValuesCookiesArgs{
Forward: pulumi.String("none"),
},
},
MinTtl: pulumi.Int(0),
DefaultTtl: pulumi.Int(86400),
MaxTtl: pulumi.Int(31536000),
Compress: pulumi.Bool(true),
ViewerProtocolPolicy: pulumi.String("redirect-to-https"),
},
&cloudfront.DistributionOrderedCacheBehaviorArgs{
PathPattern: pulumi.String("/content/*"),
AllowedMethods: pulumi.StringArray{
pulumi.String("GET"),
pulumi.String("HEAD"),
pulumi.String("OPTIONS"),
},
CachedMethods: pulumi.StringArray{
pulumi.String("GET"),
pulumi.String("HEAD"),
},
TargetOriginId: pulumi.String(s3OriginId),
ForwardedValues: &cloudfront.DistributionOrderedCacheBehaviorForwardedValuesArgs{
QueryString: pulumi.Bool(false),
Cookies: &cloudfront.DistributionOrderedCacheBehaviorForwardedValuesCookiesArgs{
Forward: pulumi.String("none"),
},
},
MinTtl: pulumi.Int(0),
DefaultTtl: pulumi.Int(3600),
MaxTtl: pulumi.Int(86400),
Compress: pulumi.Bool(true),
ViewerProtocolPolicy: pulumi.String("redirect-to-https"),
},
},
PriceClass: pulumi.String("PriceClass_200"),
Restrictions: &cloudfront.DistributionRestrictionsArgs{
GeoRestriction: &cloudfront.DistributionRestrictionsGeoRestrictionArgs{
RestrictionType: pulumi.String("whitelist"),
Locations: pulumi.StringArray{
pulumi.String("US"),
pulumi.String("CA"),
pulumi.String("GB"),
pulumi.String("DE"),
},
},
},
Tags: pulumi.StringMap{
"Environment": pulumi.String("production"),
},
ViewerCertificate: &cloudfront.DistributionViewerCertificateArgs{
AcmCertificateArn: pulumi.String(myDomainGetCertificate.Arn),
SslSupportMethod: pulumi.String("sni-only"),
},
})
if err != nil {
return err
}
// See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
originBucketPolicy := iam.GetPolicyDocumentOutput(ctx, iam.GetPolicyDocumentOutputArgs{
Statements: iam.GetPolicyDocumentStatementArray{
&iam.GetPolicyDocumentStatementArgs{
Sid: pulumi.String("AllowCloudFrontServicePrincipalReadWrite"),
Effect: pulumi.String("Allow"),
Principals: iam.GetPolicyDocumentStatementPrincipalArray{
&iam.GetPolicyDocumentStatementPrincipalArgs{
Type: pulumi.String("Service"),
Identifiers: pulumi.StringArray{
pulumi.String("cloudfront.amazonaws.com"),
},
},
},
Actions: pulumi.StringArray{
pulumi.String("s3:GetObject"),
pulumi.String("s3:PutObject"),
},
Resources: pulumi.StringArray{
b.Arn.ApplyT(func(arn string) (string, error) {
return fmt.Sprintf("%v/*", arn), nil
}).(pulumi.StringOutput),
},
Conditions: iam.GetPolicyDocumentStatementConditionArray{
&iam.GetPolicyDocumentStatementConditionArgs{
Test: pulumi.String("StringEquals"),
Variable: pulumi.String("AWS:SourceArn"),
Values: pulumi.StringArray{
s3Distribution.Arn,
},
},
},
},
},
}, nil)
_, err = s3.NewBucketPolicy(ctx, "b", &s3.BucketPolicyArgs{
Bucket: b.Bucket,
Policy: pulumi.String(originBucketPolicy.ApplyT(func(originBucketPolicy iam.GetPolicyDocumentResult) (*string, error) {
return &originBucketPolicy.Json, nil
}).(pulumi.StringPtrOutput)),
})
if err != nil {
return err
}
// Create Route53 records for the CloudFront distribution aliases
myDomainGetZone, err := route53.LookupZone(ctx, &route53.LookupZoneArgs{
Name: pulumi.StringRef(myDomain),
}, nil)
if err != nil {
return err
}
var cloudfront []*route53.Record
for key0, val0 := range s3Distribution.Aliases {
__res, err := route53.NewRecord(ctx, fmt.Sprintf("cloudfront-%v", key0), &route53.RecordArgs{
ZoneId: pulumi.String(myDomainGetZone.ZoneId),
Name: pulumi.String(val0),
Type: pulumi.String(route53.RecordTypeA),
Aliases: route53.RecordAliasArray{
&route53.RecordAliasArgs{
Name: s3Distribution.DomainName,
ZoneId: s3Distribution.HostedZoneId,
EvaluateTargetHealth: pulumi.Bool(false),
},
},
})
if err != nil {
return err
}
cloudfront = append(cloudfront, __res)
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var b = new Aws.S3.Bucket("b", new()
{
BucketName = "mybucket",
Tags =
{
{ "Name", "My bucket" },
},
});
var s3OriginId = "myS3Origin";
var myDomain = "mydomain.com";
var myDomainGetCertificate = Aws.Acm.GetCertificate.Invoke(new()
{
Region = "us-east-1",
Domain = $"*.{myDomain}",
Statuses = new[]
{
"ISSUED",
},
});
var @default = new Aws.CloudFront.OriginAccessControl("default", new()
{
Name = "default-oac",
OriginAccessControlOriginType = "s3",
SigningBehavior = "always",
SigningProtocol = "sigv4",
});
var s3Distribution = new Aws.CloudFront.Distribution("s3_distribution", new()
{
Origins = new[]
{
new Aws.CloudFront.Inputs.DistributionOriginArgs
{
DomainName = b.BucketRegionalDomainName,
OriginAccessControlId = @default.Id,
OriginId = s3OriginId,
},
},
Enabled = true,
IsIpv6Enabled = true,
Comment = "Some comment",
DefaultRootObject = "index.html",
Aliases = new[]
{
$"mysite.{myDomain}",
$"yoursite.{myDomain}",
},
DefaultCacheBehavior = new Aws.CloudFront.Inputs.DistributionDefaultCacheBehaviorArgs
{
AllowedMethods = new[]
{
"DELETE",
"GET",
"HEAD",
"OPTIONS",
"PATCH",
"POST",
"PUT",
},
CachedMethods = new[]
{
"GET",
"HEAD",
},
TargetOriginId = s3OriginId,
ForwardedValues = new Aws.CloudFront.Inputs.DistributionDefaultCacheBehaviorForwardedValuesArgs
{
QueryString = false,
Cookies = new Aws.CloudFront.Inputs.DistributionDefaultCacheBehaviorForwardedValuesCookiesArgs
{
Forward = "none",
},
},
ViewerProtocolPolicy = "allow-all",
MinTtl = 0,
DefaultTtl = 3600,
MaxTtl = 86400,
},
OrderedCacheBehaviors = new[]
{
new Aws.CloudFront.Inputs.DistributionOrderedCacheBehaviorArgs
{
PathPattern = "/content/immutable/*",
AllowedMethods = new[]
{
"GET",
"HEAD",
"OPTIONS",
},
CachedMethods = new[]
{
"GET",
"HEAD",
"OPTIONS",
},
TargetOriginId = s3OriginId,
ForwardedValues = new Aws.CloudFront.Inputs.DistributionOrderedCacheBehaviorForwardedValuesArgs
{
QueryString = false,
Headers = new[]
{
"Origin",
},
Cookies = new Aws.CloudFront.Inputs.DistributionOrderedCacheBehaviorForwardedValuesCookiesArgs
{
Forward = "none",
},
},
MinTtl = 0,
DefaultTtl = 86400,
MaxTtl = 31536000,
Compress = true,
ViewerProtocolPolicy = "redirect-to-https",
},
new Aws.CloudFront.Inputs.DistributionOrderedCacheBehaviorArgs
{
PathPattern = "/content/*",
AllowedMethods = new[]
{
"GET",
"HEAD",
"OPTIONS",
},
CachedMethods = new[]
{
"GET",
"HEAD",
},
TargetOriginId = s3OriginId,
ForwardedValues = new Aws.CloudFront.Inputs.DistributionOrderedCacheBehaviorForwardedValuesArgs
{
QueryString = false,
Cookies = new Aws.CloudFront.Inputs.DistributionOrderedCacheBehaviorForwardedValuesCookiesArgs
{
Forward = "none",
},
},
MinTtl = 0,
DefaultTtl = 3600,
MaxTtl = 86400,
Compress = true,
ViewerProtocolPolicy = "redirect-to-https",
},
},
PriceClass = "PriceClass_200",
Restrictions = new Aws.CloudFront.Inputs.DistributionRestrictionsArgs
{
GeoRestriction = new Aws.CloudFront.Inputs.DistributionRestrictionsGeoRestrictionArgs
{
RestrictionType = "whitelist",
Locations = new[]
{
"US",
"CA",
"GB",
"DE",
},
},
},
Tags =
{
{ "Environment", "production" },
},
ViewerCertificate = new Aws.CloudFront.Inputs.DistributionViewerCertificateArgs
{
AcmCertificateArn = myDomainGetCertificate.Apply(getCertificateResult => getCertificateResult.Arn),
SslSupportMethod = "sni-only",
},
});
// See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
var originBucketPolicy = Aws.Iam.GetPolicyDocument.Invoke(new()
{
Statements = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
{
Sid = "AllowCloudFrontServicePrincipalReadWrite",
Effect = "Allow",
Principals = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
{
Type = "Service",
Identifiers = new[]
{
"cloudfront.amazonaws.com",
},
},
},
Actions = new[]
{
"s3:GetObject",
"s3:PutObject",
},
Resources = new[]
{
$"{b.Arn}/*",
},
Conditions = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementConditionInputArgs
{
Test = "StringEquals",
Variable = "AWS:SourceArn",
Values = new[]
{
s3Distribution.Arn,
},
},
},
},
},
});
var bBucketPolicy = new Aws.S3.BucketPolicy("b", new()
{
Bucket = b.BucketName,
Policy = originBucketPolicy.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
});
// Create Route53 records for the CloudFront distribution aliases
var myDomainGetZone = Aws.Route53.GetZone.Invoke(new()
{
Name = myDomain,
});
var cloudfront = new List<Aws.Route53.Record>();
foreach (var range in )
{
cloudfront.Add(new Aws.Route53.Record($"cloudfront-{range.Key}", new()
{
ZoneId = myDomainGetZone.Apply(getZoneResult => getZoneResult.ZoneId),
Name = range.Value,
Type = Aws.Route53.RecordType.A,
Aliases = new[]
{
new Aws.Route53.Inputs.RecordAliasArgs
{
Name = s3Distribution.DomainName,
ZoneId = s3Distribution.HostedZoneId,
EvaluateTargetHealth = false,
},
},
}));
}
});
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.acm.AcmFunctions;
import com.pulumi.aws.acm.inputs.GetCertificateArgs;
import com.pulumi.aws.cloudfront.OriginAccessControl;
import com.pulumi.aws.cloudfront.OriginAccessControlArgs;
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.DistributionDefaultCacheBehaviorArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionDefaultCacheBehaviorForwardedValuesArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionDefaultCacheBehaviorForwardedValuesCookiesArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionOrderedCacheBehaviorArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionOrderedCacheBehaviorForwardedValuesArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionOrderedCacheBehaviorForwardedValuesCookiesArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionRestrictionsArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionRestrictionsGeoRestrictionArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionViewerCertificateArgs;
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 com.pulumi.aws.route53.Route53Functions;
import com.pulumi.aws.route53.inputs.GetZoneArgs;
import com.pulumi.aws.route53.Record;
import com.pulumi.aws.route53.RecordArgs;
import com.pulumi.aws.route53.inputs.RecordAliasArgs;
import com.pulumi.codegen.internal.KeyedValue;
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 b = new Bucket("b", BucketArgs.builder()
.bucket("mybucket")
.tags(Map.of("Name", "My bucket"))
.build());
final var s3OriginId = "myS3Origin";
final var myDomain = "mydomain.com";
final var myDomainGetCertificate = AcmFunctions.getCertificate(GetCertificateArgs.builder()
.region("us-east-1")
.domain(String.format("*.%s", myDomain))
.statuses("ISSUED")
.build());
var default_ = new OriginAccessControl("default", OriginAccessControlArgs.builder()
.name("default-oac")
.originAccessControlOriginType("s3")
.signingBehavior("always")
.signingProtocol("sigv4")
.build());
var s3Distribution = new Distribution("s3Distribution", DistributionArgs.builder()
.origins(DistributionOriginArgs.builder()
.domainName(b.bucketRegionalDomainName())
.originAccessControlId(default_.id())
.originId(s3OriginId)
.build())
.enabled(true)
.isIpv6Enabled(true)
.comment("Some comment")
.defaultRootObject("index.html")
.aliases(
String.format("mysite.%s", myDomain),
String.format("yoursite.%s", myDomain))
.defaultCacheBehavior(DistributionDefaultCacheBehaviorArgs.builder()
.allowedMethods(
"DELETE",
"GET",
"HEAD",
"OPTIONS",
"PATCH",
"POST",
"PUT")
.cachedMethods(
"GET",
"HEAD")
.targetOriginId(s3OriginId)
.forwardedValues(DistributionDefaultCacheBehaviorForwardedValuesArgs.builder()
.queryString(false)
.cookies(DistributionDefaultCacheBehaviorForwardedValuesCookiesArgs.builder()
.forward("none")
.build())
.build())
.viewerProtocolPolicy("allow-all")
.minTtl(0)
.defaultTtl(3600)
.maxTtl(86400)
.build())
.orderedCacheBehaviors(
DistributionOrderedCacheBehaviorArgs.builder()
.pathPattern("/content/immutable/*")
.allowedMethods(
"GET",
"HEAD",
"OPTIONS")
.cachedMethods(
"GET",
"HEAD",
"OPTIONS")
.targetOriginId(s3OriginId)
.forwardedValues(DistributionOrderedCacheBehaviorForwardedValuesArgs.builder()
.queryString(false)
.headers("Origin")
.cookies(DistributionOrderedCacheBehaviorForwardedValuesCookiesArgs.builder()
.forward("none")
.build())
.build())
.minTtl(0)
.defaultTtl(86400)
.maxTtl(31536000)
.compress(true)
.viewerProtocolPolicy("redirect-to-https")
.build(),
DistributionOrderedCacheBehaviorArgs.builder()
.pathPattern("/content/*")
.allowedMethods(
"GET",
"HEAD",
"OPTIONS")
.cachedMethods(
"GET",
"HEAD")
.targetOriginId(s3OriginId)
.forwardedValues(DistributionOrderedCacheBehaviorForwardedValuesArgs.builder()
.queryString(false)
.cookies(DistributionOrderedCacheBehaviorForwardedValuesCookiesArgs.builder()
.forward("none")
.build())
.build())
.minTtl(0)
.defaultTtl(3600)
.maxTtl(86400)
.compress(true)
.viewerProtocolPolicy("redirect-to-https")
.build())
.priceClass("PriceClass_200")
.restrictions(DistributionRestrictionsArgs.builder()
.geoRestriction(DistributionRestrictionsGeoRestrictionArgs.builder()
.restrictionType("whitelist")
.locations(
"US",
"CA",
"GB",
"DE")
.build())
.build())
.tags(Map.of("Environment", "production"))
.viewerCertificate(DistributionViewerCertificateArgs.builder()
.acmCertificateArn(myDomainGetCertificate.arn())
.sslSupportMethod("sni-only")
.build())
.build());
// See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
final var originBucketPolicy = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
.statements(GetPolicyDocumentStatementArgs.builder()
.sid("AllowCloudFrontServicePrincipalReadWrite")
.effect("Allow")
.principals(GetPolicyDocumentStatementPrincipalArgs.builder()
.type("Service")
.identifiers("cloudfront.amazonaws.com")
.build())
.actions(
"s3:GetObject",
"s3:PutObject")
.resources(b.arn().applyValue(_arn -> String.format("%s/*", _arn)))
.conditions(GetPolicyDocumentStatementConditionArgs.builder()
.test("StringEquals")
.variable("AWS:SourceArn")
.values(s3Distribution.arn())
.build())
.build())
.build());
var bBucketPolicy = new BucketPolicy("bBucketPolicy", BucketPolicyArgs.builder()
.bucket(b.bucket())
.policy(originBucketPolicy.applyValue(_originBucketPolicy -> _originBucketPolicy.json()))
.build());
// Create Route53 records for the CloudFront distribution aliases
final var myDomainGetZone = Route53Functions.getZone(GetZoneArgs.builder()
.name(myDomain)
.build());
for (var range : KeyedValue.of(s3Distribution.aliases())) {
new Record("cloudfront-" + range.key(), RecordArgs.builder()
.zoneId(myDomainGetZone.zoneId())
.name(range.value())
.type("A")
.aliases(RecordAliasArgs.builder()
.name(s3Distribution.domainName())
.zoneId(s3Distribution.hostedZoneId())
.evaluateTargetHealth(false)
.build())
.build());
}
}
}
resources:
b:
type: aws:s3:Bucket
properties:
bucket: mybucket
tags:
Name: My bucket
bBucketPolicy:
type: aws:s3:BucketPolicy
name: b
properties:
bucket: ${b.bucket}
policy: ${originBucketPolicy.json}
default:
type: aws:cloudfront:OriginAccessControl
properties:
name: default-oac
originAccessControlOriginType: s3
signingBehavior: always
signingProtocol: sigv4
s3Distribution:
type: aws:cloudfront:Distribution
name: s3_distribution
properties:
origins:
- domainName: ${b.bucketRegionalDomainName}
originAccessControlId: ${default.id}
originId: ${s3OriginId}
enabled: true
isIpv6Enabled: true
comment: Some comment
defaultRootObject: index.html
aliases:
- mysite.${myDomain}
- yoursite.${myDomain}
defaultCacheBehavior:
allowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
cachedMethods:
- GET
- HEAD
targetOriginId: ${s3OriginId}
forwardedValues:
queryString: false
cookies:
forward: none
viewerProtocolPolicy: allow-all
minTtl: 0
defaultTtl: 3600
maxTtl: 86400
orderedCacheBehaviors:
- pathPattern: /content/immutable/*
allowedMethods:
- GET
- HEAD
- OPTIONS
cachedMethods:
- GET
- HEAD
- OPTIONS
targetOriginId: ${s3OriginId}
forwardedValues:
queryString: false
headers:
- Origin
cookies:
forward: none
minTtl: 0
defaultTtl: 86400
maxTtl: 3.1536e+07
compress: true
viewerProtocolPolicy: redirect-to-https
- pathPattern: /content/*
allowedMethods:
- GET
- HEAD
- OPTIONS
cachedMethods:
- GET
- HEAD
targetOriginId: ${s3OriginId}
forwardedValues:
queryString: false
cookies:
forward: none
minTtl: 0
defaultTtl: 3600
maxTtl: 86400
compress: true
viewerProtocolPolicy: redirect-to-https
priceClass: PriceClass_200
restrictions:
geoRestriction:
restrictionType: whitelist
locations:
- US
- CA
- GB
- DE
tags:
Environment: production
viewerCertificate:
acmCertificateArn: ${myDomainGetCertificate.arn}
sslSupportMethod: sni-only
cloudfront:
type: aws:route53:Record
properties:
zoneId: ${myDomainGetZone.zoneId}
name: ${range.value}
type: A
aliases:
- name: ${s3Distribution.domainName}
zoneId: ${s3Distribution.hostedZoneId}
evaluateTargetHealth: false
options: {}
variables:
# See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
originBucketPolicy:
fn::invoke:
function: aws:iam:getPolicyDocument
arguments:
statements:
- sid: AllowCloudFrontServicePrincipalReadWrite
effect: Allow
principals:
- type: Service
identifiers:
- cloudfront.amazonaws.com
actions:
- s3:GetObject
- s3:PutObject
resources:
- ${b.arn}/*
conditions:
- test: StringEquals
variable: AWS:SourceArn
values:
- ${s3Distribution.arn}
s3OriginId: myS3Origin
myDomain: mydomain.com
myDomainGetCertificate:
fn::invoke:
function: aws:acm:getCertificate
arguments:
region: us-east-1
domain: '*.${myDomain}'
statuses:
- ISSUED
# Create Route53 records for the CloudFront distribution aliases
myDomainGetZone:
fn::invoke:
function: aws:route53:getZone
arguments:
name: ${myDomain}
The origins array defines where CloudFront fetches content. Each origin needs a domainName (the S3 bucket’s regional endpoint) and an originId that cache behaviors reference via targetOriginId. The originAccessControlId grants CloudFront permission to read from the bucket. The aliases property lists custom domains, which require a matching ACM certificate in viewerCertificate. The example includes orderedCacheBehaviors for path-specific caching rules and a bucket policy that allows CloudFront access.
Configure origin failover for high availability
When primary origins fail, CloudFront can automatically route requests to backup origins based on error codes.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const s3Distribution = new aws.cloudfront.Distribution("s3_distribution", {
originGroups: [{
originId: "groupS3",
failoverCriteria: {
statusCodes: [
403,
404,
500,
502,
],
},
members: [
{
originId: "primaryS3",
},
{
originId: "failoverS3",
},
],
}],
origins: [
{
domainName: primary.bucketRegionalDomainName,
originId: "primaryS3",
s3OriginConfig: {
originAccessIdentity: _default.cloudfrontAccessIdentityPath,
},
},
{
domainName: failover.bucketRegionalDomainName,
originId: "failoverS3",
s3OriginConfig: {
originAccessIdentity: _default.cloudfrontAccessIdentityPath,
},
},
],
defaultCacheBehavior: {
targetOriginId: "groupS3",
},
});
import pulumi
import pulumi_aws as aws
s3_distribution = aws.cloudfront.Distribution("s3_distribution",
origin_groups=[{
"origin_id": "groupS3",
"failover_criteria": {
"status_codes": [
403,
404,
500,
502,
],
},
"members": [
{
"origin_id": "primaryS3",
},
{
"origin_id": "failoverS3",
},
],
}],
origins=[
{
"domain_name": primary["bucketRegionalDomainName"],
"origin_id": "primaryS3",
"s3_origin_config": {
"origin_access_identity": default["cloudfrontAccessIdentityPath"],
},
},
{
"domain_name": failover["bucketRegionalDomainName"],
"origin_id": "failoverS3",
"s3_origin_config": {
"origin_access_identity": default["cloudfrontAccessIdentityPath"],
},
},
],
default_cache_behavior={
"target_origin_id": "groupS3",
})
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, "s3_distribution", &cloudfront.DistributionArgs{
OriginGroups: cloudfront.DistributionOriginGroupArray{
&cloudfront.DistributionOriginGroupArgs{
OriginId: pulumi.String("groupS3"),
FailoverCriteria: &cloudfront.DistributionOriginGroupFailoverCriteriaArgs{
StatusCodes: pulumi.IntArray{
pulumi.Int(403),
pulumi.Int(404),
pulumi.Int(500),
pulumi.Int(502),
},
},
Members: cloudfront.DistributionOriginGroupMemberArray{
&cloudfront.DistributionOriginGroupMemberArgs{
OriginId: pulumi.String("primaryS3"),
},
&cloudfront.DistributionOriginGroupMemberArgs{
OriginId: pulumi.String("failoverS3"),
},
},
},
},
Origins: cloudfront.DistributionOriginArray{
&cloudfront.DistributionOriginArgs{
DomainName: pulumi.Any(primary.BucketRegionalDomainName),
OriginId: pulumi.String("primaryS3"),
S3OriginConfig: &cloudfront.DistributionOriginS3OriginConfigArgs{
OriginAccessIdentity: pulumi.Any(_default.CloudfrontAccessIdentityPath),
},
},
&cloudfront.DistributionOriginArgs{
DomainName: pulumi.Any(failover.BucketRegionalDomainName),
OriginId: pulumi.String("failoverS3"),
S3OriginConfig: &cloudfront.DistributionOriginS3OriginConfigArgs{
OriginAccessIdentity: pulumi.Any(_default.CloudfrontAccessIdentityPath),
},
},
},
DefaultCacheBehavior: &cloudfront.DistributionDefaultCacheBehaviorArgs{
TargetOriginId: pulumi.String("groupS3"),
},
})
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 s3Distribution = new Aws.CloudFront.Distribution("s3_distribution", new()
{
OriginGroups = new[]
{
new Aws.CloudFront.Inputs.DistributionOriginGroupArgs
{
OriginId = "groupS3",
FailoverCriteria = new Aws.CloudFront.Inputs.DistributionOriginGroupFailoverCriteriaArgs
{
StatusCodes = new[]
{
403,
404,
500,
502,
},
},
Members = new[]
{
new Aws.CloudFront.Inputs.DistributionOriginGroupMemberArgs
{
OriginId = "primaryS3",
},
new Aws.CloudFront.Inputs.DistributionOriginGroupMemberArgs
{
OriginId = "failoverS3",
},
},
},
},
Origins = new[]
{
new Aws.CloudFront.Inputs.DistributionOriginArgs
{
DomainName = primary.BucketRegionalDomainName,
OriginId = "primaryS3",
S3OriginConfig = new Aws.CloudFront.Inputs.DistributionOriginS3OriginConfigArgs
{
OriginAccessIdentity = @default.CloudfrontAccessIdentityPath,
},
},
new Aws.CloudFront.Inputs.DistributionOriginArgs
{
DomainName = failover.BucketRegionalDomainName,
OriginId = "failoverS3",
S3OriginConfig = new Aws.CloudFront.Inputs.DistributionOriginS3OriginConfigArgs
{
OriginAccessIdentity = @default.CloudfrontAccessIdentityPath,
},
},
},
DefaultCacheBehavior = new Aws.CloudFront.Inputs.DistributionDefaultCacheBehaviorArgs
{
TargetOriginId = "groupS3",
},
});
});
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.DistributionOriginGroupArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionOriginGroupFailoverCriteriaArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionOriginArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionOriginS3OriginConfigArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionDefaultCacheBehaviorArgs;
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 s3Distribution = new Distribution("s3Distribution", DistributionArgs.builder()
.originGroups(DistributionOriginGroupArgs.builder()
.originId("groupS3")
.failoverCriteria(DistributionOriginGroupFailoverCriteriaArgs.builder()
.statusCodes(
403,
404,
500,
502)
.build())
.members(
DistributionOriginGroupMemberArgs.builder()
.originId("primaryS3")
.build(),
DistributionOriginGroupMemberArgs.builder()
.originId("failoverS3")
.build())
.build())
.origins(
DistributionOriginArgs.builder()
.domainName(primary.bucketRegionalDomainName())
.originId("primaryS3")
.s3OriginConfig(DistributionOriginS3OriginConfigArgs.builder()
.originAccessIdentity(default_.cloudfrontAccessIdentityPath())
.build())
.build(),
DistributionOriginArgs.builder()
.domainName(failover.bucketRegionalDomainName())
.originId("failoverS3")
.s3OriginConfig(DistributionOriginS3OriginConfigArgs.builder()
.originAccessIdentity(default_.cloudfrontAccessIdentityPath())
.build())
.build())
.defaultCacheBehavior(DistributionDefaultCacheBehaviorArgs.builder()
.targetOriginId("groupS3")
.build())
.build());
}
}
resources:
s3Distribution:
type: aws:cloudfront:Distribution
name: s3_distribution
properties:
originGroups:
- originId: groupS3
failoverCriteria:
statusCodes:
- 403
- 404
- 500
- 502
members:
- originId: primaryS3
- originId: failoverS3
origins:
- domainName: ${primary.bucketRegionalDomainName}
originId: primaryS3
s3OriginConfig:
originAccessIdentity: ${default.cloudfrontAccessIdentityPath}
- domainName: ${failover.bucketRegionalDomainName}
originId: failoverS3
s3OriginConfig:
originAccessIdentity: ${default.cloudfrontAccessIdentityPath}
defaultCacheBehavior:
targetOriginId: groupS3
The originGroups array defines failover logic. The failoverCriteria specifies which HTTP status codes trigger failover (403, 404, 500, 502). The members array lists origins in priority order: CloudFront tries primaryS3 first, then failoverS3. The defaultCacheBehavior references the origin group by its originId rather than a single origin.
Use AWS managed caching policies
AWS provides pre-configured caching policies that implement common patterns without manual cache configuration.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const s3OriginId = "myS3Origin";
const s3Distribution = new aws.cloudfront.Distribution("s3_distribution", {
origins: [{
domainName: primary.bucketRegionalDomainName,
originId: "myS3Origin",
s3OriginConfig: {
originAccessIdentity: _default.cloudfrontAccessIdentityPath,
},
}],
enabled: true,
isIpv6Enabled: true,
comment: "Some comment",
defaultRootObject: "index.html",
defaultCacheBehavior: {
cachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad",
allowedMethods: [
"GET",
"HEAD",
"OPTIONS",
],
cachedMethods: [
"GET",
"HEAD",
],
targetOriginId: s3OriginId,
viewerProtocolPolicy: "allow-all",
},
restrictions: {
geoRestriction: {
restrictionType: "whitelist",
locations: [
"US",
"CA",
"GB",
"DE",
],
},
},
viewerCertificate: {
cloudfrontDefaultCertificate: true,
},
});
import pulumi
import pulumi_aws as aws
s3_origin_id = "myS3Origin"
s3_distribution = aws.cloudfront.Distribution("s3_distribution",
origins=[{
"domain_name": primary["bucketRegionalDomainName"],
"origin_id": "myS3Origin",
"s3_origin_config": {
"origin_access_identity": default["cloudfrontAccessIdentityPath"],
},
}],
enabled=True,
is_ipv6_enabled=True,
comment="Some comment",
default_root_object="index.html",
default_cache_behavior={
"cache_policy_id": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad",
"allowed_methods": [
"GET",
"HEAD",
"OPTIONS",
],
"cached_methods": [
"GET",
"HEAD",
],
"target_origin_id": s3_origin_id,
"viewer_protocol_policy": "allow-all",
},
restrictions={
"geo_restriction": {
"restriction_type": "whitelist",
"locations": [
"US",
"CA",
"GB",
"DE",
],
},
},
viewer_certificate={
"cloudfront_default_certificate": True,
})
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 {
s3OriginId := "myS3Origin"
_, err := cloudfront.NewDistribution(ctx, "s3_distribution", &cloudfront.DistributionArgs{
Origins: cloudfront.DistributionOriginArray{
&cloudfront.DistributionOriginArgs{
DomainName: pulumi.Any(primary.BucketRegionalDomainName),
OriginId: pulumi.String("myS3Origin"),
S3OriginConfig: &cloudfront.DistributionOriginS3OriginConfigArgs{
OriginAccessIdentity: pulumi.Any(_default.CloudfrontAccessIdentityPath),
},
},
},
Enabled: pulumi.Bool(true),
IsIpv6Enabled: pulumi.Bool(true),
Comment: pulumi.String("Some comment"),
DefaultRootObject: pulumi.String("index.html"),
DefaultCacheBehavior: &cloudfront.DistributionDefaultCacheBehaviorArgs{
CachePolicyId: pulumi.String("4135ea2d-6df8-44a3-9df3-4b5a84be39ad"),
AllowedMethods: pulumi.StringArray{
pulumi.String("GET"),
pulumi.String("HEAD"),
pulumi.String("OPTIONS"),
},
CachedMethods: pulumi.StringArray{
pulumi.String("GET"),
pulumi.String("HEAD"),
},
TargetOriginId: pulumi.String(s3OriginId),
ViewerProtocolPolicy: pulumi.String("allow-all"),
},
Restrictions: &cloudfront.DistributionRestrictionsArgs{
GeoRestriction: &cloudfront.DistributionRestrictionsGeoRestrictionArgs{
RestrictionType: pulumi.String("whitelist"),
Locations: pulumi.StringArray{
pulumi.String("US"),
pulumi.String("CA"),
pulumi.String("GB"),
pulumi.String("DE"),
},
},
},
ViewerCertificate: &cloudfront.DistributionViewerCertificateArgs{
CloudfrontDefaultCertificate: pulumi.Bool(true),
},
})
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 s3OriginId = "myS3Origin";
var s3Distribution = new Aws.CloudFront.Distribution("s3_distribution", new()
{
Origins = new[]
{
new Aws.CloudFront.Inputs.DistributionOriginArgs
{
DomainName = primary.BucketRegionalDomainName,
OriginId = "myS3Origin",
S3OriginConfig = new Aws.CloudFront.Inputs.DistributionOriginS3OriginConfigArgs
{
OriginAccessIdentity = @default.CloudfrontAccessIdentityPath,
},
},
},
Enabled = true,
IsIpv6Enabled = true,
Comment = "Some comment",
DefaultRootObject = "index.html",
DefaultCacheBehavior = new Aws.CloudFront.Inputs.DistributionDefaultCacheBehaviorArgs
{
CachePolicyId = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad",
AllowedMethods = new[]
{
"GET",
"HEAD",
"OPTIONS",
},
CachedMethods = new[]
{
"GET",
"HEAD",
},
TargetOriginId = s3OriginId,
ViewerProtocolPolicy = "allow-all",
},
Restrictions = new Aws.CloudFront.Inputs.DistributionRestrictionsArgs
{
GeoRestriction = new Aws.CloudFront.Inputs.DistributionRestrictionsGeoRestrictionArgs
{
RestrictionType = "whitelist",
Locations = new[]
{
"US",
"CA",
"GB",
"DE",
},
},
},
ViewerCertificate = new Aws.CloudFront.Inputs.DistributionViewerCertificateArgs
{
CloudfrontDefaultCertificate = true,
},
});
});
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 com.pulumi.aws.cloudfront.inputs.DistributionDefaultCacheBehaviorArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionRestrictionsArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionRestrictionsGeoRestrictionArgs;
import com.pulumi.aws.cloudfront.inputs.DistributionViewerCertificateArgs;
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 s3OriginId = "myS3Origin";
var s3Distribution = new Distribution("s3Distribution", DistributionArgs.builder()
.origins(DistributionOriginArgs.builder()
.domainName(primary.bucketRegionalDomainName())
.originId("myS3Origin")
.s3OriginConfig(DistributionOriginS3OriginConfigArgs.builder()
.originAccessIdentity(default_.cloudfrontAccessIdentityPath())
.build())
.build())
.enabled(true)
.isIpv6Enabled(true)
.comment("Some comment")
.defaultRootObject("index.html")
.defaultCacheBehavior(DistributionDefaultCacheBehaviorArgs.builder()
.cachePolicyId("4135ea2d-6df8-44a3-9df3-4b5a84be39ad")
.allowedMethods(
"GET",
"HEAD",
"OPTIONS")
.cachedMethods(
"GET",
"HEAD")
.targetOriginId(s3OriginId)
.viewerProtocolPolicy("allow-all")
.build())
.restrictions(DistributionRestrictionsArgs.builder()
.geoRestriction(DistributionRestrictionsGeoRestrictionArgs.builder()
.restrictionType("whitelist")
.locations(
"US",
"CA",
"GB",
"DE")
.build())
.build())
.viewerCertificate(DistributionViewerCertificateArgs.builder()
.cloudfrontDefaultCertificate(true)
.build())
.build());
}
}
resources:
s3Distribution:
type: aws:cloudfront:Distribution
name: s3_distribution
properties:
origins:
- domainName: ${primary.bucketRegionalDomainName}
originId: myS3Origin
s3OriginConfig:
originAccessIdentity: ${default.cloudfrontAccessIdentityPath}
enabled: true
isIpv6Enabled: true
comment: Some comment
defaultRootObject: index.html
defaultCacheBehavior:
cachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
allowedMethods:
- GET
- HEAD
- OPTIONS
cachedMethods:
- GET
- HEAD
targetOriginId: ${s3OriginId}
viewerProtocolPolicy: allow-all
restrictions:
geoRestriction:
restrictionType: whitelist
locations:
- US
- CA
- GB
- DE
viewerCertificate:
cloudfrontDefaultCertificate: true
variables:
s3OriginId: myS3Origin
The cachePolicyId property references an AWS-managed policy by ID. This replaces the forwardedValues configuration from earlier examples. Managed policies handle query strings, headers, and cookies according to AWS best practices. The example uses policy ID “4135ea2d-6df8-44a3-9df3-4b5a84be39ad” (CachingOptimized).
Send access logs to S3 with standard logging V2
CloudFront’s standard logging V2 delivers access logs to S3 in Parquet format for structured analytics.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.cloudfront.Distribution("example", {});
const exampleLogDeliverySource = new aws.cloudwatch.LogDeliverySource("example", {
region: "us-east-1",
name: "example",
logType: "ACCESS_LOGS",
resourceArn: example.arn,
});
const exampleBucket = new aws.s3.Bucket("example", {
bucket: "testbucket",
forceDestroy: true,
});
const exampleLogDeliveryDestination = new aws.cloudwatch.LogDeliveryDestination("example", {
region: "us-east-1",
name: "s3-destination",
outputFormat: "parquet",
deliveryDestinationConfiguration: {
destinationResourceArn: pulumi.interpolate`${exampleBucket.arn}/prefix`,
},
});
const exampleLogDelivery = new aws.cloudwatch.LogDelivery("example", {
region: "us-east-1",
deliverySourceName: exampleLogDeliverySource.name,
deliveryDestinationArn: exampleLogDeliveryDestination.arn,
s3DeliveryConfigurations: [{
suffixPath: "/123456678910/{DistributionId}/{yyyy}/{MM}/{dd}/{HH}",
}],
});
import pulumi
import pulumi_aws as aws
example = aws.cloudfront.Distribution("example")
example_log_delivery_source = aws.cloudwatch.LogDeliverySource("example",
region="us-east-1",
name="example",
log_type="ACCESS_LOGS",
resource_arn=example.arn)
example_bucket = aws.s3.Bucket("example",
bucket="testbucket",
force_destroy=True)
example_log_delivery_destination = aws.cloudwatch.LogDeliveryDestination("example",
region="us-east-1",
name="s3-destination",
output_format="parquet",
delivery_destination_configuration={
"destination_resource_arn": example_bucket.arn.apply(lambda arn: f"{arn}/prefix"),
})
example_log_delivery = aws.cloudwatch.LogDelivery("example",
region="us-east-1",
delivery_source_name=example_log_delivery_source.name,
delivery_destination_arn=example_log_delivery_destination.arn,
s3_delivery_configurations=[{
"suffix_path": "/123456678910/{DistributionId}/{yyyy}/{MM}/{dd}/{HH}",
}])
package main
import (
"fmt"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudfront"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudwatch"
"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 := cloudfront.NewDistribution(ctx, "example", nil)
if err != nil {
return err
}
exampleLogDeliverySource, err := cloudwatch.NewLogDeliverySource(ctx, "example", &cloudwatch.LogDeliverySourceArgs{
Region: pulumi.String("us-east-1"),
Name: pulumi.String("example"),
LogType: pulumi.String("ACCESS_LOGS"),
ResourceArn: example.Arn,
})
if err != nil {
return err
}
exampleBucket, err := s3.NewBucket(ctx, "example", &s3.BucketArgs{
Bucket: pulumi.String("testbucket"),
ForceDestroy: pulumi.Bool(true),
})
if err != nil {
return err
}
exampleLogDeliveryDestination, err := cloudwatch.NewLogDeliveryDestination(ctx, "example", &cloudwatch.LogDeliveryDestinationArgs{
Region: pulumi.String("us-east-1"),
Name: pulumi.String("s3-destination"),
OutputFormat: pulumi.String("parquet"),
DeliveryDestinationConfiguration: &cloudwatch.LogDeliveryDestinationDeliveryDestinationConfigurationArgs{
DestinationResourceArn: exampleBucket.Arn.ApplyT(func(arn string) (string, error) {
return fmt.Sprintf("%v/prefix", arn), nil
}).(pulumi.StringOutput),
},
})
if err != nil {
return err
}
_, err = cloudwatch.NewLogDelivery(ctx, "example", &cloudwatch.LogDeliveryArgs{
Region: pulumi.String("us-east-1"),
DeliverySourceName: exampleLogDeliverySource.Name,
DeliveryDestinationArn: exampleLogDeliveryDestination.Arn,
S3DeliveryConfigurations: cloudwatch.LogDeliveryS3DeliveryConfigurationArray{
&cloudwatch.LogDeliveryS3DeliveryConfigurationArgs{
SuffixPath: pulumi.String("/123456678910/{DistributionId}/{yyyy}/{MM}/{dd}/{HH}"),
},
},
})
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");
var exampleLogDeliverySource = new Aws.CloudWatch.LogDeliverySource("example", new()
{
Region = "us-east-1",
Name = "example",
LogType = "ACCESS_LOGS",
ResourceArn = example.Arn,
});
var exampleBucket = new Aws.S3.Bucket("example", new()
{
BucketName = "testbucket",
ForceDestroy = true,
});
var exampleLogDeliveryDestination = new Aws.CloudWatch.LogDeliveryDestination("example", new()
{
Region = "us-east-1",
Name = "s3-destination",
OutputFormat = "parquet",
DeliveryDestinationConfiguration = new Aws.CloudWatch.Inputs.LogDeliveryDestinationDeliveryDestinationConfigurationArgs
{
DestinationResourceArn = exampleBucket.Arn.Apply(arn => $"{arn}/prefix"),
},
});
var exampleLogDelivery = new Aws.CloudWatch.LogDelivery("example", new()
{
Region = "us-east-1",
DeliverySourceName = exampleLogDeliverySource.Name,
DeliveryDestinationArn = exampleLogDeliveryDestination.Arn,
S3DeliveryConfigurations = new[]
{
new Aws.CloudWatch.Inputs.LogDeliveryS3DeliveryConfigurationArgs
{
SuffixPath = "/123456678910/{DistributionId}/{yyyy}/{MM}/{dd}/{HH}",
},
},
});
});
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.cloudwatch.LogDeliverySource;
import com.pulumi.aws.cloudwatch.LogDeliverySourceArgs;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.cloudwatch.LogDeliveryDestination;
import com.pulumi.aws.cloudwatch.LogDeliveryDestinationArgs;
import com.pulumi.aws.cloudwatch.inputs.LogDeliveryDestinationDeliveryDestinationConfigurationArgs;
import com.pulumi.aws.cloudwatch.LogDelivery;
import com.pulumi.aws.cloudwatch.LogDeliveryArgs;
import com.pulumi.aws.cloudwatch.inputs.LogDeliveryS3DeliveryConfigurationArgs;
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");
var exampleLogDeliverySource = new LogDeliverySource("exampleLogDeliverySource", LogDeliverySourceArgs.builder()
.region("us-east-1")
.name("example")
.logType("ACCESS_LOGS")
.resourceArn(example.arn())
.build());
var exampleBucket = new Bucket("exampleBucket", BucketArgs.builder()
.bucket("testbucket")
.forceDestroy(true)
.build());
var exampleLogDeliveryDestination = new LogDeliveryDestination("exampleLogDeliveryDestination", LogDeliveryDestinationArgs.builder()
.region("us-east-1")
.name("s3-destination")
.outputFormat("parquet")
.deliveryDestinationConfiguration(LogDeliveryDestinationDeliveryDestinationConfigurationArgs.builder()
.destinationResourceArn(exampleBucket.arn().applyValue(_arn -> String.format("%s/prefix", _arn)))
.build())
.build());
var exampleLogDelivery = new LogDelivery("exampleLogDelivery", LogDeliveryArgs.builder()
.region("us-east-1")
.deliverySourceName(exampleLogDeliverySource.name())
.deliveryDestinationArn(exampleLogDeliveryDestination.arn())
.s3DeliveryConfigurations(LogDeliveryS3DeliveryConfigurationArgs.builder()
.suffixPath("/123456678910/{DistributionId}/{yyyy}/{MM}/{dd}/{HH}")
.build())
.build());
}
}
resources:
example:
type: aws:cloudfront:Distribution
exampleLogDeliverySource:
type: aws:cloudwatch:LogDeliverySource
name: example
properties:
region: us-east-1
name: example
logType: ACCESS_LOGS
resourceArn: ${example.arn}
exampleBucket:
type: aws:s3:Bucket
name: example
properties:
bucket: testbucket
forceDestroy: true
exampleLogDeliveryDestination:
type: aws:cloudwatch:LogDeliveryDestination
name: example
properties:
region: us-east-1
name: s3-destination
outputFormat: parquet
deliveryDestinationConfiguration:
destinationResourceArn: ${exampleBucket.arn}/prefix
exampleLogDelivery:
type: aws:cloudwatch:LogDelivery
name: example
properties:
region: us-east-1
deliverySourceName: ${exampleLogDeliverySource.name}
deliveryDestinationArn: ${exampleLogDeliveryDestination.arn}
s3DeliveryConfigurations:
- suffixPath: /123456678910/{DistributionId}/{yyyy}/{MM}/{dd}/{HH}
The LogDeliverySource identifies the distribution as a log source. The LogDeliveryDestination configures S3 as the target, with outputFormat set to “parquet” for efficient querying. The LogDelivery resource connects source to destination, with s3DeliveryConfigurations defining the path structure for log files. This is CloudFront’s newer logging approach; the loggingConfig property configures legacy logging.
Stream access logs to Kinesis Data Firehose
For real-time processing, CloudFront can stream access logs to Kinesis Data Firehose.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.cloudfront.Distribution("example", {});
const cloudfrontLogs = new aws.kinesis.FirehoseDeliveryStream("cloudfront_logs", {
region: "us-east-1",
tags: {
LogDeliveryEnabled: "true",
},
});
const exampleLogDeliverySource = new aws.cloudwatch.LogDeliverySource("example", {
region: "us-east-1",
name: "cloudfront-logs-source",
logType: "ACCESS_LOGS",
resourceArn: example.arn,
});
const exampleLogDeliveryDestination = new aws.cloudwatch.LogDeliveryDestination("example", {
region: "us-east-1",
name: "firehose-destination",
outputFormat: "json",
deliveryDestinationConfiguration: {
destinationResourceArn: cloudfrontLogs.arn,
},
});
const exampleLogDelivery = new aws.cloudwatch.LogDelivery("example", {
region: "us-east-1",
deliverySourceName: exampleLogDeliverySource.name,
deliveryDestinationArn: exampleLogDeliveryDestination.arn,
});
import pulumi
import pulumi_aws as aws
example = aws.cloudfront.Distribution("example")
cloudfront_logs = aws.kinesis.FirehoseDeliveryStream("cloudfront_logs",
region="us-east-1",
tags={
"LogDeliveryEnabled": "true",
})
example_log_delivery_source = aws.cloudwatch.LogDeliverySource("example",
region="us-east-1",
name="cloudfront-logs-source",
log_type="ACCESS_LOGS",
resource_arn=example.arn)
example_log_delivery_destination = aws.cloudwatch.LogDeliveryDestination("example",
region="us-east-1",
name="firehose-destination",
output_format="json",
delivery_destination_configuration={
"destination_resource_arn": cloudfront_logs.arn,
})
example_log_delivery = aws.cloudwatch.LogDelivery("example",
region="us-east-1",
delivery_source_name=example_log_delivery_source.name,
delivery_destination_arn=example_log_delivery_destination.arn)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudfront"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudwatch"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/kinesis"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
example, err := cloudfront.NewDistribution(ctx, "example", nil)
if err != nil {
return err
}
cloudfrontLogs, err := kinesis.NewFirehoseDeliveryStream(ctx, "cloudfront_logs", &kinesis.FirehoseDeliveryStreamArgs{
Region: pulumi.String("us-east-1"),
Tags: pulumi.StringMap{
"LogDeliveryEnabled": pulumi.String("true"),
},
})
if err != nil {
return err
}
exampleLogDeliverySource, err := cloudwatch.NewLogDeliverySource(ctx, "example", &cloudwatch.LogDeliverySourceArgs{
Region: pulumi.String("us-east-1"),
Name: pulumi.String("cloudfront-logs-source"),
LogType: pulumi.String("ACCESS_LOGS"),
ResourceArn: example.Arn,
})
if err != nil {
return err
}
exampleLogDeliveryDestination, err := cloudwatch.NewLogDeliveryDestination(ctx, "example", &cloudwatch.LogDeliveryDestinationArgs{
Region: pulumi.String("us-east-1"),
Name: pulumi.String("firehose-destination"),
OutputFormat: pulumi.String("json"),
DeliveryDestinationConfiguration: &cloudwatch.LogDeliveryDestinationDeliveryDestinationConfigurationArgs{
DestinationResourceArn: cloudfrontLogs.Arn,
},
})
if err != nil {
return err
}
_, err = cloudwatch.NewLogDelivery(ctx, "example", &cloudwatch.LogDeliveryArgs{
Region: pulumi.String("us-east-1"),
DeliverySourceName: exampleLogDeliverySource.Name,
DeliveryDestinationArn: exampleLogDeliveryDestination.Arn,
})
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");
var cloudfrontLogs = new Aws.Kinesis.FirehoseDeliveryStream("cloudfront_logs", new()
{
Region = "us-east-1",
Tags =
{
{ "LogDeliveryEnabled", "true" },
},
});
var exampleLogDeliverySource = new Aws.CloudWatch.LogDeliverySource("example", new()
{
Region = "us-east-1",
Name = "cloudfront-logs-source",
LogType = "ACCESS_LOGS",
ResourceArn = example.Arn,
});
var exampleLogDeliveryDestination = new Aws.CloudWatch.LogDeliveryDestination("example", new()
{
Region = "us-east-1",
Name = "firehose-destination",
OutputFormat = "json",
DeliveryDestinationConfiguration = new Aws.CloudWatch.Inputs.LogDeliveryDestinationDeliveryDestinationConfigurationArgs
{
DestinationResourceArn = cloudfrontLogs.Arn,
},
});
var exampleLogDelivery = new Aws.CloudWatch.LogDelivery("example", new()
{
Region = "us-east-1",
DeliverySourceName = exampleLogDeliverySource.Name,
DeliveryDestinationArn = exampleLogDeliveryDestination.Arn,
});
});
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.kinesis.FirehoseDeliveryStream;
import com.pulumi.aws.kinesis.FirehoseDeliveryStreamArgs;
import com.pulumi.aws.cloudwatch.LogDeliverySource;
import com.pulumi.aws.cloudwatch.LogDeliverySourceArgs;
import com.pulumi.aws.cloudwatch.LogDeliveryDestination;
import com.pulumi.aws.cloudwatch.LogDeliveryDestinationArgs;
import com.pulumi.aws.cloudwatch.inputs.LogDeliveryDestinationDeliveryDestinationConfigurationArgs;
import com.pulumi.aws.cloudwatch.LogDelivery;
import com.pulumi.aws.cloudwatch.LogDeliveryArgs;
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");
var cloudfrontLogs = new FirehoseDeliveryStream("cloudfrontLogs", FirehoseDeliveryStreamArgs.builder()
.region("us-east-1")
.tags(Map.of("LogDeliveryEnabled", "true"))
.build());
var exampleLogDeliverySource = new LogDeliverySource("exampleLogDeliverySource", LogDeliverySourceArgs.builder()
.region("us-east-1")
.name("cloudfront-logs-source")
.logType("ACCESS_LOGS")
.resourceArn(example.arn())
.build());
var exampleLogDeliveryDestination = new LogDeliveryDestination("exampleLogDeliveryDestination", LogDeliveryDestinationArgs.builder()
.region("us-east-1")
.name("firehose-destination")
.outputFormat("json")
.deliveryDestinationConfiguration(LogDeliveryDestinationDeliveryDestinationConfigurationArgs.builder()
.destinationResourceArn(cloudfrontLogs.arn())
.build())
.build());
var exampleLogDelivery = new LogDelivery("exampleLogDelivery", LogDeliveryArgs.builder()
.region("us-east-1")
.deliverySourceName(exampleLogDeliverySource.name())
.deliveryDestinationArn(exampleLogDeliveryDestination.arn())
.build());
}
}
resources:
example:
type: aws:cloudfront:Distribution
cloudfrontLogs:
type: aws:kinesis:FirehoseDeliveryStream
name: cloudfront_logs
properties:
region: us-east-1
tags:
LogDeliveryEnabled: 'true'
exampleLogDeliverySource:
type: aws:cloudwatch:LogDeliverySource
name: example
properties:
region: us-east-1
name: cloudfront-logs-source
logType: ACCESS_LOGS
resourceArn: ${example.arn}
exampleLogDeliveryDestination:
type: aws:cloudwatch:LogDeliveryDestination
name: example
properties:
region: us-east-1
name: firehose-destination
outputFormat: json
deliveryDestinationConfiguration:
destinationResourceArn: ${cloudfrontLogs.arn}
exampleLogDelivery:
type: aws:cloudwatch:LogDelivery
name: example
properties:
region: us-east-1
deliverySourceName: ${exampleLogDeliverySource.name}
deliveryDestinationArn: ${exampleLogDeliveryDestination.arn}
The LogDeliveryDestination points to a Firehose stream instead of S3, with outputFormat set to “json” for streaming. The Firehose stream must have the tag LogDeliveryEnabled set to “true”. From Firehose, logs can flow to S3, Redshift, Elasticsearch, or custom processors.
Beyond these examples
These snippets focus on specific distribution-level features: S3 origin configuration with custom domains, origin failover and managed caching policies, and standard logging V2 to S3 and Firehose. They’re intentionally minimal rather than full CDN deployments.
The examples may reference pre-existing infrastructure such as S3 buckets, ACM certificates, Route53 hosted zones, origin access identities or origin access controls, and Kinesis Firehose streams for logging. They focus on configuring the distribution rather than provisioning everything around it.
To keep things focused, common distribution patterns are omitted, including:
- Path-based cache behaviors (orderedCacheBehaviors)
- Custom error responses and WAF integration
- Geographic restrictions and price class selection
- Lambda@Edge function associations
These omissions are intentional: the goal is to illustrate how each distribution feature is wired, not provide drop-in CDN modules. See the CloudFront Distribution resource reference for all available configuration options.
Let's create AWS CloudFront Distributions
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Deployment & Lifecycle
retainOnDelete to true to disable the distribution instead of deleting it. Alternatively, set waitForDeployment to false to skip waiting for deployment completion (default is true).staging property is immutable and cannot be changed after distribution creation.Caching & Behaviors
cachePolicyId (preferred). The forwardedValues option is deprecated. The defaultCacheBehavior requires either cachePolicyId or forwardedValues to be set.http1.1, http2, http2and3, and http3. The default is http2.Origins & Access Control
aws.cloudfront.OriginAccessControl resource, reference it in originAccessControlId, and add an S3 bucket policy allowing the CloudFront service principal with a condition on AWS:SourceArn matching your distribution’s ARN.originGroups with failoverCriteria specifying status codes (such as 403, 404, 500, 502) and members pointing to your primary and failover origins.Security & Certificates
us-east-1 region.waf:GetWebACL permissions. For WAFv2, use the ACL ARN; for WAF Classic, use the ACL ID.Logging
loggingConfig property configures legacy version standard logs, while v2 logging uses separate aws.cloudwatch.LogDeliverySource, LogDeliveryDestination, and LogDelivery resources.aws.cloudwatch.LogDeliverySource, LogDeliveryDestination (with outputFormat set to parquet), and LogDelivery resources. Use s3DeliveryConfigurations to customize the delivery path.LogDeliveryEnabled: "true", then create LogDeliverySource, LogDeliveryDestination (with outputFormat set to json), and LogDelivery resources.DNS & Routing
hostedZoneId for all CloudFront distributions is always Z2FDTNDATAQYW2. Use this value when creating Route53 alias records pointing to your distribution’s domainName.Using a different cloud?
Explore networking guides for other cloud providers: