diff --git a/iac/environments/dev/main.tf b/iac/environments/dev/main.tf index 6665507..bdf8df6 100644 --- a/iac/environments/dev/main.tf +++ b/iac/environments/dev/main.tf @@ -559,6 +559,17 @@ module "cosmos_account" { private_endpoint_subnet_id = module.networking.subnet_private_endpoints_id private_dns_zone_id = module.networking.private_dns_zone_ids["privatelink.documents.azure.com"] + # Allow traffic originating from any Azure datacenter — the Container + # Apps Environment hosting the backend + indexer is not vnet-integrated + # (CAE vnetConfig: null), so its egress is a public Azure NAT IP. Cosmos + # in "PE + public-enabled" mode drops public traffic by default unless + # explicitly allowed via ipRules. `0.0.0.0` is Cosmos's magic value for + # "Allow access from public Azure datacenters" — narrower than allowing + # the entire internet (which would be `0.0.0.0/0`), and AAD/RBAC still + # gates every connection regardless. When the CAE is vnet-integrated by + # a future spec, this entry can be removed. + ip_range_filter = ["0.0.0.0"] + tags = local.shared_tags } diff --git a/iac/modules/cosmos-account/README.md b/iac/modules/cosmos-account/README.md index 0ee07ab..9c0032f 100644 --- a/iac/modules/cosmos-account/README.md +++ b/iac/modules/cosmos-account/README.md @@ -34,6 +34,7 @@ Spec 004 / Spec 005 / US1 — canonical resource store + change-event log. | [location](#input\_location) | Azure region for the Cosmos DB account. | `string` | n/a | yes | | [name](#input\_name) | Cosmos DB account name. Must be globally unique, 3-44 lowercase alphanumeric / hyphen chars. | `string` | n/a | yes | | [resource\_group\_name](#input\_resource\_group\_name) | Resource group hosting the Cosmos DB account. | `string` | n/a | yes | +| [ip\_range\_filter](#input\_ip\_range\_filter) | Cosmos `ipRules` set. When a private endpoint is configured AND
`public_network_access_enabled = true`, Cosmos enters a default
"restricted public" mode where public traffic is dropped unless
explicitly allowed via this set. The special magic value `0.0.0.0`
permits traffic originating from any Azure datacenter — used in
dev so the Container Apps Environment's egress NAT (a public
Azure-allocated IP) can reach Cosmos for the indexer's change-feed
listener. Empty set keeps the default-restrictive posture (PE-only
+ named IP allowlist). Bare IP literals or CIDRs are also accepted. | `set(string)` | `[]` | no | | [log\_analytics\_workspace\_id](#input\_log\_analytics\_workspace\_id) | Optional Log Analytics Workspace resource id for diagnostic settings. When
set, a diagnostic-setting routes allLogs + AllMetrics to the workspace
(Constitution §Operational Excellence — every Azure resource routes
diagnostic logs + AllMetrics to the LAW). Pass null to skip. | `string` | `null` | no | | [private\_dns\_zone\_id](#input\_private\_dns\_zone\_id) | Private DNS zone ID for `privatelink.documents.azure.com`. Required when private\_endpoint\_enabled = true. | `string` | `null` | no | | [private\_endpoint\_enabled](#input\_private\_endpoint\_enabled) | Plan-time bool toggling the conditional private-endpoint child module.
Required as a separate variable from `private_endpoint_subnet_id`
because the subnet ID is sourced from the networking module's output,
which is "known after apply" — using a nullable string in the `count`
expression breaks plan with "Invalid count argument: count value
depends on resource attributes that cannot be determined until apply".
The env composition passes a literal bool here (`var.private_endpoints_enabled`)
so plan can statically resolve the count. | `bool` | `false` | no | diff --git a/iac/modules/cosmos-account/main.tf b/iac/modules/cosmos-account/main.tf index a7935e2..82889c8 100644 --- a/iac/modules/cosmos-account/main.tf +++ b/iac/modules/cosmos-account/main.tf @@ -37,6 +37,12 @@ resource "azurerm_cosmosdb_account" "this" { # Spec 005 FR-031 — per-env public-network-access toggle (Q2c). public_network_access_enabled = var.public_network_access_enabled + # Cosmos `ipRules` — required when public access is enabled alongside a + # private endpoint, otherwise the account enters a default-restrictive + # mode that drops all public traffic. See variables.tf for the full + # rationale (and the `0.0.0.0` Azure-datacenter magic value). + ip_range_filter = var.ip_range_filter + # Automatic-failover off for dev — single-region serverless. AVM rejects multi-region # with EnableServerless anyway. automatic_failover_enabled = false diff --git a/iac/modules/cosmos-account/variables.tf b/iac/modules/cosmos-account/variables.tf index a8bff85..386f59e 100644 --- a/iac/modules/cosmos-account/variables.tf +++ b/iac/modules/cosmos-account/variables.tf @@ -71,3 +71,19 @@ variable "public_network_access_enabled" { type = bool default = true } + +variable "ip_range_filter" { + description = <<-EOT + Cosmos `ipRules` set. When a private endpoint is configured AND + `public_network_access_enabled = true`, Cosmos enters a default + "restricted public" mode where public traffic is dropped unless + explicitly allowed via this set. The special magic value `0.0.0.0` + permits traffic originating from any Azure datacenter — used in + dev so the Container Apps Environment's egress NAT (a public + Azure-allocated IP) can reach Cosmos for the indexer's change-feed + listener. Empty set keeps the default-restrictive posture (PE-only + + named IP allowlist). Bare IP literals or CIDRs are also accepted. + EOT + type = set(string) + default = [] +}