The aws:cloudfront/continuousDeploymentPolicy:ContinuousDeploymentPolicy resource, part of the Pulumi AWS provider, defines how CloudFront routes production traffic between primary and staging distributions for safe configuration testing. This guide focuses on three capabilities: percentage-based traffic splitting, session stickiness for consistent user experience, and header-based routing for targeted testing.
Continuous deployment policies connect a production distribution to a staging distribution, both of which must exist before creating the policy. The examples are intentionally small. Combine them with your own CloudFront distributions and monitoring.
Route a percentage of traffic to staging
Teams testing configuration changes start by routing a small percentage of production traffic to staging to validate behavior before full rollout.
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 a request arrives at the production distribution, CloudFront uses the policy to decide whether to serve from production or staging. The weight property (0.01 = 1%) controls what fraction goes to staging. The production distribution references the policy via continuousDeploymentPolicyId, and the policy points to the staging distribution via stagingDistributionDnsNames. The type property set to “SingleWeight” indicates percentage-based routing.
Maintain user sessions during traffic shifting
Applications with user sessions need to ensure that once a user is routed to staging, subsequent requests continue to the same distribution.
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
Session stickiness keeps users on the same distribution for a consistent experience. The sessionStickinessConfig defines how long CloudFront maintains the routing decision: idleTtl sets the timeout for inactive sessions (300 seconds), and maximumTtl caps the total session duration (600 seconds). This extends the basic weight-based routing from the previous example with session persistence.
Route traffic based on request headers
Testing teams often route specific requests to staging based on header values, enabling controlled testing without affecting general traffic.
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
Header-based routing directs requests with specific headers to staging for targeted testing. The type property switches to “SingleHeader”, and singleHeaderConfig specifies which header name and value trigger staging routing. Requests with the header “aws-cf-cd-example: example” go to staging; all others go to production. This provides an alternative to percentage-based routing when you need precise control over which requests reach staging.
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 workflows.
The examples reference pre-existing infrastructure such as staging CloudFront distributions (marked with staging: true) and production CloudFront distributions. They focus on configuring the policy rather than provisioning the distributions themselves.
To keep things focused, common policy patterns are omitted, including:
- Policy lifecycle management (enabling/disabling)
- Multiple header conditions
- Traffic weight adjustments over time
- Rollback strategies
These omissions are intentional: the goal is to illustrate how each policy feature is wired, not provide drop-in deployment modules. 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 policy via continuousDeploymentPolicyId.enabled (boolean), stagingDistributionDnsNames (the staging distribution’s domain), and trafficConfig (routing parameters).enabled property to toggle the continuous deployment policy on or off.Traffic Routing
SingleWeight routes a percentage of traffic to staging (e.g., 1%), while SingleHeader routes based on a specific HTTP header value.weight is a decimal representing the percentage of traffic routed to staging. For example, 0.01 routes 1% of traffic.type: "SingleHeader" in trafficConfig and specify the header name and value in singleHeaderConfig.Session Stickiness
idleTtl sets the idle timeout (300 seconds in the example), and maximumTtl sets the maximum session duration (600 seconds in the example).Advanced
pulumi import aws:cloudfront/continuousDeploymentPolicy:ContinuousDeploymentPolicy example <policy-id> with the policy’s ID.Using a different cloud?
Explore networking guides for other cloud providers: