The aws:cloudfront/continuousDeploymentPolicy:ContinuousDeploymentPolicy resource, part of the Pulumi AWS provider, defines how CloudFront routes production traffic between primary and staging distributions for gradual rollouts. This guide focuses on three capabilities: percentage-based traffic splitting, session stickiness for consistent user experience, and header-based routing for selective testing.
Continuous deployment policies require both a staging distribution (marked with staging: true) and a production distribution that references the policy ID. The examples are intentionally small. Combine them with your own CloudFront distributions and monitoring infrastructure.
Route a percentage of traffic to staging
Teams testing CDN changes often route a small percentage of production traffic to staging to validate behavior under real load.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const staging = new aws.cloudfront.Distribution("staging", {
enabled: true,
staging: true,
});
const example = new aws.cloudfront.ContinuousDeploymentPolicy("example", {
enabled: true,
stagingDistributionDnsNames: {
items: [staging.domainName],
quantity: 1,
},
trafficConfig: {
type: "SingleWeight",
singleWeightConfig: {
weight: 0.01,
},
},
});
const production = new aws.cloudfront.Distribution("production", {
enabled: true,
continuousDeploymentPolicyId: example.id,
});
import pulumi
import pulumi_aws as aws
staging = aws.cloudfront.Distribution("staging",
enabled=True,
staging=True)
example = aws.cloudfront.ContinuousDeploymentPolicy("example",
enabled=True,
staging_distribution_dns_names={
"items": [staging.domain_name],
"quantity": 1,
},
traffic_config={
"type": "SingleWeight",
"single_weight_config": {
"weight": 0.01,
},
})
production = aws.cloudfront.Distribution("production",
enabled=True,
continuous_deployment_policy_id=example.id)
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 {
staging, err := cloudfront.NewDistribution(ctx, "staging", &cloudfront.DistributionArgs{
Enabled: pulumi.Bool(true),
Staging: pulumi.Bool(true),
})
if err != nil {
return err
}
example, err := cloudfront.NewContinuousDeploymentPolicy(ctx, "example", &cloudfront.ContinuousDeploymentPolicyArgs{
Enabled: pulumi.Bool(true),
StagingDistributionDnsNames: &cloudfront.ContinuousDeploymentPolicyStagingDistributionDnsNamesArgs{
Items: pulumi.StringArray{
staging.DomainName,
},
Quantity: pulumi.Int(1),
},
TrafficConfig: &cloudfront.ContinuousDeploymentPolicyTrafficConfigArgs{
Type: pulumi.String("SingleWeight"),
SingleWeightConfig: &cloudfront.ContinuousDeploymentPolicyTrafficConfigSingleWeightConfigArgs{
Weight: pulumi.Float64(0.01),
},
},
})
if err != nil {
return err
}
_, err = cloudfront.NewDistribution(ctx, "production", &cloudfront.DistributionArgs{
Enabled: pulumi.Bool(true),
ContinuousDeploymentPolicyId: example.ID(),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var staging = new Aws.CloudFront.Distribution("staging", new()
{
Enabled = true,
Staging = true,
});
var example = new Aws.CloudFront.ContinuousDeploymentPolicy("example", new()
{
Enabled = true,
StagingDistributionDnsNames = new Aws.CloudFront.Inputs.ContinuousDeploymentPolicyStagingDistributionDnsNamesArgs
{
Items = new[]
{
staging.DomainName,
},
Quantity = 1,
},
TrafficConfig = new Aws.CloudFront.Inputs.ContinuousDeploymentPolicyTrafficConfigArgs
{
Type = "SingleWeight",
SingleWeightConfig = new Aws.CloudFront.Inputs.ContinuousDeploymentPolicyTrafficConfigSingleWeightConfigArgs
{
Weight = 0.01,
},
},
});
var production = new Aws.CloudFront.Distribution("production", new()
{
Enabled = true,
ContinuousDeploymentPolicyId = example.Id,
});
});
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.ContinuousDeploymentPolicy;
import com.pulumi.aws.cloudfront.ContinuousDeploymentPolicyArgs;
import com.pulumi.aws.cloudfront.inputs.ContinuousDeploymentPolicyStagingDistributionDnsNamesArgs;
import com.pulumi.aws.cloudfront.inputs.ContinuousDeploymentPolicyTrafficConfigArgs;
import com.pulumi.aws.cloudfront.inputs.ContinuousDeploymentPolicyTrafficConfigSingleWeightConfigArgs;
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 staging = new Distribution("staging", DistributionArgs.builder()
.enabled(true)
.staging(true)
.build());
var example = new ContinuousDeploymentPolicy("example", ContinuousDeploymentPolicyArgs.builder()
.enabled(true)
.stagingDistributionDnsNames(ContinuousDeploymentPolicyStagingDistributionDnsNamesArgs.builder()
.items(staging.domainName())
.quantity(1)
.build())
.trafficConfig(ContinuousDeploymentPolicyTrafficConfigArgs.builder()
.type("SingleWeight")
.singleWeightConfig(ContinuousDeploymentPolicyTrafficConfigSingleWeightConfigArgs.builder()
.weight(0.01)
.build())
.build())
.build());
var production = new Distribution("production", DistributionArgs.builder()
.enabled(true)
.continuousDeploymentPolicyId(example.id())
.build());
}
}
resources:
staging:
type: aws:cloudfront:Distribution
properties:
enabled: true
staging: true # ... other configuration ...
example:
type: aws:cloudfront:ContinuousDeploymentPolicy
properties:
enabled: true
stagingDistributionDnsNames:
items:
- ${staging.domainName}
quantity: 1
trafficConfig:
type: SingleWeight
singleWeightConfig:
weight: '0.01'
production:
type: aws:cloudfront:Distribution
properties:
enabled: true # NOTE: A continuous deployment policy cannot be associated to distribution
# # on creation. Set this argument once the resource exists.
continuousDeploymentPolicyId: ${example.id}
When the policy is enabled, CloudFront splits traffic according to the weight property (0.01 = 1% of requests). The staging distribution must have staging: true set, and the production distribution references the policy via continuousDeploymentPolicyId. The trafficConfig type of “SingleWeight” tells CloudFront to use percentage-based splitting rather than header-based routing.
Maintain user sessions during traffic splitting
Applications with user sessions need to ensure users stay on the same distribution throughout their session to avoid inconsistent experiences.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.cloudfront.ContinuousDeploymentPolicy("example", {
enabled: true,
stagingDistributionDnsNames: {
items: [staging.domainName],
quantity: 1,
},
trafficConfig: {
type: "SingleWeight",
singleWeightConfig: {
weight: 0.01,
sessionStickinessConfig: {
idleTtl: 300,
maximumTtl: 600,
},
},
},
});
import pulumi
import pulumi_aws as aws
example = aws.cloudfront.ContinuousDeploymentPolicy("example",
enabled=True,
staging_distribution_dns_names={
"items": [staging["domainName"]],
"quantity": 1,
},
traffic_config={
"type": "SingleWeight",
"single_weight_config": {
"weight": 0.01,
"session_stickiness_config": {
"idle_ttl": 300,
"maximum_ttl": 600,
},
},
})
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.NewContinuousDeploymentPolicy(ctx, "example", &cloudfront.ContinuousDeploymentPolicyArgs{
Enabled: pulumi.Bool(true),
StagingDistributionDnsNames: &cloudfront.ContinuousDeploymentPolicyStagingDistributionDnsNamesArgs{
Items: pulumi.StringArray{
staging.DomainName,
},
Quantity: pulumi.Int(1),
},
TrafficConfig: &cloudfront.ContinuousDeploymentPolicyTrafficConfigArgs{
Type: pulumi.String("SingleWeight"),
SingleWeightConfig: &cloudfront.ContinuousDeploymentPolicyTrafficConfigSingleWeightConfigArgs{
Weight: pulumi.Float64(0.01),
SessionStickinessConfig: &cloudfront.ContinuousDeploymentPolicyTrafficConfigSingleWeightConfigSessionStickinessConfigArgs{
IdleTtl: pulumi.Int(300),
MaximumTtl: pulumi.Int(600),
},
},
},
})
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.ContinuousDeploymentPolicy("example", new()
{
Enabled = true,
StagingDistributionDnsNames = new Aws.CloudFront.Inputs.ContinuousDeploymentPolicyStagingDistributionDnsNamesArgs
{
Items = new[]
{
staging.DomainName,
},
Quantity = 1,
},
TrafficConfig = new Aws.CloudFront.Inputs.ContinuousDeploymentPolicyTrafficConfigArgs
{
Type = "SingleWeight",
SingleWeightConfig = new Aws.CloudFront.Inputs.ContinuousDeploymentPolicyTrafficConfigSingleWeightConfigArgs
{
Weight = 0.01,
SessionStickinessConfig = new Aws.CloudFront.Inputs.ContinuousDeploymentPolicyTrafficConfigSingleWeightConfigSessionStickinessConfigArgs
{
IdleTtl = 300,
MaximumTtl = 600,
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudfront.ContinuousDeploymentPolicy;
import com.pulumi.aws.cloudfront.ContinuousDeploymentPolicyArgs;
import com.pulumi.aws.cloudfront.inputs.ContinuousDeploymentPolicyStagingDistributionDnsNamesArgs;
import com.pulumi.aws.cloudfront.inputs.ContinuousDeploymentPolicyTrafficConfigArgs;
import com.pulumi.aws.cloudfront.inputs.ContinuousDeploymentPolicyTrafficConfigSingleWeightConfigArgs;
import com.pulumi.aws.cloudfront.inputs.ContinuousDeploymentPolicyTrafficConfigSingleWeightConfigSessionStickinessConfigArgs;
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 ContinuousDeploymentPolicy("example", ContinuousDeploymentPolicyArgs.builder()
.enabled(true)
.stagingDistributionDnsNames(ContinuousDeploymentPolicyStagingDistributionDnsNamesArgs.builder()
.items(staging.domainName())
.quantity(1)
.build())
.trafficConfig(ContinuousDeploymentPolicyTrafficConfigArgs.builder()
.type("SingleWeight")
.singleWeightConfig(ContinuousDeploymentPolicyTrafficConfigSingleWeightConfigArgs.builder()
.weight(0.01)
.sessionStickinessConfig(ContinuousDeploymentPolicyTrafficConfigSingleWeightConfigSessionStickinessConfigArgs.builder()
.idleTtl(300)
.maximumTtl(600)
.build())
.build())
.build())
.build());
}
}
resources:
example:
type: aws:cloudfront:ContinuousDeploymentPolicy
properties:
enabled: true
stagingDistributionDnsNames:
items:
- ${staging.domainName}
quantity: 1
trafficConfig:
type: SingleWeight
singleWeightConfig:
weight: '0.01'
sessionStickinessConfig:
idleTtl: 300
maximumTtl: 600
The sessionStickinessConfig controls how long CloudFront keeps a user on the same distribution. The idleTtl sets the maximum idle time (300 seconds), while maximumTtl caps the total session duration (600 seconds). Once a user is routed to staging, they continue hitting staging until their session expires.
Route traffic based on request headers
Development teams often test staging distributions without affecting regular users by routing only requests with specific headers.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.cloudfront.ContinuousDeploymentPolicy("example", {
enabled: true,
stagingDistributionDnsNames: {
items: [staging.domainName],
quantity: 1,
},
trafficConfig: {
type: "SingleHeader",
singleHeaderConfig: {
header: "aws-cf-cd-example",
value: "example",
},
},
});
import pulumi
import pulumi_aws as aws
example = aws.cloudfront.ContinuousDeploymentPolicy("example",
enabled=True,
staging_distribution_dns_names={
"items": [staging["domainName"]],
"quantity": 1,
},
traffic_config={
"type": "SingleHeader",
"single_header_config": {
"header": "aws-cf-cd-example",
"value": "example",
},
})
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.NewContinuousDeploymentPolicy(ctx, "example", &cloudfront.ContinuousDeploymentPolicyArgs{
Enabled: pulumi.Bool(true),
StagingDistributionDnsNames: &cloudfront.ContinuousDeploymentPolicyStagingDistributionDnsNamesArgs{
Items: pulumi.StringArray{
staging.DomainName,
},
Quantity: pulumi.Int(1),
},
TrafficConfig: &cloudfront.ContinuousDeploymentPolicyTrafficConfigArgs{
Type: pulumi.String("SingleHeader"),
SingleHeaderConfig: &cloudfront.ContinuousDeploymentPolicyTrafficConfigSingleHeaderConfigArgs{
Header: pulumi.String("aws-cf-cd-example"),
Value: pulumi.String("example"),
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.CloudFront.ContinuousDeploymentPolicy("example", new()
{
Enabled = true,
StagingDistributionDnsNames = new Aws.CloudFront.Inputs.ContinuousDeploymentPolicyStagingDistributionDnsNamesArgs
{
Items = new[]
{
staging.DomainName,
},
Quantity = 1,
},
TrafficConfig = new Aws.CloudFront.Inputs.ContinuousDeploymentPolicyTrafficConfigArgs
{
Type = "SingleHeader",
SingleHeaderConfig = new Aws.CloudFront.Inputs.ContinuousDeploymentPolicyTrafficConfigSingleHeaderConfigArgs
{
Header = "aws-cf-cd-example",
Value = "example",
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudfront.ContinuousDeploymentPolicy;
import com.pulumi.aws.cloudfront.ContinuousDeploymentPolicyArgs;
import com.pulumi.aws.cloudfront.inputs.ContinuousDeploymentPolicyStagingDistributionDnsNamesArgs;
import com.pulumi.aws.cloudfront.inputs.ContinuousDeploymentPolicyTrafficConfigArgs;
import com.pulumi.aws.cloudfront.inputs.ContinuousDeploymentPolicyTrafficConfigSingleHeaderConfigArgs;
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 ContinuousDeploymentPolicy("example", ContinuousDeploymentPolicyArgs.builder()
.enabled(true)
.stagingDistributionDnsNames(ContinuousDeploymentPolicyStagingDistributionDnsNamesArgs.builder()
.items(staging.domainName())
.quantity(1)
.build())
.trafficConfig(ContinuousDeploymentPolicyTrafficConfigArgs.builder()
.type("SingleHeader")
.singleHeaderConfig(ContinuousDeploymentPolicyTrafficConfigSingleHeaderConfigArgs.builder()
.header("aws-cf-cd-example")
.value("example")
.build())
.build())
.build());
}
}
resources:
example:
type: aws:cloudfront:ContinuousDeploymentPolicy
properties:
enabled: true
stagingDistributionDnsNames:
items:
- ${staging.domainName}
quantity: 1
trafficConfig:
type: SingleHeader
singleHeaderConfig:
header: aws-cf-cd-example
value: example
The singleHeaderConfig routes requests to staging only when they contain the specified header and value. The type property switches from “SingleWeight” to “SingleHeader”, changing the routing strategy entirely. This enables selective testing: add the header to your requests to hit staging, omit it to hit production.
Beyond these examples
These snippets focus on specific continuous deployment policy features: percentage-based traffic splitting, session stickiness, and header-based routing. They’re intentionally minimal rather than full deployment pipelines.
The examples require pre-existing infrastructure such as a staging CloudFront distribution (marked with staging: true) and a production CloudFront distribution that references the policy. They focus on configuring the policy rather than provisioning the distributions themselves.
To keep things focused, common deployment patterns are omitted, including:
- Policy lifecycle management (enabling/disabling)
- Multiple staging distributions
- Traffic ramp-up strategies
- Monitoring and rollback procedures
These omissions are intentional: the goal is to illustrate how each traffic routing feature is wired, not provide drop-in deployment pipelines. See the CloudFront Continuous Deployment Policy resource reference for all available configuration options.
Let's configure AWS CloudFront Continuous Deployment Policies
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Setup & Configuration
staging: true, then create a production distribution that references the continuous deployment policy via continuousDeploymentPolicyId. The policy connects the two distributions for traffic routing.enabled (boolean), stagingDistributionDnsNames (the staging distribution’s domain), and trafficConfig (routing parameters).Traffic Routing
SingleWeight for percentage-based traffic splitting, and SingleHeader for header-based routing to staging.type: "SingleWeight" in trafficConfig with a weight value between 0 and 1 (e.g., 0.01 routes 1% of traffic to staging).type: "SingleHeader" in trafficConfig with a custom header name and value. Requests matching that header/value pair route to staging.Session Management
sessionStickinessConfig with idleTtl (idle timeout) and maximumTtl (maximum session duration) in seconds.Using a different cloud?
Explore networking guides for other cloud providers: