Configure AWS Direct Connect Gateway Associations

The aws:directconnect/gatewayAssociation:GatewayAssociation resource, part of the Pulumi AWS provider, connects a Direct Connect gateway to either a VPN gateway or transit gateway, enabling private connectivity between on-premises networks and AWS VPCs. This guide focuses on three capabilities: VPN gateway associations, transit gateway associations with prefix filtering, and custom CIDR advertisement.

Associations require an existing Direct Connect gateway and either a VPN gateway or transit gateway. Cross-account associations follow a separate proposal workflow not covered here. The examples are intentionally small. Combine them with your own Direct Connect infrastructure and network design.

Connect a Direct Connect gateway to a VPN gateway

Organizations with on-premises data centers use Direct Connect gateways to establish private connectivity to AWS VPCs without traversing the public internet.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.directconnect.Gateway("example", {
    name: "example",
    amazonSideAsn: "64512",
});
const exampleVpc = new aws.ec2.Vpc("example", {cidrBlock: "10.255.255.0/28"});
const exampleVpnGateway = new aws.ec2.VpnGateway("example", {vpcId: exampleVpc.id});
const exampleGatewayAssociation = new aws.directconnect.GatewayAssociation("example", {
    dxGatewayId: example.id,
    associatedGatewayId: exampleVpnGateway.id,
});
import pulumi
import pulumi_aws as aws

example = aws.directconnect.Gateway("example",
    name="example",
    amazon_side_asn="64512")
example_vpc = aws.ec2.Vpc("example", cidr_block="10.255.255.0/28")
example_vpn_gateway = aws.ec2.VpnGateway("example", vpc_id=example_vpc.id)
example_gateway_association = aws.directconnect.GatewayAssociation("example",
    dx_gateway_id=example.id,
    associated_gateway_id=example_vpn_gateway.id)
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/directconnect"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := directconnect.NewGateway(ctx, "example", &directconnect.GatewayArgs{
			Name:          pulumi.String("example"),
			AmazonSideAsn: pulumi.String("64512"),
		})
		if err != nil {
			return err
		}
		exampleVpc, err := ec2.NewVpc(ctx, "example", &ec2.VpcArgs{
			CidrBlock: pulumi.String("10.255.255.0/28"),
		})
		if err != nil {
			return err
		}
		exampleVpnGateway, err := ec2.NewVpnGateway(ctx, "example", &ec2.VpnGatewayArgs{
			VpcId: exampleVpc.ID(),
		})
		if err != nil {
			return err
		}
		_, err = directconnect.NewGatewayAssociation(ctx, "example", &directconnect.GatewayAssociationArgs{
			DxGatewayId:         example.ID(),
			AssociatedGatewayId: exampleVpnGateway.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 example = new Aws.DirectConnect.Gateway("example", new()
    {
        Name = "example",
        AmazonSideAsn = "64512",
    });

    var exampleVpc = new Aws.Ec2.Vpc("example", new()
    {
        CidrBlock = "10.255.255.0/28",
    });

    var exampleVpnGateway = new Aws.Ec2.VpnGateway("example", new()
    {
        VpcId = exampleVpc.Id,
    });

    var exampleGatewayAssociation = new Aws.DirectConnect.GatewayAssociation("example", new()
    {
        DxGatewayId = example.Id,
        AssociatedGatewayId = exampleVpnGateway.Id,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.directconnect.Gateway;
import com.pulumi.aws.directconnect.GatewayArgs;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.VpnGateway;
import com.pulumi.aws.ec2.VpnGatewayArgs;
import com.pulumi.aws.directconnect.GatewayAssociation;
import com.pulumi.aws.directconnect.GatewayAssociationArgs;
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 Gateway("example", GatewayArgs.builder()
            .name("example")
            .amazonSideAsn("64512")
            .build());

        var exampleVpc = new Vpc("exampleVpc", VpcArgs.builder()
            .cidrBlock("10.255.255.0/28")
            .build());

        var exampleVpnGateway = new VpnGateway("exampleVpnGateway", VpnGatewayArgs.builder()
            .vpcId(exampleVpc.id())
            .build());

        var exampleGatewayAssociation = new GatewayAssociation("exampleGatewayAssociation", GatewayAssociationArgs.builder()
            .dxGatewayId(example.id())
            .associatedGatewayId(exampleVpnGateway.id())
            .build());

    }
}
resources:
  example:
    type: aws:directconnect:Gateway
    properties:
      name: example
      amazonSideAsn: '64512'
  exampleVpc:
    type: aws:ec2:Vpc
    name: example
    properties:
      cidrBlock: 10.255.255.0/28
  exampleVpnGateway:
    type: aws:ec2:VpnGateway
    name: example
    properties:
      vpcId: ${exampleVpc.id}
  exampleGatewayAssociation:
    type: aws:directconnect:GatewayAssociation
    name: example
    properties:
      dxGatewayId: ${example.id}
      associatedGatewayId: ${exampleVpnGateway.id}

The dxGatewayId identifies your Direct Connect gateway, while associatedGatewayId points to the VPN gateway attached to your VPC. This creates a single-account association that routes traffic between your on-premises network and the VPC. Without specifying allowedPrefixes, the association advertises the VPC’s entire CIDR block to the Direct Connect gateway.

Connect to a transit gateway with prefix filtering

Transit gateways enable hub-and-spoke network topologies that connect multiple VPCs and on-premises networks through a central routing point.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.directconnect.Gateway("example", {
    name: "example",
    amazonSideAsn: "64512",
});
const exampleTransitGateway = new aws.ec2transitgateway.TransitGateway("example", {});
const exampleGatewayAssociation = new aws.directconnect.GatewayAssociation("example", {
    dxGatewayId: example.id,
    associatedGatewayId: exampleTransitGateway.id,
    allowedPrefixes: [
        "10.255.255.0/30",
        "10.255.255.8/30",
    ],
});
import pulumi
import pulumi_aws as aws

example = aws.directconnect.Gateway("example",
    name="example",
    amazon_side_asn="64512")
example_transit_gateway = aws.ec2transitgateway.TransitGateway("example")
example_gateway_association = aws.directconnect.GatewayAssociation("example",
    dx_gateway_id=example.id,
    associated_gateway_id=example_transit_gateway.id,
    allowed_prefixes=[
        "10.255.255.0/30",
        "10.255.255.8/30",
    ])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/directconnect"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2transitgateway"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := directconnect.NewGateway(ctx, "example", &directconnect.GatewayArgs{
			Name:          pulumi.String("example"),
			AmazonSideAsn: pulumi.String("64512"),
		})
		if err != nil {
			return err
		}
		exampleTransitGateway, err := ec2transitgateway.NewTransitGateway(ctx, "example", nil)
		if err != nil {
			return err
		}
		_, err = directconnect.NewGatewayAssociation(ctx, "example", &directconnect.GatewayAssociationArgs{
			DxGatewayId:         example.ID(),
			AssociatedGatewayId: exampleTransitGateway.ID(),
			AllowedPrefixes: pulumi.StringArray{
				pulumi.String("10.255.255.0/30"),
				pulumi.String("10.255.255.8/30"),
			},
		})
		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.DirectConnect.Gateway("example", new()
    {
        Name = "example",
        AmazonSideAsn = "64512",
    });

    var exampleTransitGateway = new Aws.Ec2TransitGateway.TransitGateway("example");

    var exampleGatewayAssociation = new Aws.DirectConnect.GatewayAssociation("example", new()
    {
        DxGatewayId = example.Id,
        AssociatedGatewayId = exampleTransitGateway.Id,
        AllowedPrefixes = new[]
        {
            "10.255.255.0/30",
            "10.255.255.8/30",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.directconnect.Gateway;
import com.pulumi.aws.directconnect.GatewayArgs;
import com.pulumi.aws.ec2transitgateway.TransitGateway;
import com.pulumi.aws.directconnect.GatewayAssociation;
import com.pulumi.aws.directconnect.GatewayAssociationArgs;
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 Gateway("example", GatewayArgs.builder()
            .name("example")
            .amazonSideAsn("64512")
            .build());

        var exampleTransitGateway = new TransitGateway("exampleTransitGateway");

        var exampleGatewayAssociation = new GatewayAssociation("exampleGatewayAssociation", GatewayAssociationArgs.builder()
            .dxGatewayId(example.id())
            .associatedGatewayId(exampleTransitGateway.id())
            .allowedPrefixes(            
                "10.255.255.0/30",
                "10.255.255.8/30")
            .build());

    }
}
resources:
  example:
    type: aws:directconnect:Gateway
    properties:
      name: example
      amazonSideAsn: '64512'
  exampleTransitGateway:
    type: aws:ec2transitgateway:TransitGateway
    name: example
  exampleGatewayAssociation:
    type: aws:directconnect:GatewayAssociation
    name: example
    properties:
      dxGatewayId: ${example.id}
      associatedGatewayId: ${exampleTransitGateway.id}
      allowedPrefixes:
        - 10.255.255.0/30
        - 10.255.255.8/30

When associating with a transit gateway, the allowedPrefixes property controls which CIDR blocks are advertised to on-premises networks. This example advertises only two /30 subnets rather than the entire VPC range, enabling fine-grained control over which resources are reachable from on-premises.

Some network designs require advertising specific subnets or non-VPC address ranges instead of the default VPC CIDR.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.directconnect.Gateway("example", {
    name: "example",
    amazonSideAsn: "64512",
});
const exampleVpc = new aws.ec2.Vpc("example", {cidrBlock: "10.255.255.0/28"});
const exampleVpnGateway = new aws.ec2.VpnGateway("example", {vpcId: exampleVpc.id});
const exampleGatewayAssociation = new aws.directconnect.GatewayAssociation("example", {
    dxGatewayId: example.id,
    associatedGatewayId: exampleVpnGateway.id,
    allowedPrefixes: [
        "210.52.109.0/24",
        "175.45.176.0/22",
    ],
});
import pulumi
import pulumi_aws as aws

example = aws.directconnect.Gateway("example",
    name="example",
    amazon_side_asn="64512")
example_vpc = aws.ec2.Vpc("example", cidr_block="10.255.255.0/28")
example_vpn_gateway = aws.ec2.VpnGateway("example", vpc_id=example_vpc.id)
example_gateway_association = aws.directconnect.GatewayAssociation("example",
    dx_gateway_id=example.id,
    associated_gateway_id=example_vpn_gateway.id,
    allowed_prefixes=[
        "210.52.109.0/24",
        "175.45.176.0/22",
    ])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/directconnect"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := directconnect.NewGateway(ctx, "example", &directconnect.GatewayArgs{
			Name:          pulumi.String("example"),
			AmazonSideAsn: pulumi.String("64512"),
		})
		if err != nil {
			return err
		}
		exampleVpc, err := ec2.NewVpc(ctx, "example", &ec2.VpcArgs{
			CidrBlock: pulumi.String("10.255.255.0/28"),
		})
		if err != nil {
			return err
		}
		exampleVpnGateway, err := ec2.NewVpnGateway(ctx, "example", &ec2.VpnGatewayArgs{
			VpcId: exampleVpc.ID(),
		})
		if err != nil {
			return err
		}
		_, err = directconnect.NewGatewayAssociation(ctx, "example", &directconnect.GatewayAssociationArgs{
			DxGatewayId:         example.ID(),
			AssociatedGatewayId: exampleVpnGateway.ID(),
			AllowedPrefixes: pulumi.StringArray{
				pulumi.String("210.52.109.0/24"),
				pulumi.String("175.45.176.0/22"),
			},
		})
		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.DirectConnect.Gateway("example", new()
    {
        Name = "example",
        AmazonSideAsn = "64512",
    });

    var exampleVpc = new Aws.Ec2.Vpc("example", new()
    {
        CidrBlock = "10.255.255.0/28",
    });

    var exampleVpnGateway = new Aws.Ec2.VpnGateway("example", new()
    {
        VpcId = exampleVpc.Id,
    });

    var exampleGatewayAssociation = new Aws.DirectConnect.GatewayAssociation("example", new()
    {
        DxGatewayId = example.Id,
        AssociatedGatewayId = exampleVpnGateway.Id,
        AllowedPrefixes = new[]
        {
            "210.52.109.0/24",
            "175.45.176.0/22",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.directconnect.Gateway;
import com.pulumi.aws.directconnect.GatewayArgs;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.VpnGateway;
import com.pulumi.aws.ec2.VpnGatewayArgs;
import com.pulumi.aws.directconnect.GatewayAssociation;
import com.pulumi.aws.directconnect.GatewayAssociationArgs;
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 Gateway("example", GatewayArgs.builder()
            .name("example")
            .amazonSideAsn("64512")
            .build());

        var exampleVpc = new Vpc("exampleVpc", VpcArgs.builder()
            .cidrBlock("10.255.255.0/28")
            .build());

        var exampleVpnGateway = new VpnGateway("exampleVpnGateway", VpnGatewayArgs.builder()
            .vpcId(exampleVpc.id())
            .build());

        var exampleGatewayAssociation = new GatewayAssociation("exampleGatewayAssociation", GatewayAssociationArgs.builder()
            .dxGatewayId(example.id())
            .associatedGatewayId(exampleVpnGateway.id())
            .allowedPrefixes(            
                "210.52.109.0/24",
                "175.45.176.0/22")
            .build());

    }
}
resources:
  example:
    type: aws:directconnect:Gateway
    properties:
      name: example
      amazonSideAsn: '64512'
  exampleVpc:
    type: aws:ec2:Vpc
    name: example
    properties:
      cidrBlock: 10.255.255.0/28
  exampleVpnGateway:
    type: aws:ec2:VpnGateway
    name: example
    properties:
      vpcId: ${exampleVpc.id}
  exampleGatewayAssociation:
    type: aws:directconnect:GatewayAssociation
    name: example
    properties:
      dxGatewayId: ${example.id}
      associatedGatewayId: ${exampleVpnGateway.id}
      allowedPrefixes:
        - 210.52.109.0/24
        - 175.45.176.0/22

The allowedPrefixes property overrides the default behavior of advertising the VPC’s CIDR block. Here, two custom CIDR ranges are advertised to the Direct Connect gateway, which might represent specific application subnets or summarized routes. This configuration is useful when you need to control exactly which IP ranges are visible to on-premises networks.

Beyond these examples

These snippets focus on specific gateway association features: VPN gateway and transit gateway associations, and prefix filtering with allowedPrefixes. They’re intentionally minimal rather than full hybrid network architectures.

The examples may reference pre-existing infrastructure such as Direct Connect gateways (examples create inline, but typically pre-exist), and VPCs and VPN gateways or transit gateways. They focus on configuring the association rather than provisioning the underlying network infrastructure.

To keep things focused, common association patterns are omitted, including:

  • Cross-account associations (proposalId and associatedGatewayOwnerAccountId)
  • Multi-region gateway associations (region property)

These omissions are intentional: the goal is to illustrate how each association feature is wired, not provide drop-in hybrid networking modules. See the Direct Connect Gateway Association resource reference for all available configuration options.

Let's configure AWS Direct Connect Gateway Associations

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Cross-Account & Multi-Region Setup
How do I create a cross-account Direct Connect gateway association?
Cross-account associations require a two-step workflow: First, create a GatewayAssociationProposal in the account that owns the VGW or Transit Gateway. Then, accept the proposal by creating a GatewayAssociation with proposal_id and associated_gateway_owner_account_id in the account that owns the Direct Connect Gateway.
What's the difference between single-account and cross-account associations?
Single-account associations require dx_gateway_id and associated_gateway_id. Cross-account associations require proposal_id and associated_gateway_owner_account_id instead, following a proposal/acceptance workflow.
What do I need to configure if my associated gateway is in a different region?
You must specify a provider alias for the region where the associated gateway resides.
Gateway Types & Configuration
What gateway types can I associate with a Direct Connect gateway?
You can associate either a VPN Gateway (Virtual Private Gateway) or a Transit Gateway by setting associated_gateway_id to the respective gateway ID.
How do I configure allowed prefixes for my association?
Set allowed_prefixes to the list of CIDR blocks you want to advertise. While it defaults to the VPC CIDR, you must explicitly configure it to enable drift detection.
Why should I explicitly set allowed prefixes even though there's a default?
Drift detection only works if allowed_prefixes is explicitly configured. Without explicit configuration, Pulumi can’t detect changes to the prefixes.
Immutability & Lifecycle
What properties are immutable after creating the association?
The dx_gateway_id, associated_gateway_id, and associated_gateway_owner_account_id properties are immutable. Changing any of these requires replacing the association.
How do I import an existing Direct Connect gateway association?
Use pulumi import with the format dx_gateway_id/associated_gateway_id, for example: pulumi import aws:directconnect/gatewayAssociation:GatewayAssociation example 345508c3-7215-4aef-9832-07c125d5bd0f/vgw-98765432.
Can I associate a Direct Connect gateway with both a VPN Gateway and Transit Gateway simultaneously?
No, each association resource connects the Direct Connect gateway to a single VPN Gateway or Transit Gateway. Create separate association resources for multiple gateways.

Using a different cloud?

Explore networking guides for other cloud providers: