The aws:cloudfront/distribution:Distribution resource, part of the Pulumi AWS provider, defines a CloudFront distribution that serves content from origins through AWS’s global edge network. This guide focuses on four capabilities: S3 origin configuration with custom domains, origin failover for high availability, AWS-managed caching policies, and V2 logging to S3 and Data Firehose.
Distributions reference S3 buckets, ACM certificates, Route53 zones, and optionally Kinesis Firehose streams or WAF web ACLs. The examples are intentionally small. Combine them with your own origin infrastructure, SSL certificates, and monitoring configuration.
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,
"zone_id": s3_distribution.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 (a label you reference in cache behaviors). The originAccessControlId grants CloudFront permission to read from the bucket. The defaultCacheBehavior routes requests to your origin via targetOriginId. The aliases property lists custom domain names, which require a matching ACM certificate in viewerCertificate. CloudFront takes about 15 minutes to deploy after creation or modification.
Configure origin failover for high availability
When primary origins fail, CloudFront can automatically switch to backup origins based on HTTP status 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 (403, 404, 500, 502) trigger failover. The members array lists origins in priority order: CloudFront tries the first, then falls back to the second if it returns a triggering status code. Your defaultCacheBehavior targets the origin group ID, not individual origins.
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 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. This example uses the “CachingOptimized” policy (ID: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad).
Stream access logs to S3 with V2 logging
CloudFront V2 logging delivers access logs in Parquet format to S3, providing better compression and query performance than legacy logging.
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}
V2 logging requires three resources: a LogDeliverySource that identifies your distribution, a LogDeliveryDestination that specifies the S3 bucket and output format, and a LogDelivery that connects them. The outputFormat property supports “parquet” for efficient storage and querying. The s3DeliveryConfigurations defines the path structure for log files.
Stream access logs to Data Firehose
For real-time log processing, CloudFront can deliver access logs to Kinesis Data Firehose for immediate transformation and routing.
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 configuration mirrors S3 logging but targets a Kinesis Firehose delivery stream instead. The Firehose stream must have the LogDeliveryEnabled tag set to “true”. The outputFormat property supports “json” for Firehose delivery. From Firehose, logs can flow to S3, Redshift, Elasticsearch, or custom HTTP endpoints.
Beyond these examples
These snippets focus on specific distribution-level features: S3 origin configuration with Origin Access Control, origin failover and managed caching policies, and V2 logging to S3 and Data Firehose. They’re intentionally minimal rather than full CDN deployments.
The examples may reference pre-existing infrastructure such as S3 buckets for origins and log storage, ACM certificates and Route53 hosted zones, and IAM roles, Origin Access Identities, or Kinesis Firehose streams. They focus on configuring the distribution rather than provisioning everything around it.
To keep things focused, common distribution patterns are omitted, including:
- Legacy logging configuration (loggingConfig)
- WAF web ACL integration (webAclId)
- Custom error responses and Lambda@Edge functions
- Geographic restrictions and price class optimization
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 it instead of deleting. You can also set waitForDeployment to false to skip waiting for the deployment to complete.staging property is immutable and cannot be changed after distribution creation. Plan carefully whether your distribution should be staging or production before creating it.Cache Behavior & Performance
cachePolicyId (preferred). The forwardedValues argument is deprecated. The defaultCacheBehavior requires either cachePolicyId or forwardedValues, but AWS recommends using managed or custom cache policies via cachePolicyId.http1.1, http2 (default), http2and3, and http3. Configure this using the httpVersion property.Origins & Failover
originAccessControlId in your origin configuration. Then configure your S3 bucket policy to allow the CloudFront service principal (cloudfront.amazonaws.com) with a condition matching your distribution’s ARN.originGroups with failoverCriteria specifying status codes that trigger failover (such as 403, 404, 500, 502) and define primary and failover origin members.Security & Certificates
us-east-1 region, regardless of where your distribution or origins are located.webAclId must exist in the WAF Global (CloudFront) region, and your credentials must have waf:GetWebACL permissions assigned.Logging & Advanced Features
loggingConfig property. V2 logs use CloudWatch LogDelivery resources with destinations in S3 (with parquet output format) or Data Firehose (with json output format). For Firehose, the delivery stream must have the tag LogDeliveryEnabled: "true".continuousDeploymentPolicyId should only be set on production distributions, not staging distributions.Using a different cloud?
Explore networking guides for other cloud providers: