Add support for `iam_additive` and simplify factory interface in net VPC module (#1259)

* initial implementation, no tests

* change interface, align tests

* add examples ToC

* fix variable type, test module-level variable
This commit is contained in:
Ludovico Magnocavallo 2023-03-17 11:12:34 +01:00 committed by GitHub
parent 50adf1da2a
commit 8a8b7ea35f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 93 additions and 34 deletions

View File

@ -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
```
<!-- BEGIN TFDOC -->
## Variables
@ -422,10 +439,11 @@ module "vpc" {
| [shared_vpc_host](variables.tf#L121) | Enable shared VPC for this project. | <code>bool</code> | | <code>false</code> |
| [shared_vpc_service_projects](variables.tf#L127) | Shared VPC service projects to register with this host. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [subnet_iam](variables.tf#L133) | Subnet IAM bindings in {REGION/NAME => {ROLE => [MEMBERS]} format. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [subnets](variables.tf#L139) | Subnet configuration. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10; enable_private_access &#61; optional&#40;bool, true&#41;&#10; flow_logs_config &#61; optional&#40;object&#40;&#123;&#10; aggregation_interval &#61; optional&#40;string&#41;&#10; filter_expression &#61; optional&#40;string&#41;&#10; flow_sampling &#61; optional&#40;number&#41;&#10; metadata &#61; optional&#40;string&#41;&#10; metadata_fields &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; ipv6 &#61; optional&#40;object&#40;&#123;&#10; access_type &#61; optional&#40;string&#41;&#10; enable_private_access &#61; optional&#40;bool, true&#41;&#10; &#125;&#41;&#41;&#10; secondary_ip_ranges &#61; optional&#40;map&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [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. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10; active &#61; bool&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [subnets_psc](variables.tf#L176) | List of subnets for Private Service Connect service producers. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [vpc_create](variables.tf#L187) | Create VPC. When set to false, uses a data source to reference existing VPC. | <code>bool</code> | | <code>true</code> |
| [subnet_iam_additive](variables.tf#L139) | Subnet IAM additive bindings in {REGION/NAME => {ROLE => [MEMBERS]}} format. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [subnets](variables.tf#L146) | Subnet configuration. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10; enable_private_access &#61; optional&#40;bool, true&#41;&#10; flow_logs_config &#61; optional&#40;object&#40;&#123;&#10; aggregation_interval &#61; optional&#40;string&#41;&#10; filter_expression &#61; optional&#40;string&#41;&#10; flow_sampling &#61; optional&#40;number&#41;&#10; metadata &#61; optional&#40;string&#41;&#10; metadata_fields &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; ipv6 &#61; optional&#40;object&#40;&#123;&#10; access_type &#61; optional&#40;string&#41;&#10; enable_private_access &#61; optional&#40;bool, true&#41;&#10; &#125;&#41;&#41;&#10; secondary_ip_ranges &#61; optional&#40;map&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [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. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10; active &#61; bool&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [subnets_psc](variables.tf#L183) | List of subnets for Private Service Connect service producers. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [vpc_create](variables.tf#L194) | Create VPC. When set to false, uses a data source to reference existing VPC. | <code>bool</code> | | <code>true</code> |
## Outputs
@ -445,4 +463,3 @@ module "vpc" {
| [subnets_psc](outputs.tf#L112) | Private Service Connect subnet resources. | |
<!-- END TFDOC -->
The key format is `subnet_region/subnet_name`. For example `europe-west1/my_subnet`.

View File

@ -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
}

View File

@ -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({

View File

@ -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: {}