A single global network + Cloud WAN core network backbone, with every AWS connectivity primitive — VPC, Transit Gateway, Direct Connect, Site-to-Site VPN, and on-prem SD-WAN Connect peers — wired in as first-class, centrally-governed attachments. Built for the AWS provider v6.x.
- 🌐 Two keystones, one module:
aws_networkmanager_global_network(the account-wide container) andaws_networkmanager_core_network(the actual Cloud WAN backbone), created and wired together in one call - 📜 Policy-as-JSON, plumbing-as-Terraform: you build the Cloud WAN
segments/routing policy however you like (the
aws_networkmanager_core_network_policy_documentdata source is the recommended pattern) and pass it in as a string — this module owns the attachment and topology resources, not policy authoring - 🔌 Every connectivity path is a first-class child collection: VPC,
Connect (SD-WAN GRE/no-encap), Site-to-Site VPN, Direct Connect gateway,
and Transit Gateway registration/peering/route-table attachments — all
for_eachover stable, caller-chosen keys - 🗺️ Physical topology modeling: sites, devices, links, and link associations for organizations that track their on-prem network inventory inside Network Manager
- 🔐 Cross-account safe by construction: attachment acceptance
(
aws_networkmanager_attachment_accepter) is always an explicit, separate step — no auto-accept flag anywhere in this module - 🧵 Migration-friendly: register an existing Transit Gateway hub into Cloud WAN and peer it in without touching the TGW itself, letting you move spoke-by-spoke rather than a big-bang cutover
💡 Why it matters: once a network has outgrown a flat mesh of VPC peerings or a single-Region Transit Gateway, Cloud WAN gives one place to define segmentation policy (which workloads may talk to which) across every Region and every connectivity type — instead of re-deriving that policy independently in route tables, security groups, and VPN configs.
If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:
- ⭐ Star this repository to help others discover this Terraform module.
- 🤝 Connect with me on LinkedIn: linkedin.com/in/microsoftexpert
- ☕ Buy me a coffee: buymeacoffee.com/microsoftexpert
Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!
flowchart LR
VPC["tf-mod-aws-vpc"] --> NM["tf-mod-aws-network-manager"]
TGW["tf-mod-aws-transit-gateway"] --> NM
VPN["tf-mod-aws-vpn"] --> NM
DX["tf-mod-aws-dx"] --> NM
PL["aws_ec2_managed_prefix_list (caller-managed)"] --> NM
NM --> RAM["Cross-account RAM shares"]
NM --> R53R["tf-mod-aws-route53-resolver (on-prem DNS)"]
style NM fill:#FF9900,color:#fff
flowchart TD
GN["aws_networkmanager_global_network.this (keystone)"]
CN["aws_networkmanager_core_network.this (singleton)"]
PA["aws_networkmanager_core_network_policy_attachment.this (conditional)"]
GN --> CN
CN --> PA
GN --> Site["aws_networkmanager_site (for_each)"]
GN --> Dev["aws_networkmanager_device (for_each)"]
Site --> Link["aws_networkmanager_link (for_each)"]
Dev --> LinkAssoc["aws_networkmanager_link_association (for_each)"]
Link --> LinkAssoc
CN --> VpcAtt["aws_networkmanager_vpc_attachment (for_each)"]
CN --> ConnAtt["aws_networkmanager_connect_attachment (for_each)"]
VpcAtt --> ConnAtt
ConnAtt --> ConnPeer["aws_networkmanager_connect_peer (for_each)"]
CN --> VpnAtt["aws_networkmanager_site_to_site_vpn_attachment (for_each)"]
CN --> DxAtt["aws_networkmanager_dx_gateway_attachment (for_each)"]
GN --> TgwReg["aws_networkmanager_transit_gateway_registration (for_each)"]
CN --> TgwPeer["aws_networkmanager_transit_gateway_peering (for_each)"]
TgwPeer --> TgwRtAtt["aws_networkmanager_transit_gateway_route_table_attachment (for_each)"]
GN --> CgwAssoc["aws_networkmanager_customer_gateway_association (for_each)"]
CN --> PlAssoc["aws_networkmanager_prefix_list_association (for_each)"]
CN --> RoutingLabel["aws_networkmanager_attachment_routing_policy_label (for_each)"]
VpcAtt --> Accepter["aws_networkmanager_attachment_accepter (for_each)"]
style GN fill:#FF9900,color:#fff
style CN fill:#FF9900,color:#fff
- Terraform
>= 1.12.0 hashicorp/aws>= 6.0, < 7.0(validated against v6.54.0)- No
provider {}block inside this module — credentials,default_tags, and Region all come from the caller's provider configuration
| Action | Required for | Notes |
|---|---|---|
networkmanager:CreateGlobalNetwork, DescribeGlobalNetworks, UpdateGlobalNetwork, DeleteGlobalNetwork, TagResource, UntagResource |
Global network lifecycle | — |
networkmanager:CreateCoreNetwork, GetCoreNetwork, UpdateCoreNetwork, DeleteCoreNetwork |
Core network lifecycle | — |
networkmanager:PutCoreNetworkPolicy, GetCoreNetworkPolicy, GetCoreNetworkChangeSet, ExecuteCoreNetworkChangeSet |
Policy attachment / propagation | Change-set execution is asynchronous |
networkmanager:CreateSite/CreateDevice/CreateLink, matching Get*/Update*/Delete* |
Sites, devices, links | — |
networkmanager:AssociateLink, DisassociateLink |
Link associations | — |
networkmanager:CreateVpcAttachment, CreateConnectAttachment, CreateConnectPeer, CreateSiteToSiteVpnAttachment, CreateDirectConnectGatewayAttachment, CreateTransitGatewayRouteTableAttachment, matching Get*/DeleteAttachment |
The six attachment types | Least-privilege: scope Delete* to arn:aws:networkmanager::<account>:attachment/* |
networkmanager:RegisterTransitGateway, DeregisterTransitGateway, CreateTransitGatewayPeering |
TGW registration/peering | — |
networkmanager:AssociateCustomerGateway, DisassociateCustomerGateway |
Customer gateway associations | — |
networkmanager:PutAttachmentRoutingPolicyLabel, DeleteAttachmentRoutingPolicyLabel |
Routing policy labels | — |
networkmanager:AcceptAttachment |
Attachment accepters | Run in the core-network-owning account |
ec2:DescribeVpcs, ec2:DescribeSubnets |
Read-back validation for VPC attachments | — |
No
iam:PassRoleis required anywhere in this module's scope. A service-linked role (AWSServiceRoleForNetworkManager) is auto-created by AWS on first global-network creation — see AWS Prerequisites.
- Service-linked role
AWSServiceRoleForNetworkManagerauto-creates on first use; grantiam:CreateServiceLinkedRoleonce. - Core network requires a LIVE policy before most attachments can be
created. Supply a complete
core_network_policy_documentfrom the start, or use the two-passcreate_base_policy = truepattern (see Architecture Notes). - Prefix lists referenced by
prefix_list_associationsmust be defined inus-west-2(the documented Cloud WAN home Region), even though the association applies globally across every core network edge. - Quotas: 5 global networks/account, 1 core network/global network,
10,000 attachments/core network (soft) — verify current values via
Service Quotas (
networkmanager) before large migrations. - A global network is account-wide, not per-Region — do not create one per Region the way you would for a VPC.
tf-mod-aws-network-manager/
├── providers.tf
├── variables.tf
├── main.tf
├── outputs.tf
├── README.md
└── SCOPE.md
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
global_network_description = "global network backbone"
core_network_description = "Primary Cloud WAN core network"
# Build with the aws_networkmanager_core_network_policy_document data
# source in the caller; passed here as an opaque JSON string.
core_network_policy_document = data.aws_networkmanager_core_network_policy_document.this.json
vpc_attachments = {
app = {
vpc_arn = module.app_vpc.arn
subnet_arns = values(module.app_vpc.private_subnet_arns)
}
}
tags = {
environment = "shared-services"
owner = "network-engineering"
}
}| Input | Type | Source module |
|---|---|---|
vpc_attachments[*].vpc_arn / subnet_arns |
string / list(string) |
tf-mod-aws-vpc |
site_to_site_vpn_attachments[*].vpn_connection_arn |
string |
tf-mod-aws-vpn |
customer_gateway_associations[*].customer_gateway_arn |
string |
tf-mod-aws-vpn |
dx_gateway_attachments[*].direct_connect_gateway_arn |
string |
tf-mod-aws-dx |
transit_gateway_registrations / transit_gateway_peerings[*].transit_gateway_arn |
string |
tf-mod-aws-transit-gateway |
transit_gateway_route_table_attachments[*].transit_gateway_route_table_arn |
string |
tf-mod-aws-transit-gateway |
prefix_list_associations[*].prefix_list_arn |
string |
caller-managed aws_ec2_managed_prefix_list |
| Output | Description | Consumed by |
|---|---|---|
id / arn |
Global network id/ARN | Account-wide references |
core_network_id / core_network_arn |
Core network id/ARN | Every externally-created attachment; RAM shares |
site_ids / device_ids / link_ids |
Maps by key | Sibling collections within this module |
vpc_attachment_ids / _arns |
Map by key | connect_attachments (transport), attachment_accepters |
transit_gateway_peering_ids |
Map by key | transit_gateway_route_table_attachments |
tags_all |
Merged tags | Governance / audit |
See SCOPE.md for the complete Consumes/Emits tables (all 19 resource types).
1 · Minimal — global network + core network, no policy yet
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "sandbox-cloud-wan"
}2 · Global network + core network + policy (single Region)
data "aws_networkmanager_core_network_policy_document" "this" {
core_network_configuration {
asn_ranges = ["64512-65534"]
edge_locations { location = "us-east-1" }
}
segments {
name = "prod"
isolate_attachments = true
require_attachment_acceptance = true
}
segments {
name = "shared-services"
isolate_attachments = false
}
}
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
core_network_policy_document = data.aws_networkmanager_core_network_policy_document.this.json
}3 · Two-pass bootstrap with create_base_policy (VPC attachments before the real policy)
# Pass 1
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
create_base_policy = true
vpc_attachments = {
app = { vpc_arn = module.app_vpc.arn, subnet_arns = values(module.app_vpc.private_subnet_arns) }
}
}
# Pass 2 (after the VPC attachment id is known) — add core_network_policy_document
# referencing module.network_manager.vpc_attachment_ids["app"] in your segment-actions.4 · VPC attachment with appliance mode + IPv6 support
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
vpc_attachments = {
inspection = {
vpc_arn = module.inspection_vpc.arn
subnet_arns = values(module.inspection_vpc.private_subnet_arns)
options = {
appliance_mode_support = true
ipv6_support = true
}
}
}
}5 · Physical topology — site, device, link, link association
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
sites = {
hq = { description = "Headquarters", location = { address = "123 Main St", latitude = "47.6062", longitude = "-122.3321" } }
}
devices = {
edge-01 = { site_key = "hq", vendor = "Cisco", model = "ISR4451", type = "router" }
}
links = {
isp-a = { site_key = "hq", bandwidth = { upload_speed = 100, download_speed = 500 }, provider_name = "MegaCorp" }
}
link_associations = {
edge01-isp-a = { device_key = "edge-01", link_key = "isp-a" }
}
}6 · Connect attachment (SD-WAN overlay) + Connect peer
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
vpc_attachments = {
transport = { vpc_arn = module.transport_vpc.arn, subnet_arns = values(module.transport_vpc.private_subnet_arns) }
}
connect_attachments = {
sdwan = {
edge_location = "us-east-1"
transport_attachment_key = "transport"
options = { protocol = "GRE" }
}
}
connect_peers = {
dc1 = {
connect_attachment_key = "sdwan"
peer_address = "203.0.113.10"
bgp_options = { peer_asn = 65000 }
}
}
}7 · Site-to-Site VPN attachment (wiring from tf-mod-aws-vpn)
module "vpn_branch1" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-vpn?ref=v1.0.0"
#... transit_gateway_id or vpc_id path...
}
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
site_to_site_vpn_attachments = {
branch1 = { vpn_connection_arn = module.vpn_branch1.arn }
}
customer_gateway_associations = {
branch1 = { customer_gateway_arn = module.vpn_branch1.customer_gateway_arn, device_key = "edge-01" }
}
}8 · Direct Connect gateway attachment
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
dx_gateway_attachments = {
corp = {
direct_connect_gateway_arn = module.dx.gateway_arn
edge_locations = ["us-east-1"]
}
}
}9 · Migrating an existing Transit Gateway hub into Cloud WAN
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
# Step 1: register the existing TGW into the global network (topology only)
transit_gateway_registrations = {
legacy_hub = { transit_gateway_arn = module.tgw.arn }
}
# Step 2: peer Cloud WAN to the TGW (the actual data-plane link)
transit_gateway_peerings = {
legacy_hub = { transit_gateway_arn = module.tgw.arn }
}
# Step 3: attach individual TGW route tables (spokes) one at a time
transit_gateway_route_table_attachments = {
spoke_a = {
peering_key = "legacy_hub"
transit_gateway_route_table_arn = module.tgw.route_table_arns["spoke"]
}
}
}10 · Prefix list association referenced by alias in the policy
resource "aws_ec2_managed_prefix_list" "corp_cidrs" {
provider = aws.us_west_2 # Cloud WAN home Region
name = "corp-cidrs"
address_family = "IPv4"
max_entries = 10
}
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
prefix_list_associations = {
corp = { alias = "corpCidrs", prefix_list_arn = aws_ec2_managed_prefix_list.corp_cidrs.arn }
}
}11 · Cross-account attachment acceptance
# In the core-network-owning account's provider:
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
attachment_accepters = {
spoke_a_vpc = {
attachment_id = "attachment-0123456789abcdef0"
attachment_type = "VPC"
}
}
}12 · `tags` merging with provider `default_tags`
# Caller's provider block:
provider "aws" {
default_tags {
tags = { managed-by = "terraform", cost-center = "network" }
}
}
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
tags = { environment = "shared-services" } # merges with default_tags; resource tags win on key conflict
}13 · Secure-by-default opt-out — flattening segment isolation (NOT recommended)
# This module does not template the policy JSON, so "opting out" of segment
# isolation is done entirely in the CALLER's policy document — simply omit
# isolate_attachments/require_attachment_acceptance from a segment block to
# fall back to AWS's own defaults (isolate_attachments = false). Document any
# such exception; our baseline recommendation is explicit isolation with
# narrow share actions, not a flat segment.
data "aws_networkmanager_core_network_policy_document" "flat" {
core_network_configuration {
asn_ranges = ["64512-65534"]
edge_locations { location = "us-east-1" }
}
segments { name = "everything" } # isolate_attachments defaults to false — NOT the baseline
}14 · `for_each` pattern — multiple VPC attachments from a map of VPC modules
locals {
spoke_vpcs = { app = module.app_vpc, data = module.data_vpc, shared = module.shared_vpc }
}
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
vpc_attachments = {
for k, v in local.spoke_vpcs : k => {
vpc_arn = v.arn
subnet_arns = values(v.private_subnet_arns)
}
}
}15 · `import` block — bringing an existing global network under management
import {
to = module.network_manager.aws_networkmanager_global_network.this
id = "global-network-0d47f6t230mz46dy4"
}16 · End-to-end composition — VPC + Transit Gateway + VPN + Cloud WAN
module "app_vpc" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-vpc?ref=v1.0.0"
#...
}
module "tgw" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-transit-gateway?ref=v1.0.0"
#...
}
module "vpn_branch1" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-vpn?ref=v1.0.0"
transit_gateway_id = module.tgw.id
#...
}
data "aws_networkmanager_core_network_policy_document" "this" {
core_network_configuration {
asn_ranges = ["64512-65534"]
edge_locations { location = "us-east-1" }
}
segments {
name = "prod"
isolate_attachments = true
require_attachment_acceptance = false
}
}
module "network_manager" {
source = "git::https://github.com/microsoftexpert/tf-mod-aws-network-manager?ref=v1.0.0"
name = "corp-cloud-wan"
core_network_policy_document = data.aws_networkmanager_core_network_policy_document.this.json
vpc_attachments = {
app = { vpc_arn = module.app_vpc.arn, subnet_arns = values(module.app_vpc.private_subnet_arns) }
}
transit_gateway_registrations = { legacy_hub = { transit_gateway_arn = module.tgw.arn } }
transit_gateway_peerings = { legacy_hub = { transit_gateway_arn = module.tgw.arn } }
site_to_site_vpn_attachments = { branch1 = { vpn_connection_arn = module.vpn_branch1.arn } }
tags = { environment = "prod", owner = "network-engineering" }
}Core: name, global_network_description, core_network_description
Policy: core_network_policy_document, create_base_policy,
base_policy_document, base_policy_regions
Topology: sites, devices, links, link_associations
Attachments: vpc_attachments, connect_attachments, connect_peers,
site_to_site_vpn_attachments, dx_gateway_attachments,
transit_gateway_registrations, transit_gateway_peerings,
transit_gateway_route_table_attachments
Governance: customer_gateway_associations, prefix_list_associations,
attachment_routing_policy_labels, attachment_accepters
Universal: tags, timeouts
id/arn— global network primary outputsglobal_network_id— alias ofidcore_network_id/core_network_arn/core_network_state/core_network_edges/core_network_segments/core_network_policy_attachment_statesite_ids/site_arns,device_ids/device_arns,link_ids/link_arns,link_association_ids— topology maps by keyvpc_attachment_ids/_arns,connect_attachment_ids/_arns,connect_peer_ids/_arns— attachment maps by keyconnect_peer_configurations— sensitive; may include tunnel addressing detailsite_to_site_vpn_attachment_ids/_arns,dx_gateway_attachment_ids/_arnstransit_gateway_registration_keys,transit_gateway_peering_ids/_arns/edge_locations,transit_gateway_route_table_attachment_ids/_arnscustomer_gateway_association_keys,prefix_list_association_keys,attachment_routing_policy_label_keys,attachment_accepter_states— metadata-only resources with no AWS-exposed id/arntags_all
- ARN formats: global network —
arn:aws:networkmanager::<account>:global-network/<id>; core network —arn:aws:networkmanager::<account>:core-network/<id>; attachments —arn:aws:networkmanager:<edge-region>:<account>:attachment/<id>. - Immutable / FORCE-NEW fields:
vpc_attachments[*].vpc_arn,connect_attachments[*].options.protocol,connect_peers[*].peer_address, everyrouting_policy_label, and every field onprefix_list_associations/attachment_routing_policy_labels. tags↔tags_all↔ providerdefault_tags: resourcetagsmerge with the caller's providerdefault_tags; resource tags win on key conflict. Six resources in this module are NOT taggable in AWS:link_association,transit_gateway_registration,customer_gateway_association,attachment_accepter,prefix_list_association,attachment_routing_policy_label.- Eventual consistency: applying
aws_networkmanager_core_network_policy_attachmenttriggers an asynchronous change-set across every core network edge.terraform applyreturns once the change-set is EXECUTED, not once every edge has converged — verifycore_network_edges/ attachmentstatebefore cutting production traffic over. - Destroy ordering: deleting the core network policy attachment does NOT revert the policy (AWS behavior). Deleting the core network itself requires every attachment to be deleted first (AWS-enforced); deleting the global network requires the core network to be gone first.
- No us-east-1 global-resource requirement — unlike CloudFront/WAFv2/ACM, Cloud WAN's edge Regions are defined INSIDE the policy document, not by the provider's Region; this module can be authored from any provider Region.
aws_networkmanager_transit_gateway_peeringhas no edge-location input — AWS computes it automatically; seetransit_gateway_peering_edge_locations.
- Segment isolation is a policy-authoring discipline, not a module
flag. This module does not template the Cloud WAN policy JSON, so there
is no single "secure default" boolean to flip. Build
core_network_policy_documentwithisolate_attachments = trueandrequire_attachment_acceptance = trueon sensitive segments, and use narrowshare/deny-filteractions to open only the inter-segment paths you intend — opt-out is simply omitting these fields from a segment block, which is why the recommendation is documented here rather than enforced in code. - Cross-account attachments never auto-accept. There is no flag in this
module that accepts an attachment automatically;
attachment_acceptersis always a deliberate, separate map the core-network-owning account populates. - VPC attachment options default to the safer posture where AWS defines
one (
dns_support = true;appliance_mode_support/ipv6_support/security_group_referencing_supportall defaultfalse, requiring explicit opt-in per attachment). - Attachments are blocked from creation until a LIVE policy exists — the
module enforces this with an explicit
depends_onfrom every attachment resource to the policy attachment, soterraform applyfails fast with a clear dependency rather than a confusing AWS API error mid-apply.
cd tf-mod-aws-network-manager
terraform init -backend=false
terraform validate
terraform fmt -check
⚠️ terraform plan/applyrequire valid AWS credentials (profile / SSO / OIDC) and a Region. Pin the module source with?ref=v1.0.0— never a branch — so a later commit tomaincannot silently change what an existing root module applies.
terraform plan
terraform apply
terraform output core_network_idterraform validate+terraform fmt -check— the offline gate this module was authored against (no AWS credentials required).- For live testing, apply into a non-production account/Region with a
least-privilege role scoped to the IAM actions listed above; start with
create_base_policy = trueand a singlevpc_attachmentsentry before layering in Connect/VPN/TGW attachments. - Verify policy propagation with
core_network_edges/ attachmentstateoutputs before treating a new segment as production-ready.
$ terraform apply
...
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Outputs:
core_network_id = "core-network-0d47f6t230mz46dy4"
core_network_arn = "arn:aws:networkmanager::123456789012:core-network/core-network-0d47f6t230mz46dy4"
id = "global-network-0d47f6t230mz46dy4"
vpc_attachment_ids = {
"app" = "attachment-0a1b2c3d4e5f6g7h8"
}
ValidationException: policy is not LIVEon a VPC/Connect/VPN/DX attachment — the core network has no LIVE policy yet. Setcreate_base_policy = truefor a two-pass bootstrap, or supply a completecore_network_policy_documentup front.- Routes not showing up after
terraform apply— the policy change-set is asynchronous; checkcore_network_edgesand the relevant attachment'sstatebefore assuming the apply "didn't work." - Tag drift from
default_tagsoverlap — a key set in both the provider'sdefault_tagsand this module'stagswill show the resource value winning; remove the duplicate from one side to stop the diff noise. - Credential-chain failures — this module has no credential variables;
confirm
AWS_PROFILE/SSO session/OIDC role is valid in the shell/pipeline beforeplan/apply. prefix_list_associationfails with a Region error — the referenced managed prefix list must be defined inus-west-2regardless of where your core network's edges are.- Cross-account attachment stuck
PENDING_ATTACHMENT_ACCEPTANCE— runattachment_acceptersin the core-network-owning account's provider, not the spoke account's. - Destroy fails: "core network has attachments" — delete every
attachment collection (set the relevant maps to
{}) before removing the core network or global network. - IAM permission denials — cross-check the failing action against
## Required IAM permissionsabove; do not broaden tonetworkmanager:*.
- Terraform Registry:
hashicorp/aws— Network Manager resource family (aws_networkmanager_*) - AWS documentation: Network Manager / Cloud WAN core concepts, policy document reference, Connect attachments, and cross-account sharing
- AWS documentation: Direct Connect + Cloud WAN integration, Transit Gateway peering with Cloud WAN
🧡 "Infrastructure as Code should be standardized, consistent, and secure."