The aws:ec2transitgateway/routeTablePropagation:RouteTablePropagation resource, part of the Pulumi AWS provider, enables automatic route learning from Transit Gateway attachments into route tables. This guide focuses on two capabilities: VPC attachment propagation and Direct Connect Gateway propagation.
Route table propagation requires existing Transit Gateway attachments (VPC, Direct Connect Gateway, VPN, or peering) and route tables. The examples are intentionally small. Combine them with your own Transit Gateway infrastructure and routing policies.
Propagate VPC routes to a route table
Transit Gateway route tables learn routes from attached VPCs through propagation, automatically receiving CIDR blocks without manual route entries.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.ec2.Vpc("example", {cidrBlock: "10.0.0.0/16"});
const exampleSubnet = new aws.ec2.Subnet("example", {
vpcId: example.id,
cidrBlock: "10.0.1.0/24",
});
const exampleTransitGateway = new aws.ec2transitgateway.TransitGateway("example", {description: "example"});
const exampleVpcAttachment = new aws.ec2transitgateway.VpcAttachment("example", {
subnetIds: [exampleSubnet.id],
transitGatewayId: exampleTransitGateway.id,
vpcId: example.id,
});
const exampleRouteTable = new aws.ec2transitgateway.RouteTable("example", {transitGatewayId: exampleTransitGateway.id});
// Correct: Reference the VPC attachment ID directly
const exampleRouteTablePropagation = new aws.ec2transitgateway.RouteTablePropagation("example", {
transitGatewayAttachmentId: exampleVpcAttachment.id,
transitGatewayRouteTableId: exampleRouteTable.id,
});
import pulumi
import pulumi_aws as aws
example = aws.ec2.Vpc("example", cidr_block="10.0.0.0/16")
example_subnet = aws.ec2.Subnet("example",
vpc_id=example.id,
cidr_block="10.0.1.0/24")
example_transit_gateway = aws.ec2transitgateway.TransitGateway("example", description="example")
example_vpc_attachment = aws.ec2transitgateway.VpcAttachment("example",
subnet_ids=[example_subnet.id],
transit_gateway_id=example_transit_gateway.id,
vpc_id=example.id)
example_route_table = aws.ec2transitgateway.RouteTable("example", transit_gateway_id=example_transit_gateway.id)
# Correct: Reference the VPC attachment ID directly
example_route_table_propagation = aws.ec2transitgateway.RouteTablePropagation("example",
transit_gateway_attachment_id=example_vpc_attachment.id,
transit_gateway_route_table_id=example_route_table.id)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
"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 := ec2.NewVpc(ctx, "example", &ec2.VpcArgs{
CidrBlock: pulumi.String("10.0.0.0/16"),
})
if err != nil {
return err
}
exampleSubnet, err := ec2.NewSubnet(ctx, "example", &ec2.SubnetArgs{
VpcId: example.ID(),
CidrBlock: pulumi.String("10.0.1.0/24"),
})
if err != nil {
return err
}
exampleTransitGateway, err := ec2transitgateway.NewTransitGateway(ctx, "example", &ec2transitgateway.TransitGatewayArgs{
Description: pulumi.String("example"),
})
if err != nil {
return err
}
exampleVpcAttachment, err := ec2transitgateway.NewVpcAttachment(ctx, "example", &ec2transitgateway.VpcAttachmentArgs{
SubnetIds: pulumi.StringArray{
exampleSubnet.ID(),
},
TransitGatewayId: exampleTransitGateway.ID(),
VpcId: example.ID(),
})
if err != nil {
return err
}
exampleRouteTable, err := ec2transitgateway.NewRouteTable(ctx, "example", &ec2transitgateway.RouteTableArgs{
TransitGatewayId: exampleTransitGateway.ID(),
})
if err != nil {
return err
}
// Correct: Reference the VPC attachment ID directly
_, err = ec2transitgateway.NewRouteTablePropagation(ctx, "example", &ec2transitgateway.RouteTablePropagationArgs{
TransitGatewayAttachmentId: exampleVpcAttachment.ID(),
TransitGatewayRouteTableId: exampleRouteTable.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.Ec2.Vpc("example", new()
{
CidrBlock = "10.0.0.0/16",
});
var exampleSubnet = new Aws.Ec2.Subnet("example", new()
{
VpcId = example.Id,
CidrBlock = "10.0.1.0/24",
});
var exampleTransitGateway = new Aws.Ec2TransitGateway.TransitGateway("example", new()
{
Description = "example",
});
var exampleVpcAttachment = new Aws.Ec2TransitGateway.VpcAttachment("example", new()
{
SubnetIds = new[]
{
exampleSubnet.Id,
},
TransitGatewayId = exampleTransitGateway.Id,
VpcId = example.Id,
});
var exampleRouteTable = new Aws.Ec2TransitGateway.RouteTable("example", new()
{
TransitGatewayId = exampleTransitGateway.Id,
});
// Correct: Reference the VPC attachment ID directly
var exampleRouteTablePropagation = new Aws.Ec2TransitGateway.RouteTablePropagation("example", new()
{
TransitGatewayAttachmentId = exampleVpcAttachment.Id,
TransitGatewayRouteTableId = exampleRouteTable.Id,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.Subnet;
import com.pulumi.aws.ec2.SubnetArgs;
import com.pulumi.aws.ec2transitgateway.TransitGateway;
import com.pulumi.aws.ec2transitgateway.TransitGatewayArgs;
import com.pulumi.aws.ec2transitgateway.VpcAttachment;
import com.pulumi.aws.ec2transitgateway.VpcAttachmentArgs;
import com.pulumi.aws.ec2transitgateway.RouteTable;
import com.pulumi.aws.ec2transitgateway.RouteTableArgs;
import com.pulumi.aws.ec2transitgateway.RouteTablePropagation;
import com.pulumi.aws.ec2transitgateway.RouteTablePropagationArgs;
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 Vpc("example", VpcArgs.builder()
.cidrBlock("10.0.0.0/16")
.build());
var exampleSubnet = new Subnet("exampleSubnet", SubnetArgs.builder()
.vpcId(example.id())
.cidrBlock("10.0.1.0/24")
.build());
var exampleTransitGateway = new TransitGateway("exampleTransitGateway", TransitGatewayArgs.builder()
.description("example")
.build());
var exampleVpcAttachment = new VpcAttachment("exampleVpcAttachment", VpcAttachmentArgs.builder()
.subnetIds(exampleSubnet.id())
.transitGatewayId(exampleTransitGateway.id())
.vpcId(example.id())
.build());
var exampleRouteTable = new RouteTable("exampleRouteTable", RouteTableArgs.builder()
.transitGatewayId(exampleTransitGateway.id())
.build());
// Correct: Reference the VPC attachment ID directly
var exampleRouteTablePropagation = new RouteTablePropagation("exampleRouteTablePropagation", RouteTablePropagationArgs.builder()
.transitGatewayAttachmentId(exampleVpcAttachment.id())
.transitGatewayRouteTableId(exampleRouteTable.id())
.build());
}
}
resources:
example:
type: aws:ec2:Vpc
properties:
cidrBlock: 10.0.0.0/16
exampleSubnet:
type: aws:ec2:Subnet
name: example
properties:
vpcId: ${example.id}
cidrBlock: 10.0.1.0/24
exampleTransitGateway:
type: aws:ec2transitgateway:TransitGateway
name: example
properties:
description: example
exampleVpcAttachment:
type: aws:ec2transitgateway:VpcAttachment
name: example
properties:
subnetIds:
- ${exampleSubnet.id}
transitGatewayId: ${exampleTransitGateway.id}
vpcId: ${example.id}
exampleRouteTable:
type: aws:ec2transitgateway:RouteTable
name: example
properties:
transitGatewayId: ${exampleTransitGateway.id}
# Correct: Reference the VPC attachment ID directly
exampleRouteTablePropagation:
type: aws:ec2transitgateway:RouteTablePropagation
name: example
properties:
transitGatewayAttachmentId: ${exampleVpcAttachment.id}
transitGatewayRouteTableId: ${exampleRouteTable.id}
When you create the propagation, the route table immediately learns the VPC’s CIDR block (10.0.0.0/16 in this case). The transitGatewayAttachmentId references the VPC attachment directly, ensuring the propagation tracks the attachment lifecycle. If the attachment is replaced, the propagation resource recreates automatically to maintain consistency.
Propagate Direct Connect Gateway routes
Organizations connecting on-premises networks via Direct Connect propagate those routes into Transit Gateway route tables for cross-region or cross-account routing.
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", {description: "example"});
const exampleGatewayAssociation = new aws.directconnect.GatewayAssociation("example", {
dxGatewayId: example.id,
associatedGatewayId: exampleTransitGateway.id,
allowedPrefixes: ["10.0.0.0/16"],
});
const exampleRouteTable = new aws.ec2transitgateway.RouteTable("example", {transitGatewayId: exampleTransitGateway.id});
// Correct: Reference the attachment ID directly from the association resource
const exampleRouteTablePropagation = new aws.ec2transitgateway.RouteTablePropagation("example", {
transitGatewayAttachmentId: exampleGatewayAssociation.transitGatewayAttachmentId,
transitGatewayRouteTableId: exampleRouteTable.id,
});
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", description="example")
example_gateway_association = aws.directconnect.GatewayAssociation("example",
dx_gateway_id=example.id,
associated_gateway_id=example_transit_gateway.id,
allowed_prefixes=["10.0.0.0/16"])
example_route_table = aws.ec2transitgateway.RouteTable("example", transit_gateway_id=example_transit_gateway.id)
# Correct: Reference the attachment ID directly from the association resource
example_route_table_propagation = aws.ec2transitgateway.RouteTablePropagation("example",
transit_gateway_attachment_id=example_gateway_association.transit_gateway_attachment_id,
transit_gateway_route_table_id=example_route_table.id)
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", &ec2transitgateway.TransitGatewayArgs{
Description: pulumi.String("example"),
})
if err != nil {
return err
}
exampleGatewayAssociation, err := directconnect.NewGatewayAssociation(ctx, "example", &directconnect.GatewayAssociationArgs{
DxGatewayId: example.ID(),
AssociatedGatewayId: exampleTransitGateway.ID(),
AllowedPrefixes: pulumi.StringArray{
pulumi.String("10.0.0.0/16"),
},
})
if err != nil {
return err
}
exampleRouteTable, err := ec2transitgateway.NewRouteTable(ctx, "example", &ec2transitgateway.RouteTableArgs{
TransitGatewayId: exampleTransitGateway.ID(),
})
if err != nil {
return err
}
// Correct: Reference the attachment ID directly from the association resource
_, err = ec2transitgateway.NewRouteTablePropagation(ctx, "example", &ec2transitgateway.RouteTablePropagationArgs{
TransitGatewayAttachmentId: exampleGatewayAssociation.TransitGatewayAttachmentId,
TransitGatewayRouteTableId: exampleRouteTable.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 exampleTransitGateway = new Aws.Ec2TransitGateway.TransitGateway("example", new()
{
Description = "example",
});
var exampleGatewayAssociation = new Aws.DirectConnect.GatewayAssociation("example", new()
{
DxGatewayId = example.Id,
AssociatedGatewayId = exampleTransitGateway.Id,
AllowedPrefixes = new[]
{
"10.0.0.0/16",
},
});
var exampleRouteTable = new Aws.Ec2TransitGateway.RouteTable("example", new()
{
TransitGatewayId = exampleTransitGateway.Id,
});
// Correct: Reference the attachment ID directly from the association resource
var exampleRouteTablePropagation = new Aws.Ec2TransitGateway.RouteTablePropagation("example", new()
{
TransitGatewayAttachmentId = exampleGatewayAssociation.TransitGatewayAttachmentId,
TransitGatewayRouteTableId = exampleRouteTable.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.ec2transitgateway.TransitGateway;
import com.pulumi.aws.ec2transitgateway.TransitGatewayArgs;
import com.pulumi.aws.directconnect.GatewayAssociation;
import com.pulumi.aws.directconnect.GatewayAssociationArgs;
import com.pulumi.aws.ec2transitgateway.RouteTable;
import com.pulumi.aws.ec2transitgateway.RouteTableArgs;
import com.pulumi.aws.ec2transitgateway.RouteTablePropagation;
import com.pulumi.aws.ec2transitgateway.RouteTablePropagationArgs;
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", TransitGatewayArgs.builder()
.description("example")
.build());
var exampleGatewayAssociation = new GatewayAssociation("exampleGatewayAssociation", GatewayAssociationArgs.builder()
.dxGatewayId(example.id())
.associatedGatewayId(exampleTransitGateway.id())
.allowedPrefixes("10.0.0.0/16")
.build());
var exampleRouteTable = new RouteTable("exampleRouteTable", RouteTableArgs.builder()
.transitGatewayId(exampleTransitGateway.id())
.build());
// Correct: Reference the attachment ID directly from the association resource
var exampleRouteTablePropagation = new RouteTablePropagation("exampleRouteTablePropagation", RouteTablePropagationArgs.builder()
.transitGatewayAttachmentId(exampleGatewayAssociation.transitGatewayAttachmentId())
.transitGatewayRouteTableId(exampleRouteTable.id())
.build());
}
}
resources:
example:
type: aws:directconnect:Gateway
properties:
name: example
amazonSideAsn: 64512
exampleTransitGateway:
type: aws:ec2transitgateway:TransitGateway
name: example
properties:
description: example
exampleGatewayAssociation:
type: aws:directconnect:GatewayAssociation
name: example
properties:
dxGatewayId: ${example.id}
associatedGatewayId: ${exampleTransitGateway.id}
allowedPrefixes:
- 10.0.0.0/16
exampleRouteTable:
type: aws:ec2transitgateway:RouteTable
name: example
properties:
transitGatewayId: ${exampleTransitGateway.id}
# Correct: Reference the attachment ID directly from the association resource
exampleRouteTablePropagation:
type: aws:ec2transitgateway:RouteTablePropagation
name: example
properties:
transitGatewayAttachmentId: ${exampleGatewayAssociation.transitGatewayAttachmentId}
transitGatewayRouteTableId: ${exampleRouteTable.id}
The GatewayAssociation resource connects the Direct Connect Gateway to the Transit Gateway and exposes a transitGatewayAttachmentId attribute. Reference this attribute directly rather than using a data source lookup; data sources can trigger unnecessary recreation when unrelated properties like allowedPrefixes change. The allowedPrefixes property controls which on-premises routes propagate into the Transit Gateway.
Beyond these examples
These snippets focus on specific propagation features: VPC attachment propagation and Direct Connect Gateway propagation. They’re intentionally minimal rather than full Transit Gateway deployments.
The examples may reference pre-existing infrastructure such as Transit Gateway attachments (VPC or Direct Connect Gateway) and Transit Gateway route tables. They focus on configuring propagation rather than provisioning the entire Transit Gateway topology.
To keep things focused, common propagation patterns are omitted, including:
- VPN attachment propagation
- Peering attachment propagation
- Static route configuration (alternative to propagation)
- Route table association (companion to propagation)
These omissions are intentional: the goal is to illustrate how propagation is wired, not provide drop-in networking modules. See the Transit Gateway Route Table Propagation resource reference for all available configuration options.
Let's configure AWS Transit Gateway Route Table Propagation
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration & Setup
transitGatewayAttachmentId attribute directly from the aws.directconnect.GatewayAssociation resource (available in v6.5.0+). This ensures proper dependency tracking and avoids unnecessary resource recreation.id attribute directly as the transitGatewayAttachmentId. Avoid using data sources or lifecycle rules that might cause the attachment ID to become unknown during planning.Common Pitfalls
aws.ec2transitgateway.getDirectConnectGatewayAttachment can cause unnecessary resource recreation when unrelated attributes of the Direct Connect Gateway association change (such as allowedPrefixes). Always reference the transitGatewayAttachmentId attribute directly from the aws.directconnect.GatewayAssociation resource.Resource Behavior
transitGatewayAttachmentId and transitGatewayRouteTableId are immutable. Changing either property will force the resource to be recreated.transitGatewayAttachmentId changes (for example, when a VPC attachment is replaced), this resource is recreated to maintain consistency between the attachment and its route table propagation. This is expected behavior.Using a different cloud?
Explore networking guides for other cloud providers: