Configure AWS CloudFront Continuous Deployment Policies

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 FREE

Frequently Asked Questions

Setup & Configuration
How do I set up staging and production distributions for continuous deployment?
Create a staging distribution with staging: true, then create a production distribution that references the policy via continuousDeploymentPolicyId.
What are the required properties for a continuous deployment policy?
You must provide enabled (boolean), stagingDistributionDnsNames (the staging distribution’s domain), and trafficConfig (routing parameters).
Can I enable or disable the policy after creation?
Yes, use the enabled property to toggle the continuous deployment policy on or off.
Traffic Routing
What's the difference between SingleWeight and SingleHeader traffic routing?
SingleWeight routes a percentage of traffic to staging (e.g., 1%), while SingleHeader routes based on a specific HTTP header value.
What does the weight value control in SingleWeight config?
The weight is a decimal representing the percentage of traffic routed to staging. For example, 0.01 routes 1% of traffic.
How do I route traffic based on HTTP headers?
Use type: "SingleHeader" in trafficConfig and specify the header name and value in singleHeaderConfig.
Session Stickiness
What's session stickiness and when should I use it?
Session stickiness keeps users on the same distribution (staging or production) for a period of time. Use it to ensure consistent user experience during testing.
What are the TTL settings in sessionStickinessConfig?
idleTtl sets the idle timeout (300 seconds in the example), and maximumTtl sets the maximum session duration (600 seconds in the example).
Advanced
How do I import an existing continuous deployment policy?
Use 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: