Add attachment groups factory to 2-networking (#3871)

* feat(fast): add attachment groups factory to 2-networking

Adds support for `google_compute_interconnect_attachment_group` in the `2-networking` stage.

By implementing this at the factory level alongside `vlan-attachments`, users can now declaratively aggregate VLAN attachments across multiple VPCs and natively reference them using the `$attachment_groups:<key>` context identifier in their configuration YAMLs.

Includes:
- Factory implementation in `factory-vlan-attachments.tf`.
- New JSON schemas for `attachment-groups` and updates to `vlan-attachments` to support context linkage.
- Test coverage with new inventory generations.

Fixes #3791
This commit is contained in:
Simone Ruffilli
2026-04-22 12:22:50 +02:00
committed by GitHub
parent ec39405352
commit 9eb69ffaa3
12 changed files with 277 additions and 35 deletions

View File

@@ -367,7 +367,7 @@ Internally created resources are mapped to context namespaces, and use specific
| [factory-peering.tf](./factory-peering.tf) | VPC Peering factory. | | <code>google_compute_network_peering</code> |
| [factory-projects.tf](./factory-projects.tf) | Projects factory. | <code>project-factory</code> | |
| [factory-routers.tf](./factory-routers.tf) | Routers factory. | | <code>google_compute_router</code> |
| [factory-vlan-attachments.tf](./factory-vlan-attachments.tf) | VLAN attachments factory. | <code>net-vlan-attachment</code> | |
| [factory-vlan-attachments.tf](./factory-vlan-attachments.tf) | VLAN attachments factory. | <code>net-vlan-attachment</code> | <code>google_compute_interconnect_attachment_group</code> |
| [factory-vpcs.tf](./factory-vpcs.tf) | VPC and firewall rules factory. | <code>net-vpc</code> · <code>net-vpc-factory</code> | |
| [factory-vpns.tf](./factory-vpns.tf) | VPNs factory. | <code>net-vpn-ha</code> | <code>google_compute_ha_vpn_gateway</code> |
| [main.tf](./main.tf) | Module-level locals and resources. | | |

View File

@@ -66,6 +66,87 @@ locals {
mtu = try(v.mtu, local.vpcs[v.vpc_key].mtu, local.vpc_defaults.mtu, 1500)
})
}
_attachment_groups_files = try(
merge([
for vpc_key, vpc in local.vpcs : {
for f in try(fileset(
try(
startswith(vpc.factories_config.attachment_groups, "/") || startswith(vpc.factories_config.attachment_groups, ".") ? vpc.factories_config.attachment_groups :
"${vpc.factory_basepath}/${vpc.factories_config.attachment_groups}",
"${vpc.factory_basepath}/attachment-groups"
),
"**/*.yaml"
), []) :
"${vpc_key}-${replace(f, ".yaml", "")}" => {
vpc_key = vpc_key
filename = f
path = try(
startswith(vpc.factories_config.attachment_groups, "/") || startswith(vpc.factories_config.attachment_groups, ".")
? "${vpc.factories_config.attachment_groups}/${f}"
: "${vpc.factory_basepath}/${vpc.factories_config.attachment_groups}/${f}",
"${vpc.factory_basepath}/attachment-groups/${f}"
)
}
}
]...),
{}
)
_attachment_groups_preprocess = {
for k, v in local._attachment_groups_files : k => merge(
{
project_id = local.vpcs[v.vpc_key].project_id
},
try(yamldecode(file(v.path)), {}),
{
key = k
vpc_key = v.vpc_key
}
)
}
attachment_groups = {
for k, v in local._attachment_groups_preprocess : k => merge(v, {
name = try(v.name, k)
intent = try(v.intent, { availability_sla = "NO_SLA" })
})
}
ctx_attachment_groups = {
for k, v in local.attachment_groups : "${v.vpc_key}/${v.name}" => k
}
ctx_vlan_attachments = {
for k, v in local.vlan_attachments : "${v.vpc_key}/${try(v.name, k)}" => k
}
# Gathers all members for each attachment group. Membership can be defined
# in two ways:
# 1. From the VLAN attachment's config via the `attachment_group` attribute.
# 2. From the attachment group's config via the `attachments` map.
_attachment_groups_attachments = {
for g_key, g_config in local.attachment_groups : g_key =>
concat(
[
for a_key, a_config in local.vlan_attachments : {
name = try(a_config.name, a_config.key)
attachment = module.vlan-attachments[a_key].id
}
if try(
lookup(local.ctx_attachment_groups, replace(a_config.attachment_group, "$attachment_groups:", ""), a_config.attachment_group),
null
) == g_key || (try(a_config.attachment_group, null) == g_config.name && a_config.vpc_key == g_config.vpc_key)
],
[
for a in values(try(g_config.attachments, {})) : {
name = a.name
attachment = try(
module.vlan-attachments[lookup(local.ctx_vlan_attachments, replace(a.attachment, "$vlan_attachments:", ""), a.attachment)].id,
a.attachment
)
}
]
)
}
}
module "vlan-attachments" {
@@ -95,3 +176,28 @@ module "vlan-attachments" {
}
depends_on = [module.vpc-factory]
}
resource "google_compute_interconnect_attachment_group" "default" {
for_each = local.attachment_groups
project = lookup(
local.ctx_projects.project_ids,
replace(each.value.project_id, "$project_ids:", ""),
each.value.project_id
)
name = each.value.name
description = try(each.value.description, "Terraform-managed.")
intent {
availability_sla = try(each.value.intent.availability_sla, "NO_SLA")
}
dynamic "attachments" {
for_each = local._attachment_groups_attachments[each.key]
content {
name = attachments.value.name
attachment = attachments.value.attachment
}
}
depends_on = [module.vlan-attachments]
}

View File

@@ -0,0 +1,56 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/blob/master/fast/stages/2-networking/schemas/attachment-groups.schema.json",
"title": "Attachment Groups schema",
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
},
"project_id": {
"type": "string"
},
"description": {
"type": "string"
},
"intent": {
"type": "object",
"additionalProperties": false,
"properties": {
"availability_sla": {
"type": "string",
"enum": [
"NO_SLA",
"PRODUCTION_NON_CRITICAL",
"PRODUCTION_CRITICAL",
"AVAILABILITY_SLA_UNSPECIFIED"
],
"default": "NO_SLA"
}
}
},
"attachments": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-zA-Z0-9-_]+$": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"attachment"
],
"properties": {
"name": {
"type": "string"
},
"attachment": {
"type": "string"
}
}
}
}
}
}
}

View File

@@ -0,0 +1,23 @@
# Attachment Groups schema
<!-- markdownlint-disable MD036 -->
## Properties
*additional properties: false*
- **name**: *string*
- **project_id**: *string*
- **description**: *string*
- **intent**: *object*
<br>*additional properties: false*
- **availability_sla**: *string*
<br>*default: NO_SLA*, *enum: ['NO_SLA', 'PRODUCTION_NON_CRITICAL', 'PRODUCTION_CRITICAL', 'AVAILABILITY_SLA_UNSPECIFIED']*
- **attachments**: *object*
<br>*additional properties: false*
- **`^[a-zA-Z0-9-_]+$`**: *object*
<br>*additional properties: false*
- ⁺**name**: *string*
- ⁺**attachment**: *string*
## Definitions

View File

@@ -13,6 +13,9 @@
"type": "boolean",
"default": true
},
"attachment_group": {
"type": "string"
},
"dedicated_interconnect_config": {
"type": "object",
"additionalProperties": false,

View File

@@ -7,6 +7,7 @@
*additional properties: false*
- **admin_enabled**: *boolean*
- **attachment_group**: *string*
- **dedicated_interconnect_config**: *object*
<br>*additional properties: false*
- **bandwidth**: *string*

View File

@@ -25,6 +25,9 @@
"firewall_rules": {
"type": "string"
},
"attachment_groups": {
"type": "string"
},
"subnets": {
"type": "string"
},

View File

@@ -12,6 +12,7 @@
- **factories_config**: *object*
<br>*additional properties: false*
- **firewall_rules**: *string*
- **attachment_groups**: *string*
- **subnets**: *string*
- **vlan_attachments**: *string*
- **vpns**: *string*