diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md
index 6e82910da9..7f9926601b 100644
--- a/modules/net-vpc/README.md
+++ b/modules/net-vpc/README.md
@@ -1,10 +1,20 @@
-# Minimalistic VPC module
+# VPC module
-This module allows creation and management of VPC networks including subnetworks and subnetwork IAM bindings, Shared VPC activation and service project registration, and one-to-one peering.
+This module allows creation and management of VPC networks including subnetworks and subnetwork IAM bindings, and most features and options related to VPCs and subnets.
## Examples
-The module allows for several different VPC configurations, some of the most common are shown below.
+- [Simple VPC](#simple-vpc)
+- [Subnet Options](#subnet-options)
+- [Subnet IAM](#subnet-iam)
+- [Peering](#peering)
+- [Shared VPC](#shared-vpc)
+- [Private Service Networking](#private-service-networking)
+- [Private Service Networking with Peering Routes](#private-service-networking-with-peering-routes)
+- [Subnets for Private Service Connect, Proxy-only subnets](#subnets-for-private-service-connect-proxy-only-subnets)
+- [DNS Policies](#dns-policies)
+- [Subnet Factory](#subnet-factory)
+- [Custom Routes](#custom-routes)
### Simple VPC
@@ -105,6 +115,8 @@ module "vpc" {
"user:user1@example.com", "group:group1@example.com"
]
}
+ }
+ subnet_iam_additive = {
"europe-west1/subnet-2" = {
"roles/compute.networkUser" = [
"user:user2@example.com", "group:group2@example.com"
@@ -112,7 +124,7 @@ module "vpc" {
}
}
}
-# tftest modules=1 resources=5 inventory=subnet-iam.yaml
+# tftest modules=1 resources=6 inventory=subnet-iam.yaml
```
### Peering
@@ -315,7 +327,7 @@ module "vpc" {
name = "my-network"
data_folder = "config/subnets"
}
-# tftest modules=1 resources=7 files=subnet-simple,subnet-simple-2,subnet-detailed,subnet-proxy,subnet-psc inventory=factory.yaml
+# tftest modules=1 resources=9 files=subnet-simple,subnet-simple-2,subnet-detailed,subnet-proxy,subnet-psc inventory=factory.yaml
```
```yaml
@@ -338,13 +350,17 @@ region: europe-west1
description: Sample description
ip_cidr_range: 10.0.0.0/24
# optional attributes
-enable_private_access: false # defaults to true
-iam_users: ["foobar@example.com"] # grant compute/networkUser to users
-iam_groups: ["lorem@example.com"] # grant compute/networkUser to groups
-iam_service_accounts: ["fbz@prj.iam.gserviceaccount.com"]
-secondary_ip_ranges: # map of secondary ip ranges
+enable_private_access: false # defaults to true
+iam: # grant roles/compute.networkUser
+ - group:lorem@example.com
+ - serviceAccount:fbz@prj.iam.gserviceaccount.com
+ - user:foobar@example.com
+iam_additive: # grant roles/compute.networkUser
+ - user:foo@example.com
+ - serviceAccount:fbx@prj.iam.gserviceaccount.com
+secondary_ip_ranges: # map of secondary ip ranges
secondary-range-a: 192.168.0.0/24
-flow_logs: # enable, set to empty map to use defaults
+flow_logs: # enable, set to empty map to use defaults
aggregation_interval: "INTERVAL_5_SEC"
flow_sampling: 0.5
metadata: "INCLUDE_ALL_METADATA"
@@ -402,6 +418,7 @@ module "vpc" {
}
# tftest modules=5 resources=15 inventory=routes.yaml
```
+
## Variables
@@ -422,10 +439,11 @@ module "vpc" {
| [shared_vpc_host](variables.tf#L121) | Enable shared VPC for this project. | bool
| | false
|
| [shared_vpc_service_projects](variables.tf#L127) | Shared VPC service projects to register with this host. | list(string)
| | []
|
| [subnet_iam](variables.tf#L133) | Subnet IAM bindings in {REGION/NAME => {ROLE => [MEMBERS]} format. | map(map(list(string)))
| | {}
|
-| [subnets](variables.tf#L139) | Subnet configuration. | list(object({…}))
| | []
|
-| [subnets_proxy_only](variables.tf#L164) | List of proxy-only subnets for Regional HTTPS or Internal HTTPS load balancers. Note: Only one proxy-only subnet for each VPC network in each region can be active. | list(object({…}))
| | []
|
-| [subnets_psc](variables.tf#L176) | List of subnets for Private Service Connect service producers. | list(object({…}))
| | []
|
-| [vpc_create](variables.tf#L187) | Create VPC. When set to false, uses a data source to reference existing VPC. | bool
| | true
|
+| [subnet_iam_additive](variables.tf#L139) | Subnet IAM additive bindings in {REGION/NAME => {ROLE => [MEMBERS]}} format. | map(map(list(string)))
| | {}
|
+| [subnets](variables.tf#L146) | Subnet configuration. | list(object({…}))
| | []
|
+| [subnets_proxy_only](variables.tf#L171) | List of proxy-only subnets for Regional HTTPS or Internal HTTPS load balancers. Note: Only one proxy-only subnet for each VPC network in each region can be active. | list(object({…}))
| | []
|
+| [subnets_psc](variables.tf#L183) | List of subnets for Private Service Connect service producers. | list(object({…}))
| | []
|
+| [vpc_create](variables.tf#L194) | Create VPC. When set to false, uses a data source to reference existing VPC. | bool
| | true
|
## Outputs
@@ -445,4 +463,3 @@ module "vpc" {
| [subnets_psc](outputs.tf#L112) | Private Service Connect subnet resources. | |
-The key format is `subnet_region/subnet_name`. For example `europe-west1/my_subnet`.
diff --git a/modules/net-vpc/subnets.tf b/modules/net-vpc/subnets.tf
index 7719d298e1..322a7c4003 100644
--- a/modules/net-vpc/subnets.tf
+++ b/modules/net-vpc/subnets.tf
@@ -31,24 +31,39 @@ locals {
flow_logs_config = try(v.flow_logs, null)
ipv6 = try(v.ipv6, null)
secondary_ip_ranges = try(v.secondary_ip_ranges, null)
- iam_groups = try(v.iam_groups, [])
- iam_users = try(v.iam_users, [])
- iam_service_accounts = try(v.iam_service_accounts, [])
+ iam = try(v.iam, [])
+ iam_additive = try(v.iam_additive, [])
purpose = try(v.purpose, null)
active = try(v.active, null)
}
}
+ _factory_subnets_iam_additive = flatten([
+ for k, v in local._factory_subnets : [
+ for member in lookup(v, "iam_additive", []) : {
+ member = member
+ subnet = k
+ role = "roles/compute.networkUser"
+ }
+ ] if v.purpose == null
+ ])
_factory_subnets_iam = [
for k, v in local._factory_subnets : {
- subnet = k
- role = "roles/compute.networkUser"
- members = concat(
- formatlist("group:%s", lookup(v, "iam_groups", [])),
- formatlist("user:%s", lookup(v, "iam_users", [])),
- formatlist("serviceAccount:%s", lookup(v, "iam_service_accounts", []))
- )
- } if v.purpose == null
+ subnet = k
+ role = "roles/compute.networkUser"
+ members = v.iam
+ } if v.purpose == null && v.iam != null
]
+ _subnet_iam_additive_members = flatten([
+ for subnet, roles in var.subnet_iam_additive : [
+ for role, members in roles : [
+ for member in members : {
+ member = member
+ role = role
+ subnet = subnet
+ }
+ ]
+ ]
+ ])
_subnet_iam_members = flatten([
for subnet, roles in(var.subnet_iam == null ? {} : var.subnet_iam) : [
for role, members in roles : {
@@ -58,6 +73,10 @@ locals {
}
]
])
+ subnet_iam_additive_members = concat(
+ local._factory_subnets_iam_additive,
+ local._subnet_iam_additive_members
+ )
subnet_iam_members = concat(
[for k in local._factory_subnets_iam : k if length(k.members) > 0],
local._subnet_iam_members
@@ -151,3 +170,15 @@ resource "google_compute_subnetwork_iam_binding" "binding" {
role = each.value.role
members = each.value.members
}
+
+resource "google_compute_subnetwork_iam_member" "binding" {
+ for_each = {
+ for binding in local.subnet_iam_additive_members :
+ "${binding.subnet}.${binding.role}.${binding.member}" => binding
+ }
+ project = var.project_id
+ subnetwork = google_compute_subnetwork.subnetwork[each.value.subnet].name
+ region = google_compute_subnetwork.subnetwork[each.value.subnet].region
+ role = each.value.role
+ member = each.value.member
+}
diff --git a/modules/net-vpc/variables.tf b/modules/net-vpc/variables.tf
index a7aa207721..e05ece3f72 100644
--- a/modules/net-vpc/variables.tf
+++ b/modules/net-vpc/variables.tf
@@ -136,6 +136,13 @@ variable "subnet_iam" {
default = {}
}
+variable "subnet_iam_additive" {
+ description = "Subnet IAM additive bindings in {REGION/NAME => {ROLE => [MEMBERS]}} format."
+ type = map(map(list(string)))
+ default = {}
+ nullable = false
+}
+
variable "subnets" {
description = "Subnet configuration."
type = list(object({
diff --git a/tests/modules/net_vpc/examples/subnet-iam.yaml b/tests/modules/net_vpc/examples/subnet-iam.yaml
index cb53ecd80a..ce853c71d8 100644
--- a/tests/modules/net_vpc/examples/subnet-iam.yaml
+++ b/tests/modules/net_vpc/examples/subnet-iam.yaml
@@ -34,11 +34,16 @@ values:
region: europe-west1
role: roles/compute.networkUser
subnetwork: subnet-1
- module.vpc.google_compute_subnetwork_iam_binding.binding["europe-west1/subnet-2.roles/compute.networkUser"]:
+ module.vpc.google_compute_subnetwork_iam_member.binding["europe-west1/subnet-2.roles/compute.networkUser.user:user2@example.com"]:
condition: []
- members:
- - group:group2@example.com
- - user:user2@example.com
+ member: user:user2@example.com
+ project: my-project
+ region: europe-west1
+ role: roles/compute.networkUser
+ subnetwork: subnet-2
+ module.vpc.google_compute_subnetwork_iam_member.binding["europe-west1/subnet-2.roles/compute.networkUser.group:group2@example.com"]:
+ condition: []
+ member: group:group2@example.com
project: my-project
region: europe-west1
role: roles/compute.networkUser
@@ -47,8 +52,7 @@ values:
counts:
google_compute_network: 1
google_compute_subnetwork: 2
- google_compute_subnetwork_iam_binding: 2
- modules: 1
- resources: 5
+ google_compute_subnetwork_iam_binding: 1
+ google_compute_subnetwork_iam_member: 2
outputs: {}