diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 1aaa749de..a18d5ae76 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -41,40 +41,36 @@ env:
DEFAULT_TOFU_VERSION: "1.10.0"
jobs:
- compute-matrix:
- runs-on: ubuntu-latest
- outputs:
- DEFAULT_TERRAFORM_FLAVOUR: ${{ env.DEFAULT_TERRAFORM_FLAVOUR }}
- DEFAULT_TERRAFORM_VERSION: ${{ env.DEFAULT_TERRAFORM_VERSION }}
- DEFAULT_TOFU_VERSION: ${{ env.DEFAULT_TOFU_VERSION }}
- steps:
- - name: dummy
- run: echo
-
setup-tf-providers:
runs-on: ubuntu-latest
- needs:
- - compute-matrix
strategy:
matrix:
include:
- flavour: terraform
- version: "${{ needs.compute-matrix.outputs.DEFAULT_TERRAFORM_VERSION }}"
- flavour: tofu
- version: "${{ needs.compute-matrix.outputs.DEFAULT_TOFU_VERSION }}"
steps:
- uses: actions/checkout@v4
+ - name: Set Terraform versions
+ run: |
+ set -e -o xtrace
+ if [[ ${{ matrix.flavour }} == 'terraform' ]] ; then
+ echo TERRAFORM_VERSION=${{ env.DEFAULT_TERRAFORM_VERSION }} | tee -a ${GITHUB_ENV}
+ elif [[ ${{ matrix.flavour }} == 'tofu' ]] ; then
+ echo TERRAFORM_VERSION=${{ env.DEFAULT_TOFU_VERSION }} | tee -a ${GITHUB_ENV}
+ else
+ echo TERRAFORM_VERSION=unkown_flavor | tee -a ${GITHUB_ENV}
+ fi
- uses: hashicorp/setup-terraform@v3
if: ${{ matrix.flavour == 'terraform' }}
with:
- terraform_version: ${{ matrix.version }}
+ terraform_version: ${{ env.TERRAFORM_VERSION }}
terraform_wrapper: false
- uses: opentofu/setup-opentofu@v1
if: ${{ matrix.flavour == 'tofu' }}
with:
- tofu_version: ${{ matrix.version }}
+ tofu_version: ${{ env.TERRAFORM_VERSION }}
tofu_wrapper: false
- name: Build lockfile and fetch providers
@@ -86,7 +82,7 @@ jobs:
sed -i -e 's/>=\(.*# tftest\)/=\1/g' tools/lockfile/versions.tf tools/lockfile/versions.tofu
# change terraform version to the one that is running
- sed -i 's/required_version = .*$/required_version = ">= ${{ matrix.version }}"/g' tools/lockfile/versions.tf
+ sed -i 's/required_version = .*$/required_version = ">= ${{ env.TERRAFORM_VERSION }}"/g' tools/lockfile/versions.tf
cd tools/lockfile
${{ matrix.flavour }} init -upgrade=true
@@ -95,12 +91,12 @@ jobs:
uses: actions/cache@v4
with:
path: ${{ env.TF_PLUGIN_CACHE_DIR }}
- key: ${{ runner.os }}-${{ matrix.flavour }}-${{ matrix.version }}-${{ hashFiles('tools/lockfile/.terraform.lock.hcl') }}
+ key: ${{ runner.os }}-${{ matrix.flavour }}-${{ env.TERRAFORM_VERSION }}-${{ hashFiles('tools/lockfile/.terraform.lock.hcl') }}
- name: Upload lockfile
uses: actions/upload-artifact@v4
with:
- name: lockfile-${{ runner.os }}-${{ matrix.flavour }}-${{ matrix.version }}
+ name: lockfile-${{ runner.os }}-${{ matrix.flavour }}-${{ env.TERRAFORM_VERSION }}
path: tools/lockfile/.terraform.lock.hcl
overwrite: true
include-hidden-files: true
@@ -131,23 +127,30 @@ jobs:
runs-on: ubuntu-latest
needs:
- setup-tf-providers
- - compute-matrix
strategy:
matrix:
include:
- flavour: terraform
- version: "${{ needs.compute-matrix.outputs.DEFAULT_TERRAFORM_VERSION }}"
- flavour: tofu
- version: "${{ needs.compute-matrix.outputs.DEFAULT_TOFU_VERSION }}"
steps:
- uses: actions/checkout@v4
+ - name: Set Terraform versions
+ run: |
+ set -e -o xtrace
+ if [[ ${{ matrix.flavour }} == 'terraform' ]] ; then
+ echo TERRAFORM_VERSION=${{ env.DEFAULT_TERRAFORM_VERSION }} | tee -a ${GITHUB_ENV}
+ elif [[ ${{ matrix.flavour }} == 'tofu' ]] ; then
+ echo TERRAFORM_VERSION=${{ env.DEFAULT_TOFU_VERSION }} | tee -a ${GITHUB_ENV}
+ else
+ echo TERRAFORM_VERSION=unkown_flavor | tee -a ${GITHUB_ENV}
+ fi
- name: Call composite action fabric-tests
uses: ./.github/actions/fabric-tests
with:
PYTHON_VERSION: ${{ env.PYTHON_VERSION }}
- TERRAFORM_VERSION: ${{ matrix.version }}
+ TERRAFORM_VERSION: ${{ env.TERRAFORM_VERSION }}
TERRAFORM_FLAVOUR: ${{ matrix.flavour }}
- name: Run tests on documentation examples
@@ -165,17 +168,24 @@ jobs:
runs-on: ubuntu-latest
needs:
- setup-tf-providers
- - compute-matrix
strategy:
matrix:
include:
- flavour: terraform
- version: "${{ needs.compute-matrix.outputs.DEFAULT_TERRAFORM_VERSION }}"
- flavour: tofu
- version: "${{ needs.compute-matrix.outputs.DEFAULT_TOFU_VERSION }}"
steps:
- uses: actions/checkout@v4
+ - name: Set Terraform versions
+ run: |
+ set -e -o xtrace
+ if [[ ${{ matrix.flavour }} == 'terraform' ]] ; then
+ echo TERRAFORM_VERSION=${{ env.DEFAULT_TERRAFORM_VERSION }} | tee -a ${GITHUB_ENV}
+ elif [[ ${{ matrix.flavour }} == 'tofu' ]] ; then
+ echo TERRAFORM_VERSION=${{ env.DEFAULT_TOFU_VERSION }} | tee -a ${GITHUB_ENV}
+ else
+ echo TERRAFORM_VERSION=unkown_flavor | tee -a ${GITHUB_ENV}
+ fi
- name: Call composite action fabric-tests
uses: ./.github/actions/fabric-tests
@@ -183,7 +193,7 @@ jobs:
TERRAFORM: ${{ matrix.flavour }}
with:
PYTHON_VERSION: ${{ env.PYTHON_VERSION }}
- TERRAFORM_VERSION: ${{ matrix.version }}
+ TERRAFORM_VERSION: ${{ env.TERRAFORM_VERSION }}
TERRAFORM_FLAVOUR: ${{ matrix.flavour }}
- name: Run tests modules
@@ -200,15 +210,30 @@ jobs:
fast:
runs-on: ubuntu-latest
needs: setup-tf-providers
+ strategy:
+ matrix:
+ include:
+ - flavour: terraform
+ # - flavour: tofu # tofu fails to find the terraform binary for FAST tests
steps:
- uses: actions/checkout@v4
+ - name: Set Terraform versions
+ run: |
+ set -e -o xtrace
+ if [[ ${{ matrix.flavour }} == 'terraform' ]] ; then
+ echo TERRAFORM_VERSION=${{ env.DEFAULT_TERRAFORM_VERSION }} | tee -a ${GITHUB_ENV}
+ elif [[ ${{ matrix.flavour }} == 'tofu' ]] ; then
+ echo TERRAFORM_VERSION=${{ env.DEFAULT_TOFU_VERSION }} | tee -a ${GITHUB_ENV}
+ else
+ echo TERRAFORM_VERSION=unkown_flavor | tee -a ${GITHUB_ENV}
+ fi
- name: Call composite action fabric-tests
uses: ./.github/actions/fabric-tests
with:
PYTHON_VERSION: ${{ env.PYTHON_VERSION }}
- TERRAFORM_VERSION: ${{ env.DEFAULT_TERRAFORM_VERSION }}
- TERRAFORM_FLAVOUR: ${{ env.DEFAULT_TERRAFORM_FLAVOUR }}
+ TERRAFORM_VERSION: ${{ env.TERRAFORM_VERSION }}
+ TERRAFORM_FLAVOUR: ${{ matrix.flavour }}
- name: Run tests on FAST stages
run: pytest -vv -n4 --tb=line --junit-xml=test-results-raw.xml tests/fast
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c92c7d7e2..f90d93d26 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -34,6 +34,12 @@ All notable changes to this project will be documented in this file.
### MODULES
+- [[#3419](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3419)] Add support for context to net-lb-int net-vpc-firewall and net-vpc module ([ludoo](https://github.com/ludoo))
+- [[#3414](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3414)] Add support for context to net-cloudnat, net-firewall-policy modules ([ludoo](https://github.com/ludoo))
+- [[#3413](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3413)] Make SSM gitignores a list ([juliocc](https://github.com/juliocc))
+- [[#3412](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3412)] Add support for context to DNS modules ([ludoo](https://github.com/ludoo))
+- [[#3403](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3403)] modules fixes for E2E tests ([wiktorn](https://github.com/wiktorn))
+- [[#3406](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3406)] Add support for contexts to compute-vm module ([ludoo](https://github.com/ludoo))
- [[#3332](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3332)] Update gke-hub module to use new Policy Controller API ([juliocc](https://github.com/juliocc))
- [[#3402](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3402)] Fix incorrect cloudservices agent email for global universe in project module ([ludoo](https://github.com/ludoo))
- [[#3388](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3388)] Add support for context to bigquery module ([ludoo](https://github.com/ludoo))
@@ -56,6 +62,7 @@ All notable changes to this project will be documented in this file.
### TOOLS
+- [[#3407](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3407)] remove tf version from matrix, to keep workflow names stable across upgrades ([wiktorn](https://github.com/wiktorn))
- [[#3332](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3332)] Update gke-hub module to use new Policy Controller API ([juliocc](https://github.com/juliocc))
- [[#3404](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3404)] Add tests for service agents iam_emails ([wiktorn](https://github.com/wiktorn))
diff --git a/modules/alloydb/main.tf b/modules/alloydb/main.tf
index 1b04f6161..3de00cbb5 100644
--- a/modules/alloydb/main.tf
+++ b/modules/alloydb/main.tf
@@ -254,6 +254,7 @@ resource "google_alloydb_cluster" "secondary" {
cluster_type = var.cross_region_replication.promote_secondary || var.cross_region_replication.switchover_mode ? "PRIMARY" : "SECONDARY"
database_version = var.database_version
deletion_policy = "FORCE"
+ deletion_protection = var.deletion_protection
display_name = coalesce(var.cross_region_replication.secondary_cluster_display_name, local.secondary_cluster_name)
labels = var.labels
location = var.cross_region_replication.region
diff --git a/modules/compute-vm/README.md b/modules/compute-vm/README.md
index 0844c5997..b2f627fe5 100644
--- a/modules/compute-vm/README.md
+++ b/modules/compute-vm/README.md
@@ -937,41 +937,42 @@ module "sole-tenancy" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L266) | Instance name. | string | ✓ | |
-| [network_interfaces](variables.tf#L278) | Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed. | list(object({…})) | ✓ | |
-| [project_id](variables.tf#L363) | Project id. | string | ✓ | |
-| [zone](variables.tf#L483) | Compute zone. | string | ✓ | |
+| [name](variables.tf#L283) | Instance name. | string | ✓ | |
+| [network_interfaces](variables.tf#L295) | Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed. | list(object({…})) | ✓ | |
+| [project_id](variables.tf#L380) | Project id. | string | ✓ | |
+| [zone](variables.tf#L500) | Compute zone. | string | ✓ | |
| [attached_disk_defaults](variables.tf#L17) | Defaults for attached disks options. | object({…}) | | {…} |
| [attached_disks](variables.tf#L37) | Additional disks, if options is null defaults will be used in its place. Source type is one of 'image' (zonal disks in vms and template), 'snapshot' (vm), 'existing', and null. | list(object({…})) | | [] |
| [boot_disk](variables.tf#L82) | Boot disk properties. Initialize params are ignored when source is set. | object({…}) | | {…} |
| [can_ip_forward](variables.tf#L113) | Enable IP forwarding. | bool | | false |
| [confidential_compute](variables.tf#L119) | Enable Confidential Compute for these instances. | bool | | false |
-| [create_template](variables.tf#L125) | Create instance template instead of instances. Defaults to a global template. | object({…}) | | null |
-| [description](variables.tf#L134) | Description of a Compute Instance. | string | | "Managed by the compute-vm Terraform module." |
-| [enable_display](variables.tf#L140) | Enable virtual display on the instances. | bool | | false |
-| [encryption](variables.tf#L146) | Encryption options. Only one of kms_key_self_link and disk_encryption_key_raw may be set. If needed, you can specify to encrypt or not the boot disk. | object({…}) | | null |
-| [gpu](variables.tf#L156) | GPU information. Based on https://cloud.google.com/compute/docs/gpus. | object({…}) | | null |
-| [group](variables.tf#L191) | Define this variable to create an instance group for instances. Disabled for template use. | object({…}) | | null |
-| [hostname](variables.tf#L199) | Instance FQDN name. | string | | null |
-| [iam](variables.tf#L205) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} |
-| [instance_schedule](variables.tf#L211) | Assign or create and assign an instance schedule policy. Either resource policy id or create_config must be specified if not null. Set active to null to dtach a policy from vm before destroying. | object({…}) | | null |
-| [instance_type](variables.tf#L235) | Instance type. | string | | "f1-micro" |
-| [labels](variables.tf#L241) | Instance labels. | map(string) | | {} |
-| [metadata](variables.tf#L247) | Instance metadata. | map(string) | | {} |
-| [metadata_startup_script](variables.tf#L253) | Instance startup script. Will trigger recreation on change, even after importing. | string | | null |
-| [min_cpu_platform](variables.tf#L260) | Minimum CPU platform. | string | | null |
-| [network_attached_interfaces](variables.tf#L271) | Network interfaces using network attachments. | list(string) | | [] |
-| [network_tag_bindings](variables.tf#L299) | Resource manager tag bindings in arbitrary key => tag key or value id format. Set on both the instance only for networking purposes, and modifiable without impacting the main resource lifecycle. | map(string) | | {} |
-| [options](variables.tf#L306) | Instance options. | object({…}) | | {…} |
-| [project_number](variables.tf#L368) | Project number. Used in tag bindings to avoid a permadiff. | string | | null |
-| [resource_policies](variables.tf#L374) | Resource policies to attach to the instance or template. | list(string) | | null |
-| [scratch_disks](variables.tf#L381) | Scratch disks configuration. | object({…}) | | {…} |
-| [service_account](variables.tf#L393) | Service account email and scopes. If email is null, the default Compute service account will be used unless auto_create is true, in which case a service account will be created. Set the variable to null to avoid attaching a service account. | object({…}) | | {} |
-| [shielded_config](variables.tf#L403) | Shielded VM configuration of the instances. | object({…}) | | null |
-| [snapshot_schedules](variables.tf#L413) | Snapshot schedule resource policies that can be attached to disks. | map(object({…})) | | {} |
-| [tag_bindings](variables.tf#L456) | Resource manager tag bindings in arbitrary key => tag key or value id format. Set on both the instance and zonal disks, and modifiable without impacting the main resource lifecycle. | map(string) | | {} |
-| [tag_bindings_immutable](variables.tf#L463) | Immutable resource manager tag bindings, in tagKeys/id => tagValues/id format. These are set on the instance or instance template at creation time, and trigger recreation if changed. | map(string) | | null |
-| [tags](variables.tf#L477) | Instance network tags for firewall rule targets. | list(string) | | [] |
+| [context](variables.tf#L125) | Context-specific interpolations. | object({…}) | | {} |
+| [create_template](variables.tf#L142) | Create instance template instead of instances. Defaults to a global template. | object({…}) | | null |
+| [description](variables.tf#L151) | Description of a Compute Instance. | string | | "Managed by the compute-vm Terraform module." |
+| [enable_display](variables.tf#L157) | Enable virtual display on the instances. | bool | | false |
+| [encryption](variables.tf#L163) | Encryption options. Only one of kms_key_self_link and disk_encryption_key_raw may be set. If needed, you can specify to encrypt or not the boot disk. | object({…}) | | null |
+| [gpu](variables.tf#L173) | GPU information. Based on https://cloud.google.com/compute/docs/gpus. | object({…}) | | null |
+| [group](variables.tf#L208) | Define this variable to create an instance group for instances. Disabled for template use. | object({…}) | | null |
+| [hostname](variables.tf#L216) | Instance FQDN name. | string | | null |
+| [iam](variables.tf#L222) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} |
+| [instance_schedule](variables.tf#L228) | Assign or create and assign an instance schedule policy. Either resource policy id or create_config must be specified if not null. Set active to null to dtach a policy from vm before destroying. | object({…}) | | null |
+| [instance_type](variables.tf#L252) | Instance type. | string | | "f1-micro" |
+| [labels](variables.tf#L258) | Instance labels. | map(string) | | {} |
+| [metadata](variables.tf#L264) | Instance metadata. | map(string) | | {} |
+| [metadata_startup_script](variables.tf#L270) | Instance startup script. Will trigger recreation on change, even after importing. | string | | null |
+| [min_cpu_platform](variables.tf#L277) | Minimum CPU platform. | string | | null |
+| [network_attached_interfaces](variables.tf#L288) | Network interfaces using network attachments. | list(string) | | [] |
+| [network_tag_bindings](variables.tf#L316) | Resource manager tag bindings in arbitrary key => tag key or value id format. Set on both the instance only for networking purposes, and modifiable without impacting the main resource lifecycle. | map(string) | | {} |
+| [options](variables.tf#L323) | Instance options. | object({…}) | | {…} |
+| [project_number](variables.tf#L385) | Project number. Used in tag bindings to avoid a permadiff. | string | | null |
+| [resource_policies](variables.tf#L391) | Resource policies to attach to the instance or template. | list(string) | | null |
+| [scratch_disks](variables.tf#L398) | Scratch disks configuration. | object({…}) | | {…} |
+| [service_account](variables.tf#L410) | Service account email and scopes. If email is null, the default Compute service account will be used unless auto_create is true, in which case a service account will be created. Set the variable to null to avoid attaching a service account. | object({…}) | | {} |
+| [shielded_config](variables.tf#L420) | Shielded VM configuration of the instances. | object({…}) | | null |
+| [snapshot_schedules](variables.tf#L430) | Snapshot schedule resource policies that can be attached to disks. | map(object({…})) | | {} |
+| [tag_bindings](variables.tf#L473) | Resource manager tag bindings in arbitrary key => tag key or value id format. Set on both the instance and zonal disks, and modifiable without impacting the main resource lifecycle. | map(string) | | {} |
+| [tag_bindings_immutable](variables.tf#L480) | Immutable resource manager tag bindings, in tagKeys/id => tagValues/id format. These are set on the instance or instance template at creation time, and trigger recreation if changed. | map(string) | | null |
+| [tags](variables.tf#L494) | Instance network tags for firewall rule targets. | list(string) | | [] |
## Outputs
diff --git a/modules/compute-vm/main.tf b/modules/compute-vm/main.tf
index 2880e47b9..4535cce8f 100644
--- a/modules/compute-vm/main.tf
+++ b/modules/compute-vm/main.tf
@@ -15,6 +15,7 @@
*/
locals {
+ _region = join("-", slice(split("-", local.zone), 0, 2))
advanced_mf = var.options.advanced_machine_features
attached_disks = {
for i, disk in var.attached_disks :
@@ -30,18 +31,27 @@ locals {
for k, v in local.attached_disks :
k => v if try(v.options.replica_zone, null) == null
}
+ ctx = {
+ for k, v in var.context : k => {
+ for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv
+ }
+ }
+ ctx_p = "$"
+ gpu = var.gpu != null
on_host_maintenance = (
var.options.spot || var.confidential_compute || local.gpu
? "TERMINATE"
: "MIGRATE"
)
- region = join("-", slice(split("-", var.zone), 0, 2))
- gpu = var.gpu != null
+ project_id = lookup(local.ctx.project_ids, var.project_id, var.project_id)
+ region = lookup(local.ctx.locations, local._region, local._region)
service_account = var.service_account == null ? null : {
- email = (
- var.service_account.auto_create
+ email = (var.service_account.auto_create
? google_service_account.service_account[0].email
- : var.service_account.email
+ : try(
+ local.ctx.iam_principals[var.service_account.email],
+ var.service_account.email
+ )
)
scopes = (
var.service_account.scopes != null ? var.service_account.scopes : (
@@ -63,12 +73,13 @@ locals {
termination_action = (
var.options.spot || var.options.max_run_duration != null ? coalesce(var.options.termination_action, "STOP") : null
)
+ zone = lookup(local.ctx.locations, var.zone, var.zone)
}
resource "google_compute_disk" "boot" {
count = !local.template_create && var.boot_disk.use_independent_disk ? 1 : 0
- project = var.project_id
- zone = var.zone
+ project = local.project_id
+ zone = local.zone
# by default, GCP creates boot disks with the same name as instance, the deviation here is kept for backwards
# compatibility
name = "${var.name}-boot"
@@ -82,8 +93,12 @@ resource "google_compute_disk" "boot" {
dynamic "disk_encryption_key" {
for_each = var.encryption != null ? [""] : []
content {
- raw_key = var.encryption.disk_encryption_key_raw
- kms_key_self_link = var.encryption.kms_key_self_link
+ raw_key = var.encryption.disk_encryption_key_raw
+ kms_key_self_link = lookup(
+ local.ctx.kms_keys,
+ var.encryption.kms_key_self_link,
+ var.encryption.kms_key_self_link
+ )
}
}
}
@@ -93,8 +108,8 @@ resource "google_compute_disk" "disks" {
for k, v in local.attached_disks_zonal :
k => v if v.source_type != "attach"
}
- project = var.project_id
- zone = var.zone
+ project = local.project_id
+ zone = local.zone
name = "${var.name}-${each.key}"
type = each.value.options.type
size = each.value.size
@@ -107,8 +122,12 @@ resource "google_compute_disk" "disks" {
dynamic "disk_encryption_key" {
for_each = var.encryption != null ? [""] : []
content {
- raw_key = var.encryption.disk_encryption_key_raw
- kms_key_self_link = var.encryption.kms_key_self_link
+ raw_key = var.encryption.disk_encryption_key_raw
+ kms_key_self_link = lookup(
+ local.ctx.kms_keys,
+ var.encryption.kms_key_self_link,
+ var.encryption.kms_key_self_link
+ )
}
}
}
@@ -119,9 +138,9 @@ resource "google_compute_region_disk" "disks" {
for k, v in local.attached_disks_regional :
k => v if v.source_type != "attach"
}
- project = var.project_id
+ project = local.project_id
region = local.region
- replica_zones = [var.zone, each.value.options.replica_zone]
+ replica_zones = [local.zone, each.value.options.replica_zone]
name = "${var.name}-${each.key}"
type = each.value.options.type
size = each.value.size
@@ -136,7 +155,12 @@ resource "google_compute_region_disk" "disks" {
content {
raw_key = var.encryption.disk_encryption_key_raw
# TODO: check if self link works here
- kms_key_name = var.encryption.kms_key_self_link
+ kms_key_name = lookup(
+ local.ctx.kms_keys,
+ var.encryption.kms_key_self_link,
+ var.encryption.kms_key_self_link
+ )
+
}
}
}
@@ -144,8 +168,8 @@ resource "google_compute_region_disk" "disks" {
resource "google_compute_instance" "default" {
provider = google-beta
count = local.template_create ? 0 : 1
- project = var.project_id
- zone = var.zone
+ project = local.project_id
+ zone = local.zone
name = var.name
hostname = var.hostname
description = var.description
@@ -228,10 +252,20 @@ resource "google_compute_instance" "default" {
: var.boot_disk.source
)
disk_encryption_key_raw = (
- var.encryption != null ? var.encryption.disk_encryption_key_raw : null
+ var.encryption != null ?
+ try(
+ local.ctx.kms_keys[var.encryption.disk_encryption_key_raw],
+ var.encryption.disk_encryption_key_raw
+ )
+ : null
)
kms_key_self_link = (
- var.encryption != null ? var.encryption.kms_key_self_link : null
+ var.encryption != null
+ ? try(
+ local.ctx.kms_keys[var.encryption.kms_key_self_link],
+ var.encryption.kms_key_self_link
+ )
+ : null
)
dynamic "initialize_params" {
for_each = (
@@ -263,15 +297,27 @@ resource "google_compute_instance" "default" {
for_each = var.network_interfaces
iterator = config
content {
- network = config.value.network
- subnetwork = config.value.subnetwork
- network_ip = try(config.value.addresses.internal, null)
+ network = lookup(
+ local.ctx.networks, config.value.network, config.value.network
+ )
+ subnetwork = lookup(
+ local.ctx.subnets, config.value.subnetwork, config.value.subnetwork
+ )
+ network_ip = try(
+ local.ctx.addresses[config.value.addresses.internal],
+ config.value.addresses.internal,
+ null
+ )
nic_type = config.value.nic_type
stack_type = config.value.stack_type
dynamic "access_config" {
for_each = config.value.nat || config.value.network_tier != null ? [""] : []
content {
- nat_ip = try(config.value.addresses.external, null)
+ nat_ip = try(
+ local.ctx.addresses[config.value.addresses.external],
+ config.value.addresses.external,
+ null
+ )
network_tier = try(config.value.network_tier, null)
}
}
@@ -378,24 +424,26 @@ resource "google_compute_instance" "default" {
}
resource "google_compute_instance_iam_binding" "default" {
- project = var.project_id
+ project = local.project_id
for_each = var.iam
- zone = var.zone
+ zone = local.zone
instance_name = var.name
- role = each.key
- members = each.value
- depends_on = [google_compute_instance.default]
+ role = lookup(local.ctx.custom_roles, each.key, each.key)
+ members = [
+ for m in each.value : lookup(local.ctx.iam_principals, m, m)
+ ]
+ depends_on = [google_compute_instance.default]
}
resource "google_compute_instance_group" "unmanaged" {
count = var.group != null && !local.template_create ? 1 : 0
- project = var.project_id
+ project = local.project_id
network = (
length(var.network_interfaces) > 0
? var.network_interfaces[0].network
: ""
)
- zone = var.zone
+ zone = local.zone
name = var.name
description = var.description
instances = [google_compute_instance.default[0].self_link]
@@ -411,7 +459,7 @@ resource "google_compute_instance_group" "unmanaged" {
resource "google_service_account" "service_account" {
count = try(var.service_account.auto_create, null) == true ? 1 : 0
- project = var.project_id
+ project = local.project_id
account_id = "tf-vm-${var.name}"
display_name = "Terraform VM ${var.name}."
}
diff --git a/modules/compute-vm/outputs.tf b/modules/compute-vm/outputs.tf
index 79551b84a..7e3b97e88 100644
--- a/modules/compute-vm/outputs.tf
+++ b/modules/compute-vm/outputs.tf
@@ -57,7 +57,7 @@ output "internal_ips" {
output "login_command" {
description = "Command to SSH into the machine."
- value = "gcloud compute ssh --project ${var.project_id} --zone ${var.zone} ${var.name}"
+ value = "gcloud compute ssh --project ${local.project_id} --zone ${local.zone} ${var.name}"
}
output "self_link" {
diff --git a/modules/compute-vm/resource-policies.tf b/modules/compute-vm/resource-policies.tf
index fbabd667f..e717ecd74 100644
--- a/modules/compute-vm/resource-policies.tf
+++ b/modules/compute-vm/resource-policies.tf
@@ -46,8 +46,8 @@ locals {
resource "google_compute_resource_policy" "schedule" {
count = var.instance_schedule != null ? 1 : 0
- project = var.project_id
- region = substr(var.zone, 0, length(var.zone) - 2)
+ project = local.project_id
+ region = substr(local.zone, 0, length(local.zone) - 2)
name = var.name
description = coalesce(
var.instance_schedule.description, "Schedule policy for ${var.name}."
@@ -73,8 +73,8 @@ resource "google_compute_resource_policy" "schedule" {
resource "google_compute_resource_policy" "snapshot" {
for_each = var.snapshot_schedules
- project = var.project_id
- region = substr(var.zone, 0, length(var.zone) - 2)
+ project = local.project_id
+ region = substr(local.zone, 0, length(local.zone) - 2)
name = "${var.name}-${each.key}"
description = coalesce(
each.value.description, "Schedule policy ${each.key} for ${var.name}."
@@ -132,8 +132,8 @@ resource "google_compute_resource_policy" "snapshot" {
resource "google_compute_disk_resource_policy_attachment" "boot" {
for_each = var.boot_disk.snapshot_schedule != null ? toset(var.boot_disk.snapshot_schedule) : []
- project = var.project_id
- zone = var.zone
+ project = local.project_id
+ zone = local.zone
name = try(
google_compute_resource_policy.snapshot[each.value].name,
each.value
@@ -153,8 +153,8 @@ resource "google_compute_disk_resource_policy_attachment" "attached" {
"${attachment.disk_key}-${attachment.snapshot_schedule}" => attachment
}
- project = var.project_id
- zone = var.zone
+ project = local.project_id
+ zone = local.zone
name = try(
google_compute_resource_policy.snapshot[each.value.snapshot_schedule].name,
each.value.snapshot_schedule
@@ -176,7 +176,7 @@ resource "google_compute_region_disk_resource_policy_attachment" "attached" {
"${attachment.disk_key}-${attachment.snapshot_schedule}" => attachment
}
- project = var.project_id
+ project = local.project_id
name = try(
google_compute_resource_policy.snapshot[each.value.snapshot_schedule].name,
each.value.snapshot_schedule
diff --git a/modules/compute-vm/tags.tf b/modules/compute-vm/tags.tf
index e06c9f8c0..d5a6a4fb7 100644
--- a/modules/compute-vm/tags.tf
+++ b/modules/compute-vm/tags.tf
@@ -46,7 +46,7 @@ locals {
])
tag_parent_base = format(
"//compute.googleapis.com/projects/%s",
- coalesce(var.project_number, var.project_id)
+ coalesce(var.project_number, local.project_id)
)
}
@@ -55,19 +55,19 @@ locals {
resource "google_tags_location_tag_binding" "network" {
for_each = local.template_create ? {} : var.network_tag_bindings
parent = (
- "${local.tag_parent_base}/zones/${var.zone}/instances/${google_compute_instance.default[0].instance_id}"
+ "${local.tag_parent_base}/zones/${local.zone}/instances/${google_compute_instance.default[0].instance_id}"
)
- tag_value = each.value
- location = var.zone
+ tag_value = lookup(local.ctx.tag_values, each.value, each.value)
+ location = local.zone
}
resource "google_tags_location_tag_binding" "instance" {
for_each = local.template_create ? {} : var.tag_bindings
parent = (
- "${local.tag_parent_base}/zones/${var.zone}/instances/${google_compute_instance.default[0].instance_id}"
+ "${local.tag_parent_base}/zones/${local.zone}/instances/${google_compute_instance.default[0].instance_id}"
)
- tag_value = each.value
- location = var.zone
+ tag_value = lookup(local.ctx.tag_values, each.value, each.value)
+ location = local.zone
}
resource "google_tags_location_tag_binding" "boot_disks" {
@@ -75,10 +75,10 @@ resource "google_tags_location_tag_binding" "boot_disks" {
local.template_create ? {} : { for v in local.boot_disk_tags : v.key => v }
)
parent = (
- "${local.tag_parent_base}/zones/${var.zone}/disks/${each.value.disk_id}"
+ "${local.tag_parent_base}/zones/${local.zone}/disks/${each.value.disk_id}"
)
- tag_value = each.value.tag_value
- location = var.zone
+ tag_value = lookup(local.ctx.tag_values, each.value.tag_value, each.value.tag_value)
+ location = local.zone
}
resource "google_tags_location_tag_binding" "disks" {
@@ -86,10 +86,10 @@ resource "google_tags_location_tag_binding" "disks" {
local.template_create ? {} : { for v in local.disk_tags : v.key => v }
)
parent = (
- "${local.tag_parent_base}/zones/${var.zone}/disks/${each.value.disk_id}"
+ "${local.tag_parent_base}/zones/${local.zone}/disks/${each.value.disk_id}"
)
- tag_value = each.value.tag_value
- location = var.zone
+ tag_value = lookup(local.ctx.tag_values, each.value.tag_value, each.value.tag_value)
+ location = local.zone
}
resource "google_tags_location_tag_binding" "disks_regional" {
@@ -99,7 +99,7 @@ resource "google_tags_location_tag_binding" "disks_regional" {
parent = (
"${local.tag_parent_base}/regions/${local.region}/disks/${each.value.disk_id}"
)
- tag_value = each.value.tag_value
+ tag_value = lookup(local.ctx.tag_values, each.value.tag_value, each.value.tag_value)
location = local.region
}
@@ -108,8 +108,8 @@ resource "google_tags_location_tag_binding" "disks_regional" {
# resource "google_tags_location_tag_binding" "template" {
# for_each = local.template_create ? var.tag_bindings : {}
# parent = (
-# "${local.tag_parent_base}/regions/${local.region}/instanceTemplates/${google_compute_instance.default[0].instance_id}"
+# "${local.tag_parent_base}/regions/${local.region}/instanceTemplates/${google_compute_instance_template.default[0].instance_id}"
# )
-# tag_value = each.value
+# tag_value = lookup(local.ctx.tag_values, each.value, each.value)
# location = local.region
# }
diff --git a/modules/compute-vm/template.tf b/modules/compute-vm/template.tf
index 4e06f9be5..4001d3e65 100644
--- a/modules/compute-vm/template.tf
+++ b/modules/compute-vm/template.tf
@@ -22,7 +22,7 @@ locals {
resource "google_compute_instance_template" "default" {
provider = google-beta
count = local.template_create && !local.template_regional ? 1 : 0
- project = var.project_id
+ project = local.project_id
region = local.region
name_prefix = "${var.name}-"
description = var.description
@@ -67,7 +67,11 @@ resource "google_compute_instance_template" "default" {
dynamic "disk_encryption_key" {
for_each = var.encryption != null ? [""] : []
content {
- kms_key_self_link = var.encryption.kms_key_self_link
+ kms_key_self_link = lookup(
+ local.ctx.kms_keys,
+ var.encryption.kms_key_self_link,
+ var.encryption.kms_key_self_link
+ )
}
}
}
@@ -117,7 +121,11 @@ resource "google_compute_instance_template" "default" {
dynamic "disk_encryption_key" {
for_each = var.encryption != null ? [""] : []
content {
- kms_key_self_link = var.encryption.kms_key_self_link
+ kms_key_self_link = lookup(
+ local.ctx.kms_keys,
+ var.encryption.kms_key_self_link,
+ var.encryption.kms_key_self_link
+ )
}
}
}
@@ -127,15 +135,27 @@ resource "google_compute_instance_template" "default" {
for_each = var.network_interfaces
iterator = config
content {
- network = config.value.network
- subnetwork = config.value.subnetwork
- network_ip = try(config.value.addresses.internal, null)
+ network = lookup(
+ local.ctx.networks, config.value.network, config.value.network
+ )
+ subnetwork = lookup(
+ local.ctx.subnets, config.value.subnetwork, config.value.subnetwork
+ )
+ network_ip = try(
+ local.ctx.addresses[config.value.addresses.internal],
+ config.value.addresses.internal,
+ null
+ )
nic_type = config.value.nic_type
stack_type = config.value.stack_type
dynamic "access_config" {
for_each = config.value.nat ? [""] : []
content {
- nat_ip = try(config.value.addresses.external, null)
+ nat_ip = try(
+ local.ctx.addresses[config.value.addresses.external],
+ config.value.addresses.external,
+ null
+ )
}
}
dynamic "alias_ip_range" {
@@ -221,7 +241,7 @@ resource "google_compute_instance_template" "default" {
resource "google_compute_region_instance_template" "default" {
provider = google-beta
count = local.template_create && local.template_regional ? 1 : 0
- project = var.project_id
+ project = local.project_id
region = local.region
name_prefix = "${var.name}-"
description = var.description
@@ -266,7 +286,10 @@ resource "google_compute_region_instance_template" "default" {
dynamic "disk_encryption_key" {
for_each = var.encryption != null ? [""] : []
content {
- kms_key_self_link = var.encryption.kms_key_self_link
+ kms_key_self_link = try(
+ local.ctx.kms_keys[var.encryption.kms_key_self_link],
+ var.encryption.kms_key_self_link
+ )
}
}
}
@@ -316,7 +339,10 @@ resource "google_compute_region_instance_template" "default" {
dynamic "disk_encryption_key" {
for_each = var.encryption != null ? [""] : []
content {
- kms_key_self_link = var.encryption.kms_key_self_link
+ kms_key_self_link = try(
+ local.ctx.kms_keys[var.encryption.kms_key_self_link],
+ var.encryption.kms_key_self_link
+ )
}
}
}
@@ -326,15 +352,27 @@ resource "google_compute_region_instance_template" "default" {
for_each = var.network_interfaces
iterator = config
content {
- network = config.value.network
- subnetwork = config.value.subnetwork
- network_ip = try(config.value.addresses.internal, null)
+ network = lookup(
+ local.ctx.networks, config.value.network, config.value.network
+ )
+ subnetwork = lookup(
+ local.ctx.subnets, config.value.subnetwork, config.value.subnetwork
+ )
+ network_ip = try(
+ local.ctx.addresses[config.value.addresses.internal],
+ config.value.addresses.internal,
+ null
+ )
nic_type = config.value.nic_type
stack_type = config.value.stack_type
dynamic "access_config" {
for_each = config.value.nat ? [""] : []
content {
- nat_ip = try(config.value.addresses.external, null)
+ nat_ip = try(
+ local.ctx.addresses[config.value.addresses.external],
+ config.value.addresses.external,
+ null
+ )
}
}
dynamic "alias_ip_range" {
diff --git a/modules/compute-vm/variables.tf b/modules/compute-vm/variables.tf
index 2d502af73..6e6dfa9c9 100644
--- a/modules/compute-vm/variables.tf
+++ b/modules/compute-vm/variables.tf
@@ -122,6 +122,23 @@ variable "confidential_compute" {
default = false
}
+variable "context" {
+ description = "Context-specific interpolations."
+ type = object({
+ addresses = optional(map(string), {})
+ custom_roles = optional(map(string), {})
+ kms_keys = optional(map(string), {})
+ iam_principals = optional(map(string), {})
+ locations = optional(map(string), {})
+ networks = optional(map(string), {})
+ project_ids = optional(map(string), {})
+ subnets = optional(map(string), {})
+ tag_values = optional(map(string), {})
+ })
+ default = {}
+ nullable = false
+}
+
variable "create_template" {
description = "Create instance template instead of instances. Defaults to a global template."
type = object({
diff --git a/modules/dns-response-policy/README.md b/modules/dns-response-policy/README.md
index 010c09c5a..0cce4c060 100644
--- a/modules/dns-response-policy/README.md
+++ b/modules/dns-response-policy/README.md
@@ -145,14 +145,15 @@ restricted:
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L39) | Policy name. | string | ✓ | |
-| [project_id](variables.tf#L58) | Project id for the zone. | string | ✓ | |
+| [name](variables.tf#L49) | Policy name. | string | ✓ | |
+| [project_id](variables.tf#L68) | Project id for the zone. | string | ✓ | |
| [clusters](variables.tf#L17) | Map of GKE clusters to which this policy is applied in name => id format. | map(string) | | {} |
-| [description](variables.tf#L24) | Policy description. | string | | "Terraform managed." |
-| [factories_config](variables.tf#L30) | Path to folder containing rules data files for the optional factory. | object({…}) | | {} |
-| [networks](variables.tf#L44) | Map of VPC self links to which this policy is applied in name => self link format. | map(string) | | {} |
-| [policy_create](variables.tf#L51) | Set to false to use the existing policy matching name and only manage rules. | bool | | true |
-| [rules](variables.tf#L63) | Map of policy rules in name => rule format. Local data takes precedence over behavior and is in the form record type => attributes. | map(object({…})) | | {} |
+| [context](variables.tf#L24) | Context-specific interpolations. | object({…}) | | {} |
+| [description](variables.tf#L34) | Policy description. | string | | "Terraform managed." |
+| [factories_config](variables.tf#L40) | Path to folder containing rules data files for the optional factory. | object({…}) | | {} |
+| [networks](variables.tf#L54) | Map of VPC self links to which this policy is applied in name => self link format. | map(string) | | {} |
+| [policy_create](variables.tf#L61) | Set to false to use the existing policy matching name and only manage rules. | bool | | true |
+| [rules](variables.tf#L73) | Map of policy rules in name => rule format. Local data takes precedence over behavior and is in the form record type => attributes. | map(object({…})) | | {} |
## Outputs
diff --git a/modules/dns-response-policy/main.tf b/modules/dns-response-policy/main.tf
index 0cee3ca14..9e2e06cc9 100644
--- a/modules/dns-response-policy/main.tf
+++ b/modules/dns-response-policy/main.tf
@@ -21,6 +21,12 @@ locals {
: "{}"
)
_factory_rules = yamldecode(local._factory_data)
+ ctx = {
+ for k, v in var.context : k => {
+ for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv
+ }
+ }
+ ctx_p = "$"
factory_rules = {
for k, v in local._factory_rules : k => {
dns_name = v.dns_name
@@ -36,18 +42,19 @@ locals {
? google_dns_response_policy.default[0].response_policy_name
: var.name
)
+ project_id = lookup(local.ctx.project_ids, var.project_id, var.project_id)
}
resource "google_dns_response_policy" "default" {
provider = google-beta
count = var.policy_create ? 1 : 0
- project = var.project_id
+ project = local.project_id
description = var.description
response_policy_name = var.name
dynamic "networks" {
for_each = var.networks
content {
- network_url = networks.value
+ network_url = lookup(local.ctx.networks, networks.value, networks.value)
}
}
dynamic "gke_clusters" {
@@ -61,7 +68,7 @@ resource "google_dns_response_policy" "default" {
resource "google_dns_response_policy_rule" "default" {
provider = google-beta
for_each = merge(local.factory_rules, var.rules)
- project = var.project_id
+ project = local.project_id
response_policy = local.policy_name
rule_name = each.key
dns_name = each.value.dns_name
diff --git a/modules/dns-response-policy/variables.tf b/modules/dns-response-policy/variables.tf
index 1c39260a9..01392e4ff 100644
--- a/modules/dns-response-policy/variables.tf
+++ b/modules/dns-response-policy/variables.tf
@@ -21,6 +21,16 @@ variable "clusters" {
nullable = false
}
+variable "context" {
+ description = "Context-specific interpolations."
+ type = object({
+ networks = optional(map(string), {})
+ project_ids = optional(map(string), {})
+ })
+ default = {}
+ nullable = false
+}
+
variable "description" {
description = "Policy description."
type = string
diff --git a/modules/dns/README.md b/modules/dns/README.md
index fe2fdb90c..c1a68f4b2 100644
--- a/modules/dns/README.md
+++ b/modules/dns/README.md
@@ -190,13 +190,14 @@ module "public-dns" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L35) | Zone name, must be unique within the project. | string | ✓ | |
-| [project_id](variables.tf#L40) | Project id for the zone. | string | ✓ | |
-| [description](variables.tf#L17) | Domain description. | string | | "Terraform managed." |
-| [force_destroy](variables.tf#L23) | Set this to true to delete all records in the zone upon zone destruction. | bool | | null |
-| [iam](variables.tf#L29) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | null |
-| [recordsets](variables.tf#L45) | Map of DNS recordsets in \"type name\" => {ttl, [records]} format. | map(object({…})) | | {} |
-| [zone_config](variables.tf#L102) | DNS zone configuration. | object({…}) | | null |
+| [name](variables.tf#L47) | Zone name, must be unique within the project. | string | ✓ | |
+| [project_id](variables.tf#L52) | Project id for the zone. | string | ✓ | |
+| [context](variables.tf#L17) | Context-specific interpolations. | object({…}) | | {} |
+| [description](variables.tf#L29) | Domain description. | string | | "Terraform managed." |
+| [force_destroy](variables.tf#L35) | Set this to true to delete all records in the zone upon zone destruction. | bool | | null |
+| [iam](variables.tf#L41) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | null |
+| [recordsets](variables.tf#L57) | Map of DNS recordsets in \"type name\" => {ttl, [records]} format. | map(object({…})) | | {} |
+| [zone_config](variables.tf#L114) | DNS zone configuration. | object({…}) | | null |
## Outputs
diff --git a/modules/dns/main.tf b/modules/dns/main.tf
index a13ea40f1..ae8c21678 100644
--- a/modules/dns/main.tf
+++ b/modules/dns/main.tf
@@ -15,15 +15,27 @@
*/
locals {
- managed_zone = (var.zone_config == null ?
- data.google_dns_managed_zone.dns_managed_zone[0]
- : google_dns_managed_zone.dns_managed_zone[0]
- )
# split record name and type and set as keys in a map
_recordsets_0 = {
for key, attrs in var.recordsets :
key => merge(attrs, zipmap(["type", "name"], split(" ", key)))
}
+ client_networks = concat(
+ coalesce(try(var.zone_config.forwarding.client_networks, null), []),
+ coalesce(try(var.zone_config.peering.client_networks, null), []),
+ coalesce(try(var.zone_config.private.client_networks, null), [])
+ )
+ ctx = {
+ for k, v in var.context : k => {
+ for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv
+ }
+ }
+ ctx_p = "$"
+ managed_zone = (var.zone_config == null ?
+ data.google_dns_managed_zone.dns_managed_zone[0]
+ : google_dns_managed_zone.dns_managed_zone[0]
+ )
+ project_id = lookup(local.ctx.project_ids, var.project_id, var.project_id)
# compute the final resource name for the recordset
recordsets = {
for key, attrs in local._recordsets_0 :
@@ -39,11 +51,6 @@ locals {
)
})
}
- client_networks = concat(
- coalesce(try(var.zone_config.forwarding.client_networks, null), []),
- coalesce(try(var.zone_config.peering.client_networks, null), []),
- coalesce(try(var.zone_config.private.client_networks, null), [])
- )
visibility = (var.zone_config == null ?
null
: (var.zone_config.forwarding != null ||
@@ -57,7 +64,7 @@ locals {
resource "google_dns_managed_zone" "dns_managed_zone" {
count = (var.zone_config == null) ? 0 : 1
provider = google-beta
- project = var.project_id
+ project = local.project_id
name = var.name
dns_name = var.zone_config.domain
description = var.description
@@ -122,7 +129,9 @@ resource "google_dns_managed_zone" "dns_managed_zone" {
for_each = local.client_networks
iterator = network
content {
- network_url = network.value
+ network_url = lookup(
+ local.ctx.networks, network.value, network.value
+ )
}
}
}
@@ -146,22 +155,24 @@ resource "google_dns_managed_zone" "dns_managed_zone" {
data "google_dns_managed_zone" "dns_managed_zone" {
count = var.zone_config == null ? 1 : 0
- project = var.project_id
+ project = local.project_id
name = var.name
}
resource "google_dns_managed_zone_iam_binding" "iam_bindings" {
for_each = coalesce(var.iam, {})
- project = var.project_id
+ project = local.project_id
managed_zone = local.managed_zone.id
- role = each.key
- members = each.value
+ role = lookup(local.ctx.custom_roles, each.key, each.key)
+ members = [
+ for m in each.value : lookup(local.ctx.iam_principals, m, m)
+ ]
}
data "google_dns_keys" "dns_keys" {
count = try(var.zone_config.public.dnssec_config.state, "off") != "off" ? 1 : 0
managed_zone = local.managed_zone.id
- project = var.project_id
+ project = local.project_id
depends_on = [
google_dns_managed_zone.dns_managed_zone
]
@@ -169,7 +180,7 @@ data "google_dns_keys" "dns_keys" {
resource "google_dns_record_set" "dns_record_set" {
for_each = local.recordsets
- project = var.project_id
+ project = local.project_id
managed_zone = var.name
name = each.value.resource_name
type = each.value.type
diff --git a/modules/dns/variables.tf b/modules/dns/variables.tf
index 4dc10e2c5..b80188608 100644
--- a/modules/dns/variables.tf
+++ b/modules/dns/variables.tf
@@ -14,6 +14,18 @@
* limitations under the License.
*/
+variable "context" {
+ description = "Context-specific interpolations."
+ type = object({
+ custom_roles = optional(map(string), {})
+ iam_principals = optional(map(string), {})
+ networks = optional(map(string), {})
+ project_ids = optional(map(string), {})
+ })
+ default = {}
+ nullable = false
+}
+
variable "description" {
description = "Domain description."
type = string
diff --git a/modules/gcs/README.md b/modules/gcs/README.md
index 8c7d2104c..d787566bf 100644
--- a/modules/gcs/README.md
+++ b/modules/gcs/README.md
@@ -149,12 +149,11 @@ module "bucket-gcs-notification" {
name = "my-bucket"
location = "EU"
notification_config = {
- enabled = true
- payload_format = "JSON_API_V1"
- sa_email = module.project.service_agents.storage.email
- topic_name = "gcs-notification-topic"
- event_types = ["OBJECT_FINALIZE"]
- custom_attributes = {}
+ enabled = true
+ payload_format = "JSON_API_V1"
+ sa_email = module.project.service_agents.storage.email
+ topic_name = "gcs-notification-topic"
+ event_types = ["OBJECT_FINALIZE"]
}
}
# tftest e2e
diff --git a/modules/net-cloudnat/README.md b/modules/net-cloudnat/README.md
index b8b8b614e..dd1e88922 100644
--- a/modules/net-cloudnat/README.md
+++ b/modules/net-cloudnat/README.md
@@ -189,21 +189,22 @@ module "nat" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L107) | Name of the Cloud NAT resource. | string | ✓ | |
-| [project_id](variables.tf#L112) | Project where resources will be created. | string | ✓ | |
-| [region](variables.tf#L117) | Region where resources will be created. | string | ✓ | |
+| [name](variables.tf#L121) | Name of the Cloud NAT resource. | string | ✓ | |
+| [project_id](variables.tf#L126) | Project where resources will be created. | string | ✓ | |
+| [region](variables.tf#L131) | Region where resources will be created. | string | ✓ | |
| [addresses](variables.tf#L17) | Optional list of external address self links. | list(string) | | [] |
-| [config_port_allocation](variables.tf#L23) | Configuration for how to assign ports to virtual machines. min_ports_per_vm and max_ports_per_vm have no effect unless enable_dynamic_port_allocation is set to 'true'. | object({…}) | | {} |
-| [config_source_subnetworks](variables.tf#L39) | Subnetwork configuration. | object({…}) | | {} |
-| [config_timeouts](variables.tf#L69) | Timeout configurations. | object({…}) | | {} |
-| [endpoint_types](variables.tf#L82) | Specifies the endpoint Types supported by the NAT Gateway. Supported values include: ENDPOINT_TYPE_VM, ENDPOINT_TYPE_SWG, ENDPOINT_TYPE_MANAGED_PROXY_LB. | list(string) | | null |
-| [logging_filter](variables.tf#L101) | Enables logging if not null, value is one of 'ERRORS_ONLY', 'TRANSLATIONS_ONLY', 'ALL'. | string | | null |
-| [router_asn](variables.tf#L122) | Router ASN used for auto-created router. | number | | null |
-| [router_create](variables.tf#L128) | Create router. | bool | | true |
-| [router_name](variables.tf#L134) | Router name, leave blank if router will be created to use auto generated name. | string | | null |
-| [router_network](variables.tf#L140) | Name of the VPC used for auto-created router. | string | | null |
-| [rules](variables.tf#L146) | List of rules associated with this NAT. | list(object({…})) | | [] |
-| [type](variables.tf#L166) | Whether this Cloud NAT is used for public or private IP translation. One of 'PUBLIC' or 'PRIVATE'. | string | | "PUBLIC" |
+| [config_port_allocation](variables.tf#L24) | Configuration for how to assign ports to virtual machines. min_ports_per_vm and max_ports_per_vm have no effect unless enable_dynamic_port_allocation is set to 'true'. | object({…}) | | {} |
+| [config_source_subnetworks](variables.tf#L40) | Subnetwork configuration. | object({…}) | | {} |
+| [config_timeouts](variables.tf#L70) | Timeout configurations. | object({…}) | | {} |
+| [context](variables.tf#L83) | Context-specific interpolations. | object({…}) | | {} |
+| [endpoint_types](variables.tf#L96) | Specifies the endpoint Types supported by the NAT Gateway. Supported values include: ENDPOINT_TYPE_VM, ENDPOINT_TYPE_SWG, ENDPOINT_TYPE_MANAGED_PROXY_LB. | list(string) | | null |
+| [logging_filter](variables.tf#L115) | Enables logging if not null, value is one of 'ERRORS_ONLY', 'TRANSLATIONS_ONLY', 'ALL'. | string | | null |
+| [router_asn](variables.tf#L136) | Router ASN used for auto-created router. | number | | null |
+| [router_create](variables.tf#L142) | Create router. | bool | | true |
+| [router_name](variables.tf#L148) | Router name, leave blank if router will be created to use auto generated name. | string | | null |
+| [router_network](variables.tf#L154) | Name of the VPC used for auto-created router. | string | | null |
+| [rules](variables.tf#L160) | List of rules associated with this NAT. | list(object({…})) | | [] |
+| [type](variables.tf#L180) | Whether this Cloud NAT is used for public or private IP translation. One of 'PUBLIC' or 'PRIVATE'. | string | | "PUBLIC" |
## Outputs
diff --git a/modules/net-cloudnat/main.tf b/modules/net-cloudnat/main.tf
index 2c6d8eb67..60a4f72d0 100644
--- a/modules/net-cloudnat/main.tf
+++ b/modules/net-cloudnat/main.tf
@@ -15,11 +15,19 @@
*/
locals {
+ ctx = {
+ for k, v in var.context : k => {
+ for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv
+ }
+ }
+ ctx_p = "$"
router_name = (
var.router_create
? try(google_compute_router.router[0].name, null)
: var.router_name
)
+ project_id = lookup(local.ctx.project_ids, var.project_id, var.project_id)
+ region = lookup(local.ctx.locations, var.region, var.region)
subnet_config = (
var.config_source_subnetworks.all != true
? "LIST_OF_SUBNETWORKS"
@@ -34,10 +42,9 @@ locals {
resource "google_compute_router" "router" {
count = var.router_create ? 1 : 0
name = var.router_name == null ? "${var.name}-nat" : var.router_name
- project = var.project_id
- region = var.region
- network = var.router_network
-
+ project = local.project_id
+ region = local.region
+ network = lookup(local.ctx.networks, var.router_network, var.router_network)
dynamic "bgp" {
for_each = var.router_asn == null ? [] : [1]
content {
@@ -48,13 +55,15 @@ resource "google_compute_router" "router" {
resource "google_compute_router_nat" "nat" {
provider = google-beta
- project = var.project_id
- region = var.region
+ project = local.project_id
+ region = local.region
name = var.name
endpoint_types = var.endpoint_types
type = var.type
router = local.router_name
- nat_ips = var.addresses
+ nat_ips = [
+ for a in var.addresses : lookup(local.ctx.addresses, a, a)
+ ]
nat_ip_allocate_option = (
var.type == "PRIVATE"
? null
@@ -64,30 +73,28 @@ resource "google_compute_router_nat" "nat" {
: "AUTO_ONLY"
)
)
- source_subnetwork_ip_ranges_to_nat = local.subnet_config
icmp_idle_timeout_sec = var.config_timeouts.icmp
- udp_idle_timeout_sec = var.config_timeouts.udp
+ source_subnetwork_ip_ranges_to_nat = local.subnet_config
tcp_established_idle_timeout_sec = var.config_timeouts.tcp_established
tcp_time_wait_timeout_sec = var.config_timeouts.tcp_time_wait
tcp_transitory_idle_timeout_sec = var.config_timeouts.tcp_transitory
+ udp_idle_timeout_sec = var.config_timeouts.udp
enable_endpoint_independent_mapping = (
var.config_port_allocation.enable_endpoint_independent_mapping
)
enable_dynamic_port_allocation = (
var.config_port_allocation.enable_dynamic_port_allocation
)
+ log_config {
+ enable = var.logging_filter == null ? false : true
+ filter = var.logging_filter == null ? "ALL" : var.logging_filter
+ }
min_ports_per_vm = (
var.config_port_allocation.min_ports_per_vm
)
max_ports_per_vm = (
var.config_port_allocation.max_ports_per_vm
)
-
- log_config {
- enable = var.logging_filter == null ? false : true
- filter = var.logging_filter == null ? "ALL" : var.logging_filter
- }
-
dynamic "subnetwork" {
for_each = toset(
local.subnet_config == "LIST_OF_SUBNETWORKS"
@@ -95,7 +102,9 @@ resource "google_compute_router_nat" "nat" {
: []
)
content {
- name = subnetwork.value.self_link
+ name = lookup(
+ local.ctx.subnets, subnetwork.value.self_link, subnetwork.value.self_link
+ )
source_ip_ranges_to_nat = (
subnetwork.value.all_ranges == true
? ["ALL_IP_RANGES"]
@@ -120,7 +129,6 @@ resource "google_compute_router_nat" "nat" {
)
}
}
-
dynamic "rules" {
for_each = { for i, r in var.rules : i => r }
content {
diff --git a/modules/net-cloudnat/variables.tf b/modules/net-cloudnat/variables.tf
index 88fe1cb83..369a3d94f 100644
--- a/modules/net-cloudnat/variables.tf
+++ b/modules/net-cloudnat/variables.tf
@@ -17,6 +17,7 @@
variable "addresses" {
description = "Optional list of external address self links."
type = list(string)
+ nullable = false
default = []
}
@@ -79,6 +80,19 @@ variable "config_timeouts" {
nullable = false
}
+variable "context" {
+ description = "Context-specific interpolations."
+ type = object({
+ addresses = optional(map(string), {})
+ locations = optional(map(string), {})
+ networks = optional(map(string), {})
+ project_ids = optional(map(string), {})
+ subnets = optional(map(string), {})
+ })
+ default = {}
+ nullable = false
+}
+
variable "endpoint_types" {
description = "Specifies the endpoint Types supported by the NAT Gateway. Supported values include: ENDPOINT_TYPE_VM, ENDPOINT_TYPE_SWG, ENDPOINT_TYPE_MANAGED_PROXY_LB."
type = list(string)
diff --git a/modules/net-firewall-policy/README.md b/modules/net-firewall-policy/README.md
index ca3aecf2f..fe899cc03 100644
--- a/modules/net-firewall-policy/README.md
+++ b/modules/net-firewall-policy/README.md
@@ -17,8 +17,8 @@ The module also makes fewer assumptions about implicit defaults, only using one
- [Factory](#factory)
- [Firewall Rule Factory Schema](#firewall-rule-factory-schema)
- [Dynamic Rule Matching](#dynamic-rule-matching)
- - [Ingress Rules (](#ingress-rules-)
- - [Egress Rules (](#egress-rules-)
+ - [Ingress Rules](#ingress-rules)
+ - [Egress Rules](#egress-rules)
- [Rule-Level Mappings](#rule-level-mappings)
- [Variables](#variables)
- [Outputs](#outputs)
@@ -351,7 +351,9 @@ This module simplifies firewall rule creation by using generic, context-aware va
The tables below provide a complete reference for these dynamic mappings.
-#### Ingress Rules (`direction = "INGRESS"`)
+#### Ingress Rules
+
+`direction = "INGRESS"`
| Module Variable (`match.*`) | Mapped Resource Attribute |
| :--- | :--- |
@@ -361,7 +363,9 @@ The tables below provide a complete reference for these dynamic mappings.
| `source_tags` | `src_secure_tags` |
| `threat_intelligences` | `src_threat_intelligences` |
-#### Egress Rules (`direction = "EGRESS"`)
+#### Egress Rules
+
+`direction = "EGRESS"`
| Module Variable (`match.*`) | Mapped Resource Attribute |
| :--- | :--- |
@@ -377,22 +381,21 @@ The following variable is defined at the top level of the rule (not within the `
| Module Variable | Mapped Resource Attribute |
| :--- | :--- |
| `target_tags` | `target_secure_tags` |
-
-
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L117) | Policy name. | string | ✓ | |
-| [parent_id](variables.tf#L123) | Parent node where the policy will be created, `folders/nnn` or `organizations/nnn` for hierarchical policy, project id for a network policy. | string | ✓ | |
+| [name](variables.tf#L132) | Policy name. | string | ✓ | |
+| [parent_id](variables.tf#L138) | Parent node where the policy will be created, `folders/nnn` or `organizations/nnn` for hierarchical policy, project id for a network policy. | string | ✓ | |
| [attachments](variables.tf#L17) | Ids of the resources to which this policy will be attached, in descriptive name => self link format. Specify folders or organization for hierarchical policy, VPCs for network policy. | map(string) | | {} |
-| [description](variables.tf#L24) | Policy description. | string | | null |
-| [egress_rules](variables.tf#L30) | List of egress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'. The match.layer4configs map is in protocol => optional [ports] format. | map(object({…})) | | {} |
-| [factories_config](variables.tf#L68) | Paths to folders for the optional factories. | object({…}) | | {} |
-| [ingress_rules](variables.tf#L79) | List of ingress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'. | map(object({…})) | | {} |
-| [region](variables.tf#L129) | Policy region. Leave null for hierarchical policy, set to 'global' for a global network policy. | string | | null |
-| [security_profile_group_ids](variables.tf#L135) | The optional security groups ids to be referenced in factories. | map(string) | | {} |
+| [context](variables.tf#L24) | Context-specific interpolations. | object({…}) | | {} |
+| [description](variables.tf#L39) | Policy description. | string | | null |
+| [egress_rules](variables.tf#L45) | List of egress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'. The match.layer4configs map is in protocol => optional [ports] format. | map(object({…})) | | {} |
+| [factories_config](variables.tf#L83) | Paths to folders for the optional factories. | object({…}) | | {} |
+| [ingress_rules](variables.tf#L94) | List of ingress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'. | map(object({…})) | | {} |
+| [region](variables.tf#L144) | Policy region. Leave null for hierarchical policy, set to 'global' for a global network policy. | string | | null |
+| [security_profile_group_ids](variables.tf#L150) | The optional security groups ids to be referenced in factories. | map(string) | | {} |
## Outputs
diff --git a/modules/net-firewall-policy/hierarchical.tf b/modules/net-firewall-policy/hierarchical.tf
index 7969a559d..dffbb7805 100644
--- a/modules/net-firewall-policy/hierarchical.tf
+++ b/modules/net-firewall-policy/hierarchical.tf
@@ -16,7 +16,7 @@
resource "google_compute_firewall_policy" "hierarchical" {
count = local.use_hierarchical ? 1 : 0
- parent = var.parent_id
+ parent = lookup(local.ctx.folder_ids, var.parent_id, var.parent_id)
short_name = var.name
description = var.description
}
@@ -24,7 +24,7 @@ resource "google_compute_firewall_policy" "hierarchical" {
resource "google_compute_firewall_policy_association" "hierarchical" {
for_each = local.use_hierarchical ? var.attachments : {}
name = "${var.name}-${each.key}"
- attachment_target = each.value
+ attachment_target = lookup(local.ctx.folder_ids, each.value, each.value)
firewall_policy = google_compute_firewall_policy.hierarchical[0].name
}
@@ -33,23 +33,43 @@ resource "google_compute_firewall_policy_rule" "hierarchical" {
for_each = toset(
local.use_hierarchical ? keys(local.rules) : []
)
- firewall_policy = google_compute_firewall_policy.hierarchical[0].name
- action = local.rules[each.key].action
- description = local.rules[each.key].description
- direction = local.rules[each.key].direction
- disabled = local.rules[each.key].disabled
- enable_logging = local.rules[each.key].enable_logging
- priority = local.rules[each.key].priority
- target_resources = local.rules[each.key].target_resources
- target_service_accounts = local.rules[each.key].target_service_accounts
- tls_inspect = local.rules[each.key].tls_inspect
+ firewall_policy = google_compute_firewall_policy.hierarchical[0].name
+ action = local.rules[each.key].action
+ description = local.rules[each.key].description
+ direction = local.rules[each.key].direction
+ disabled = local.rules[each.key].disabled
+ enable_logging = local.rules[each.key].enable_logging
+ priority = local.rules[each.key].priority
+ target_resources = (
+ local.rules[each.key].target_resources == null ? null : [
+ for n in local.rules[each.key].target_resources :
+ lookup(local.ctx.networks, n, n)
+ ]
+ )
+ target_service_accounts = (
+ local.rules[each.key].target_service_accounts == null ? null : [
+ for n in local.rules[each.key].target_service_accounts :
+ lookup(local.ctx.iam_principals, n, n)
+ ]
+ )
+ tls_inspect = local.rules[each.key].tls_inspect
security_profile_group = try(
var.security_profile_group_ids[local.rules[each.key].security_profile_group],
local.rules[each.key].security_profile_group
)
match {
- dest_ip_ranges = local.rules[each.key].match.destination_ranges
- src_ip_ranges = local.rules[each.key].match.source_ranges
+ dest_ip_ranges = (
+ local.rules[each.key].match.destination_ranges == null ? null : [
+ for r in local.rules[each.key].match.destination_ranges :
+ lookup(local.ctx.cidr_ranges, r, r)
+ ]
+ )
+ src_ip_ranges = (
+ local.rules[each.key].match.source_ranges == null ? null : [
+ for r in local.rules[each.key].match.source_ranges :
+ lookup(local.ctx.cidr_ranges, r, r)
+ ]
+ )
dest_address_groups = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.address_groups
diff --git a/modules/net-firewall-policy/main.tf b/modules/net-firewall-policy/main.tf
index 093c8bf23..b59a92126 100644
--- a/modules/net-firewall-policy/main.tf
+++ b/modules/net-firewall-policy/main.tf
@@ -23,6 +23,12 @@ locals {
for name, rule in merge(var.ingress_rules) :
"ingress/${name}" => merge(rule, { name = name, direction = "INGRESS" })
}
+ ctx = {
+ for k, v in var.context : k => {
+ for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv
+ }
+ }
+ ctx_p = "$"
rules = merge(
local.factory_egress_rules, local.factory_ingress_rules,
local._rules_egress, local._rules_ingress
diff --git a/modules/net-firewall-policy/net-global.tf b/modules/net-firewall-policy/net-global.tf
index cf837c861..8d9bc4049 100644
--- a/modules/net-firewall-policy/net-global.tf
+++ b/modules/net-firewall-policy/net-global.tf
@@ -16,7 +16,7 @@
resource "google_compute_network_firewall_policy" "net-global" {
count = !local.use_hierarchical && !local.use_regional ? 1 : 0
- project = var.parent_id
+ project = lookup(local.ctx.project_ids, var.parent_id, var.parent_id)
name = var.name
description = var.description
}
@@ -25,9 +25,9 @@ resource "google_compute_network_firewall_policy_association" "net-global" {
for_each = (
!local.use_hierarchical && !local.use_regional ? var.attachments : {}
)
- project = var.parent_id
+ project = lookup(local.ctx.project_ids, var.parent_id, var.parent_id)
name = "${var.name}-${each.key}"
- attachment_target = each.value
+ attachment_target = lookup(local.ctx.networks, each.value, each.value)
firewall_policy = google_compute_network_firewall_policy.net-global[0].name
}
@@ -38,24 +38,39 @@ resource "google_compute_network_firewall_policy_rule" "net-global" {
? keys(local.rules)
: []
)
- project = var.parent_id
- firewall_policy = google_compute_network_firewall_policy.net-global[0].name
- rule_name = local.rules[each.key].name
- action = local.rules[each.key].action
- description = local.rules[each.key].description
- direction = local.rules[each.key].direction
- disabled = local.rules[each.key].disabled
- enable_logging = local.rules[each.key].enable_logging
- priority = local.rules[each.key].priority
- target_service_accounts = local.rules[each.key].target_service_accounts
- tls_inspect = local.rules[each.key].tls_inspect
+ project = lookup(local.ctx.project_ids, var.parent_id, var.parent_id)
+ firewall_policy = google_compute_network_firewall_policy.net-global[0].name
+ rule_name = local.rules[each.key].name
+ action = local.rules[each.key].action
+ description = local.rules[each.key].description
+ direction = local.rules[each.key].direction
+ disabled = local.rules[each.key].disabled
+ enable_logging = local.rules[each.key].enable_logging
+ priority = local.rules[each.key].priority
+ target_service_accounts = (
+ local.rules[each.key].target_service_accounts == null ? null : [
+ for n in local.rules[each.key].target_service_accounts :
+ lookup(local.ctx.iam_principals, n, n)
+ ]
+ )
+ tls_inspect = local.rules[each.key].tls_inspect
security_profile_group = try(
var.security_profile_group_ids[local.rules[each.key].security_profile_group],
local.rules[each.key].security_profile_group
)
match {
- dest_ip_ranges = local.rules[each.key].match.destination_ranges
- src_ip_ranges = local.rules[each.key].match.source_ranges
+ dest_ip_ranges = (
+ local.rules[each.key].match.destination_ranges == null ? null : [
+ for r in local.rules[each.key].match.destination_ranges :
+ lookup(local.ctx.cidr_ranges, r, r)
+ ]
+ )
+ src_ip_ranges = (
+ local.rules[each.key].match.source_ranges == null ? null : [
+ for r in local.rules[each.key].match.source_ranges :
+ lookup(local.ctx.cidr_ranges, r, r)
+ ]
+ )
dest_address_groups = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.address_groups
@@ -106,7 +121,9 @@ resource "google_compute_network_firewall_policy_rule" "net-global" {
dynamic "src_secure_tags" {
for_each = toset(coalesce(local.rules[each.key].match.source_tags, []))
content {
- name = src_secure_tags.key
+ name = lookup(
+ local.ctx.tag_values, src_secure_tags.key, src_secure_tags.key
+ )
}
}
}
@@ -117,7 +134,9 @@ resource "google_compute_network_firewall_policy_rule" "net-global" {
: local.rules[each.key].target_tags
)
content {
- name = target_secure_tags.value
+ name = lookup(
+ local.ctx.tag_values, target_secure_tags.value, target_secure_tags.value
+ )
}
}
}
diff --git a/modules/net-firewall-policy/net-regional.tf b/modules/net-firewall-policy/net-regional.tf
index 281cbc4f9..9a83a5ef6 100644
--- a/modules/net-firewall-policy/net-regional.tf
+++ b/modules/net-firewall-policy/net-regional.tf
@@ -16,20 +16,20 @@
resource "google_compute_region_network_firewall_policy" "net-regional" {
count = !local.use_hierarchical && local.use_regional ? 1 : 0
- project = var.parent_id
+ project = lookup(local.ctx.project_ids, var.parent_id, var.parent_id)
name = var.name
description = var.description
- region = var.region
+ region = lookup(local.ctx.locations, var.region, var.region)
}
resource "google_compute_region_network_firewall_policy_association" "net-regional" {
for_each = (
!local.use_hierarchical && local.use_regional ? var.attachments : {}
)
- project = var.parent_id
- region = var.region
+ project = lookup(local.ctx.project_ids, var.parent_id, var.parent_id)
+ region = lookup(local.ctx.locations, var.region, var.region)
name = "${var.name}-${each.key}"
- attachment_target = each.value
+ attachment_target = lookup(local.ctx.networks, each.value, each.value)
firewall_policy = google_compute_region_network_firewall_policy.net-regional[0].name
}
@@ -40,20 +40,35 @@ resource "google_compute_region_network_firewall_policy_rule" "net-regional" {
? keys(local.rules)
: []
)
- project = var.parent_id
- region = var.region
- firewall_policy = google_compute_region_network_firewall_policy.net-regional[0].name
- rule_name = local.rules[each.key].name
- action = local.rules[each.key].action
- description = local.rules[each.key].description
- direction = local.rules[each.key].direction
- disabled = local.rules[each.key].disabled
- enable_logging = local.rules[each.key].enable_logging
- priority = local.rules[each.key].priority
- target_service_accounts = local.rules[each.key].target_service_accounts
+ project = lookup(local.ctx.project_ids, var.parent_id, var.parent_id)
+ region = lookup(local.ctx.locations, var.region, var.region)
+ firewall_policy = google_compute_region_network_firewall_policy.net-regional[0].name
+ rule_name = local.rules[each.key].name
+ action = local.rules[each.key].action
+ description = local.rules[each.key].description
+ direction = local.rules[each.key].direction
+ disabled = local.rules[each.key].disabled
+ enable_logging = local.rules[each.key].enable_logging
+ priority = local.rules[each.key].priority
+ target_service_accounts = (
+ local.rules[each.key].target_service_accounts == null ? null : [
+ for n in local.rules[each.key].target_service_accounts :
+ lookup(local.ctx.iam_principals, n, n)
+ ]
+ )
match {
- dest_ip_ranges = local.rules[each.key].match.destination_ranges
- src_ip_ranges = local.rules[each.key].match.source_ranges
+ dest_ip_ranges = (
+ local.rules[each.key].match.destination_ranges == null ? null : [
+ for r in local.rules[each.key].match.destination_ranges :
+ lookup(local.ctx.cidr_ranges, r, r)
+ ]
+ )
+ src_ip_ranges = (
+ local.rules[each.key].match.source_ranges == null ? null : [
+ for r in local.rules[each.key].match.source_ranges :
+ lookup(local.ctx.cidr_ranges, r, r)
+ ]
+ )
dest_address_groups = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.address_groups
@@ -104,7 +119,9 @@ resource "google_compute_region_network_firewall_policy_rule" "net-regional" {
dynamic "src_secure_tags" {
for_each = toset(coalesce(local.rules[each.key].match.source_tags, []))
content {
- name = src_secure_tags.key
+ name = lookup(
+ local.ctx.tag_values, src_secure_tags.key, src_secure_tags.key
+ )
}
}
}
@@ -115,7 +132,9 @@ resource "google_compute_region_network_firewall_policy_rule" "net-regional" {
: local.rules[each.key].target_tags
)
content {
- name = target_secure_tags.value
+ name = lookup(
+ local.ctx.tag_values, target_secure_tags.value, target_secure_tags.value
+ )
}
}
}
diff --git a/modules/net-firewall-policy/variables.tf b/modules/net-firewall-policy/variables.tf
index bab3ee853..9ccc91783 100644
--- a/modules/net-firewall-policy/variables.tf
+++ b/modules/net-firewall-policy/variables.tf
@@ -21,6 +21,21 @@ variable "attachments" {
nullable = false
}
+variable "context" {
+ description = "Context-specific interpolations."
+ type = object({
+ cidr_ranges = optional(map(string), {})
+ folder_ids = optional(map(string), {})
+ iam_principals = optional(map(string), {})
+ locations = optional(map(string), {})
+ networks = optional(map(string), {})
+ project_ids = optional(map(string), {})
+ tag_values = optional(map(string), {})
+ })
+ default = {}
+ nullable = false
+}
+
variable "description" {
description = "Policy description."
type = string
diff --git a/modules/net-lb-int/README.md b/modules/net-lb-int/README.md
index 5ed7b40a9..99ae056fe 100644
--- a/modules/net-lb-int/README.md
+++ b/modules/net-lb-int/README.md
@@ -374,20 +374,21 @@ One other issue is a `Provider produced inconsistent final plan` error which is
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L188) | Name used for all resources. | string | ✓ | |
-| [project_id](variables.tf#L193) | Project id where resources will be created. | string | ✓ | |
-| [region](variables.tf#L198) | GCP region. | string | ✓ | |
-| [vpc_config](variables.tf#L224) | VPC-level configuration. | object({…}) | ✓ | |
+| [name](variables.tf#L201) | Name used for all resources. | string | ✓ | |
+| [project_id](variables.tf#L206) | Project id where resources will be created. | string | ✓ | |
+| [region](variables.tf#L211) | GCP region. | string | ✓ | |
+| [vpc_config](variables.tf#L237) | VPC-level configuration. | object({…}) | ✓ | |
| [backend_service_config](variables.tf#L17) | Backend service level configuration. | object({…}) | | {} |
| [backends](variables.tf#L53) | Load balancer backends. | list(object({…})) | | [] |
-| [description](variables.tf#L64) | Optional description used for resources. | string | | "Terraform managed." |
-| [forwarding_rules_config](variables.tf#L70) | The optional forwarding rules configuration. | map(object({…})) | | {…} |
-| [group_configs](variables.tf#L86) | Optional unmanaged groups to create. Can be referenced in backends via outputs. | map(object({…})) | | {} |
-| [health_check](variables.tf#L98) | Name of existing health check to use, disables auto-created health check. Also set `health_check_config = null` when cross-referencing an health check from another load balancer module to avoid a Terraform error. | string | | null |
-| [health_check_config](variables.tf#L104) | Optional auto-created health check configuration, use the output self-link to set it in the auto healing policy. Refer to examples for usage. | object({…}) | | {…} |
-| [labels](variables.tf#L182) | Labels set on resources. | map(string) | | {} |
-| [service_attachments](variables.tf#L203) | PSC service attachments, keyed by forwarding rule. | map(object({…})) | | null |
-| [service_label](variables.tf#L218) | Optional prefix of the fully qualified forwarding rule name. | string | | null |
+| [context](variables.tf#L64) | Context-specific interpolations. | object({…}) | | {} |
+| [description](variables.tf#L77) | Optional description used for resources. | string | | "Terraform managed." |
+| [forwarding_rules_config](variables.tf#L83) | The optional forwarding rules configuration. | map(object({…})) | | {…} |
+| [group_configs](variables.tf#L99) | Optional unmanaged groups to create. Can be referenced in backends via outputs. | map(object({…})) | | {} |
+| [health_check](variables.tf#L111) | Name of existing health check to use, disables auto-created health check. Also set `health_check_config = null` when cross-referencing an health check from another load balancer module to avoid a Terraform error. | string | | null |
+| [health_check_config](variables.tf#L117) | Optional auto-created health check configuration, use the output self-link to set it in the auto healing policy. Refer to examples for usage. | object({…}) | | {…} |
+| [labels](variables.tf#L195) | Labels set on resources. | map(string) | | {} |
+| [service_attachments](variables.tf#L216) | PSC service attachments, keyed by forwarding rule. | map(object({…})) | | null |
+| [service_label](variables.tf#L231) | Optional prefix of the fully qualified forwarding rule name. | string | | null |
## Outputs
diff --git a/modules/net-lb-int/groups.tf b/modules/net-lb-int/groups.tf
index 736dfc6f5..3492f5b3d 100644
--- a/modules/net-lb-int/groups.tf
+++ b/modules/net-lb-int/groups.tf
@@ -18,8 +18,8 @@
resource "google_compute_instance_group" "default" {
for_each = var.group_configs
- project = var.project_id
- zone = each.value.zone
+ project = local.project_id
+ zone = lookup(local.ctx.locations, each.value.zone, each.value.zone)
name = "${var.name}-${each.key}"
description = each.value.description
instances = each.value.instances
diff --git a/modules/net-lb-int/health-check.tf b/modules/net-lb-int/health-check.tf
index 1e0bd193b..18bc4ccac 100644
--- a/modules/net-lb-int/health-check.tf
+++ b/modules/net-lb-int/health-check.tf
@@ -31,7 +31,7 @@ locals {
resource "google_compute_health_check" "default" {
provider = google-beta
count = local.hc != null ? 1 : 0
- project = var.project_id
+ project = local.project_id
name = coalesce(local.hc.name, var.name)
description = local.hc.description
check_interval_sec = local.hc.check_interval_sec
diff --git a/modules/net-lb-int/main.tf b/modules/net-lb-int/main.tf
index 201d1e3eb..c517c662b 100644
--- a/modules/net-lb-int/main.tf
+++ b/modules/net-lb-int/main.tf
@@ -16,19 +16,34 @@
locals {
+ _service_attachments = (
+ var.service_attachments == null ? {} : var.service_attachments
+ )
bs_conntrack = var.backend_service_config.connection_tracking
bs_failover = var.backend_service_config.failover_config
forwarding_rule_names = {
for k, v in var.forwarding_rules_config :
k => k == "" ? var.name : "${var.name}-${k}"
}
+ ctx = {
+ for k, v in var.context : k => {
+ for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv
+ }
+ }
+ ctx_p = "$"
health_check = (
var.health_check != null
? var.health_check
: google_compute_health_check.default[0].self_link
)
- _service_attachments = (
- var.service_attachments == null ? {} : var.service_attachments
+ network = lookup(
+ local.ctx.networks, var.vpc_config.network, var.vpc_config.network
+ )
+ project_id = lookup(
+ local.ctx.project_ids, var.project_id, var.project_id
+ )
+ region = lookup(
+ local.ctx.locations, var.region, var.region
)
service_attachments = {
for k, v in local._service_attachments :
@@ -44,36 +59,38 @@ moved {
resource "google_compute_forwarding_rule" "default" {
for_each = var.forwarding_rules_config
provider = google-beta
- project = var.project_id
+ project = local.project_id
name = coalesce(each.value.name, local.forwarding_rule_names[each.key])
- region = var.region
+ region = local.region
description = each.value.description
- ip_address = each.value.address
+ ip_address = try(local.ctx.addresses[each.value.address], each.value.address)
ip_protocol = each.value.protocol
ip_version = each.value.address != null ? null : each.value.ipv6 == true ? "IPV6" : "IPV4" # do not set if address is provided
backend_service = (
google_compute_region_backend_service.default.self_link
)
load_balancing_scheme = "INTERNAL"
- network = var.vpc_config.network
+ network = local.network
ports = each.value.ports # "nnnnn" or "nnnnn,nnnnn,nnnnn" max 5
- subnetwork = var.vpc_config.subnetwork
- allow_global_access = each.value.global_access
- labels = var.labels
- all_ports = each.value.ports == null ? true : null
- service_label = var.service_label
+ subnetwork = lookup(
+ local.ctx.subnets, var.vpc_config.subnetwork, var.vpc_config.subnetwork
+ )
+ allow_global_access = each.value.global_access
+ labels = var.labels
+ all_ports = each.value.ports == null ? true : null
+ service_label = var.service_label
# is_mirroring_collector = false
}
resource "google_compute_region_backend_service" "default" {
provider = google-beta
- project = var.project_id
- region = var.region
+ project = local.project_id
+ region = local.region
name = coalesce(var.backend_service_config.name, var.name)
description = var.backend_service_config.description
load_balancing_scheme = "INTERNAL"
protocol = var.backend_service_config.protocol
- network = var.vpc_config.network
+ network = local.network
health_checks = [local.health_check]
connection_draining_timeout_sec = var.backend_service_config.connection_draining_timeout_sec
session_affinity = var.backend_service_config.session_affinity
@@ -135,12 +152,14 @@ resource "google_compute_region_backend_service" "default" {
resource "google_compute_service_attachment" "default" {
for_each = local.service_attachments
- project = var.project_id
- region = var.region
+ project = local.project_id
+ region = local.region
name = local.forwarding_rule_names[each.key]
description = var.description
target_service = google_compute_forwarding_rule.default[each.key].id
- nat_subnets = each.value.nat_subnets
+ nat_subnets = each.value.nat_subnets == null ? null : [
+ for s in each.value.nat_subnets : lookup(local.ctx.subnets, s, s)
+ ]
connection_preference = (
each.value.automatic_connection ? "ACCEPT_AUTOMATIC" : "ACCEPT_MANUAL"
)
diff --git a/modules/net-lb-int/variables.tf b/modules/net-lb-int/variables.tf
index 5df659c0a..5b023581f 100644
--- a/modules/net-lb-int/variables.tf
+++ b/modules/net-lb-int/variables.tf
@@ -61,6 +61,19 @@ variable "backends" {
nullable = false
}
+variable "context" {
+ description = "Context-specific interpolations."
+ type = object({
+ addresses = optional(map(string), {})
+ locations = optional(map(string), {})
+ networks = optional(map(string), {})
+ project_ids = optional(map(string), {})
+ subnets = optional(map(string), {})
+ })
+ default = {}
+ nullable = false
+}
+
variable "description" {
description = "Optional description used for resources."
type = string
diff --git a/modules/net-vpc-firewall/README.md b/modules/net-vpc-firewall/README.md
index 8750d073f..f074556e8 100644
--- a/modules/net-vpc-firewall/README.md
+++ b/modules/net-vpc-firewall/README.md
@@ -269,13 +269,14 @@ module "firewall" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [network](variables.tf#L111) | Name of the network this set of firewall rules applies to. | string | ✓ | |
-| [project_id](variables.tf#L116) | Project id of the project that holds the network. | string | ✓ | |
-| [default_rules_config](variables.tf#L17) | Optionally created convenience rules. Set the 'disabled' attribute to true, or individual rule attributes to empty lists to disable. | object({…}) | | {} |
-| [egress_rules](variables.tf#L37) | List of egress rule definitions, default to deny action. Null destination ranges will be replaced with 0/0. | map(object({…})) | | {} |
-| [factories_config](variables.tf#L60) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} |
-| [ingress_rules](variables.tf#L70) | List of ingress rule definitions, default to allow action. Null source ranges will be replaced with 0/0. | map(object({…})) | | {} |
-| [named_ranges](variables.tf#L94) | Define mapping of names to ranges that can be used in custom rules. | map(list(string)) | | {…} |
+| [network](variables.tf#L123) | Name of the network this set of firewall rules applies to. | string | ✓ | |
+| [project_id](variables.tf#L128) | Project id of the project that holds the network. | string | ✓ | |
+| [context](variables.tf#L17) | Context-specific interpolations. | object({…}) | | {} |
+| [default_rules_config](variables.tf#L29) | Optionally created convenience rules. Set the 'disabled' attribute to true, or individual rule attributes to empty lists to disable. | object({…}) | | {} |
+| [egress_rules](variables.tf#L49) | List of egress rule definitions, default to deny action. Null destination ranges will be replaced with 0/0. | map(object({…})) | | {} |
+| [factories_config](variables.tf#L72) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} |
+| [ingress_rules](variables.tf#L82) | List of ingress rule definitions, default to allow action. Null source ranges will be replaced with 0/0. | map(object({…})) | | {} |
+| [named_ranges](variables.tf#L106) | Define mapping of names to ranges that can be used in custom rules. | map(list(string)) | | {…} |
## Outputs
diff --git a/modules/net-vpc-firewall/default-rules.tf b/modules/net-vpc-firewall/default-rules.tf
index bbca6dd57..a0f38ab70 100644
--- a/modules/net-vpc-firewall/default-rules.tf
+++ b/modules/net-vpc-firewall/default-rules.tf
@@ -25,23 +25,27 @@ locals {
}
resource "google_compute_firewall" "allow-admins" {
- count = length(local.default_rules.admin_ranges) > 0 ? 1 : 0
- name = "${var.network}-ingress-admins"
- description = "Access from the admin subnet to all subnets."
- network = var.network
- project = var.project_id
- source_ranges = local.default_rules.admin_ranges
+ count = length(local.default_rules.admin_ranges) > 0 ? 1 : 0
+ project = local.project_id
+ network = local.network
+ name = "${local.network_name}-ingress-admins"
+ description = "Access from the admin subnet to all subnets."
+ source_ranges = [
+ for r in local.default_rules.admin_ranges : lookup(local.ctx.cidr_ranges, r, r)
+ ]
allow { protocol = "all" }
}
resource "google_compute_firewall" "allow-tag-http" {
- count = length(local.default_rules.http_ranges) > 0 ? 1 : 0
- name = "${var.network}-ingress-tag-http"
- description = "Allow http to machines with matching tags."
- network = var.network
- project = var.project_id
- source_ranges = local.default_rules.http_ranges
- target_tags = local.default_rules.http_tags
+ count = length(local.default_rules.http_ranges) > 0 ? 1 : 0
+ project = local.project_id
+ network = local.network
+ name = "${local.network_name}-ingress-tag-http"
+ description = "Allow http to machines with matching tags."
+ source_ranges = [
+ for r in local.default_rules.http_ranges : lookup(local.ctx.cidr_ranges, r, r)
+ ]
+ target_tags = local.default_rules.http_tags
allow {
protocol = "tcp"
ports = ["80"]
@@ -49,13 +53,15 @@ resource "google_compute_firewall" "allow-tag-http" {
}
resource "google_compute_firewall" "allow-tag-https" {
- count = length(local.default_rules.https_ranges) > 0 ? 1 : 0
- name = "${var.network}-ingress-tag-https"
- description = "Allow http to machines with matching tags."
- network = var.network
- project = var.project_id
- source_ranges = local.default_rules.https_ranges
- target_tags = local.default_rules.https_tags
+ count = length(local.default_rules.https_ranges) > 0 ? 1 : 0
+ project = local.project_id
+ network = local.network
+ name = "${local.network_name}-ingress-tag-https"
+ description = "Allow http to machines with matching tags."
+ source_ranges = [
+ for r in local.default_rules.https_ranges : lookup(local.ctx.cidr_ranges, r, r)
+ ]
+ target_tags = local.default_rules.https_tags
allow {
protocol = "tcp"
ports = ["443"]
@@ -63,13 +69,15 @@ resource "google_compute_firewall" "allow-tag-https" {
}
resource "google_compute_firewall" "allow-tag-ssh" {
- count = length(local.default_rules.ssh_ranges) > 0 ? 1 : 0
- name = "${var.network}-ingress-tag-ssh"
- description = "Allow SSH to machines with matching tags."
- network = var.network
- project = var.project_id
- source_ranges = local.default_rules.ssh_ranges
- target_tags = local.default_rules.ssh_tags
+ count = length(local.default_rules.ssh_ranges) > 0 ? 1 : 0
+ project = local.project_id
+ network = local.network
+ name = "${local.network_name}-ingress-tag-ssh"
+ description = "Allow SSH to machines with matching tags."
+ source_ranges = [
+ for r in local.default_rules.ssh_ranges : lookup(local.ctx.cidr_ranges, r, r)
+ ]
+ target_tags = local.default_rules.ssh_tags
allow {
protocol = "tcp"
ports = ["22"]
diff --git a/modules/net-vpc-firewall/main.tf b/modules/net-vpc-firewall/main.tf
index 5fed5817e..4a55de8ed 100644
--- a/modules/net-vpc-firewall/main.tf
+++ b/modules/net-vpc-firewall/main.tf
@@ -66,6 +66,15 @@ locals {
for name, rule in merge(var.ingress_rules) :
name => merge(rule, { direction = "INGRESS" })
}
+ ctx = {
+ for k, v in var.context : k => {
+ for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv
+ }
+ }
+ ctx_p = "$"
+ network = lookup(local.ctx.networks, var.network, var.network)
+ network_name = reverse(split("/", local.network))[0]
+ project_id = lookup(local.ctx.project_ids, var.project_id, var.project_id)
# convert rules data to resource format and replace range template variables
rules = {
for name, rule in local._rules :
@@ -94,30 +103,32 @@ locals {
resource "google_compute_firewall" "custom-rules" {
for_each = local.rules
- project = var.project_id
- network = var.network
+ project = local.project_id
+ network = local.network
name = each.key
description = each.value.description
direction = each.value.direction
source_ranges = (
- each.value.direction == "INGRESS"
+ each.value.source_ranges == null
? (
- each.value.source_ranges == null && each.value.sources == null
+ each.value.direction == "INGRESS" && each.value.sources == null
? ["0.0.0.0/0"]
- : each.value.source_ranges
+ : null
)
- #for egress, we will include the source_ranges when provided. Previously, null was forced
- : each.value.source_ranges
+ : [
+ for r in each.value.source_ranges : lookup(local.ctx.cidr_ranges, r, r)
+ ]
)
destination_ranges = (
- each.value.direction == "EGRESS"
+ each.value.destination_ranges == null
? (
- each.value.destination_ranges == null
+ each.value.direction == "EGRESS"
? ["0.0.0.0/0"]
- : each.value.destination_ranges
+ : null
)
- #for ingress, we will include the destination_ranges when provided. Previously, null was forced
- : each.value.destination_ranges
+ : [
+ for r in each.value.destination_ranges : lookup(local.ctx.cidr_ranges, r, r)
+ ]
)
source_tags = (
each.value.use_service_accounts || each.value.direction == "EGRESS"
@@ -126,14 +137,19 @@ resource "google_compute_firewall" "custom-rules" {
)
source_service_accounts = (
each.value.use_service_accounts && each.value.direction == "INGRESS"
- ? each.value.sources
+ ? (each.value.sources == null ? null : [
+ for s in each.value.sources : lookup(local.ctx.iam_principals, s, s)
+ ])
: null
)
target_tags = (
- each.value.use_service_accounts ? null : each.value.targets
+ !each.value.use_service_accounts ? each.value.targets : null
)
target_service_accounts = (
- each.value.use_service_accounts ? each.value.targets : null
+ !each.value.use_service_accounts ? null : (
+ each.value.targets == null ? null : [
+ for s in each.value.targets : lookup(local.ctx.iam_principals, s, s)
+ ])
)
disabled = each.value.disabled == true
priority = each.value.priority
diff --git a/modules/net-vpc-firewall/variables.tf b/modules/net-vpc-firewall/variables.tf
index 104f87d5b..594492694 100644
--- a/modules/net-vpc-firewall/variables.tf
+++ b/modules/net-vpc-firewall/variables.tf
@@ -14,6 +14,18 @@
* limitations under the License.
*/
+variable "context" {
+ description = "Context-specific interpolations."
+ type = object({
+ cidr_ranges = optional(map(string), {})
+ iam_principals = optional(map(string), {})
+ networks = optional(map(string), {})
+ project_ids = optional(map(string), {})
+ })
+ default = {}
+ nullable = false
+}
+
variable "default_rules_config" {
description = "Optionally created convenience rules. Set the 'disabled' attribute to true, or individual rule attributes to empty lists to disable."
type = object({
diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md
index 8309ec6c4..af95ac727 100644
--- a/modules/net-vpc/README.md
+++ b/modules/net-vpc/README.md
@@ -486,7 +486,7 @@ module "vpc" {
project_id = var.project_id
name = "my-network"
context = {
- regions = {
+ locations = {
primary = "europe-west4"
secondary = "europe-west8"
}
@@ -500,7 +500,7 @@ module "vpc" {
```yaml
name: simple
-region: $regions:primary
+region: $locations:primary
ip_cidr_range: 10.0.1.0/24
# tftest-file id=subnet-simple path=config/subnets/subnet-simple.yaml schema=subnet.schema.json
@@ -899,32 +899,32 @@ secondary_ip_ranges:
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L171) | The name of the network being created. | string | ✓ | |
-| [project_id](variables.tf#L248) | The ID of the project where this VPC will be created. | string | ✓ | |
+| [name](variables.tf#L184) | The name of the network being created. | string | ✓ | |
+| [project_id](variables.tf#L261) | The ID of the project where this VPC will be created. | string | ✓ | |
| [auto_create_subnetworks](variables.tf#L17) | Set to true to create an auto mode subnet, defaults to custom mode. | bool | | false |
-| [context](variables.tf#L23) | Context-specific interpolations. | object({…}) | | {} |
-| [create_googleapis_routes](variables.tf#L32) | Toggle creation of googleapis private/restricted routes. Disabled when vpc creation is turned off, or when set to null. | object({…}) | | {} |
-| [delete_default_routes_on_create](variables.tf#L45) | Set to true to delete the default routes at creation time. | bool | | false |
-| [description](variables.tf#L51) | An optional description of this resource (triggers recreation on change). | string | | "Terraform-managed." |
-| [dns_policy](variables.tf#L57) | DNS policy setup for the VPC. | object({…}) | | null |
-| [factories_config](variables.tf#L70) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} |
-| [firewall_policy_enforcement_order](variables.tf#L79) | Order that Firewall Rules and Firewall Policies are evaluated. Can be either 'BEFORE_CLASSIC_FIREWALL' or 'AFTER_CLASSIC_FIREWALL'. | string | | "AFTER_CLASSIC_FIREWALL" |
-| [internal_ranges](variables.tf#L91) | Internal range configuration for IPAM operations within the VPC network. | list(object({…})) | | [] |
-| [ipv6_config](variables.tf#L155) | Optional IPv6 configuration for this network. | object({…}) | | {} |
-| [mtu](variables.tf#L165) | Maximum Transmission Unit in bytes. The minimum value for this field is 1460 (the default) and the maximum value is 1500 bytes. | number | | null |
-| [network_attachments](variables.tf#L176) | PSC network attachments, names as keys. | map(object({…})) | | {} |
-| [peering_config](variables.tf#L189) | VPC peering configuration. | object({…}) | | null |
-| [policy_based_routes](variables.tf#L200) | Policy based routes, keyed by name. | map(object({…})) | | {} |
-| [psa_configs](variables.tf#L253) | The Private Service Access configuration. | list(object({…})) | | [] |
-| [routes](variables.tf#L284) | Network routes, keyed by name. | map(object({…})) | | {} |
-| [routing_mode](variables.tf#L305) | The network routing mode (default 'GLOBAL'). | string | | "GLOBAL" |
-| [shared_vpc_host](variables.tf#L315) | Enable shared VPC for this project. | bool | | false |
-| [shared_vpc_service_projects](variables.tf#L321) | Shared VPC service projects to register with this host. | list(string) | | [] |
-| [subnets](variables.tf#L327) | Subnet configuration. | list(object({…})) | | [] |
-| [subnets_private_nat](variables.tf#L407) | List of private NAT subnets. | list(object({…})) | | [] |
-| [subnets_proxy_only](variables.tf#L419) | 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#L453) | List of subnets for Private Service Connect service producers. | list(object({…})) | | [] |
-| [vpc_reuse](variables.tf#L485) | Reuse existing VPC if not null. If the network_id number is not passed in, a data source is used. | object({…}) | | null |
+| [context](variables.tf#L23) | Context-specific interpolations. | object({…}) | | {} |
+| [create_googleapis_routes](variables.tf#L45) | Toggle creation of googleapis private/restricted routes. Disabled when vpc creation is turned off, or when set to null. | object({…}) | | {} |
+| [delete_default_routes_on_create](variables.tf#L58) | Set to true to delete the default routes at creation time. | bool | | false |
+| [description](variables.tf#L64) | An optional description of this resource (triggers recreation on change). | string | | "Terraform-managed." |
+| [dns_policy](variables.tf#L70) | DNS policy setup for the VPC. | object({…}) | | null |
+| [factories_config](variables.tf#L83) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} |
+| [firewall_policy_enforcement_order](variables.tf#L92) | Order that Firewall Rules and Firewall Policies are evaluated. Can be either 'BEFORE_CLASSIC_FIREWALL' or 'AFTER_CLASSIC_FIREWALL'. | string | | "AFTER_CLASSIC_FIREWALL" |
+| [internal_ranges](variables.tf#L104) | Internal range configuration for IPAM operations within the VPC network. | list(object({…})) | | [] |
+| [ipv6_config](variables.tf#L168) | Optional IPv6 configuration for this network. | object({…}) | | {} |
+| [mtu](variables.tf#L178) | Maximum Transmission Unit in bytes. The minimum value for this field is 1460 (the default) and the maximum value is 1500 bytes. | number | | null |
+| [network_attachments](variables.tf#L189) | PSC network attachments, names as keys. | map(object({…})) | | {} |
+| [peering_config](variables.tf#L202) | VPC peering configuration. | object({…}) | | null |
+| [policy_based_routes](variables.tf#L213) | Policy based routes, keyed by name. | map(object({…})) | | {} |
+| [psa_configs](variables.tf#L266) | The Private Service Access configuration. | list(object({…})) | | [] |
+| [routes](variables.tf#L297) | Network routes, keyed by name. | map(object({…})) | | {} |
+| [routing_mode](variables.tf#L318) | The network routing mode (default 'GLOBAL'). | string | | "GLOBAL" |
+| [shared_vpc_host](variables.tf#L328) | Enable shared VPC for this project. | bool | | false |
+| [shared_vpc_service_projects](variables.tf#L334) | Shared VPC service projects to register with this host. | list(string) | | [] |
+| [subnets](variables.tf#L340) | Subnet configuration. | list(object({…})) | | [] |
+| [subnets_private_nat](variables.tf#L420) | List of private NAT subnets. | list(object({…})) | | [] |
+| [subnets_proxy_only](variables.tf#L432) | 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#L466) | List of subnets for Private Service Connect service producers. | list(object({…})) | | [] |
+| [vpc_reuse](variables.tf#L498) | Reuse existing VPC if not null. If the network_id number is not passed in, a data source is used. | object({…}) | | null |
## Outputs
diff --git a/modules/net-vpc/internal-ranges.tf b/modules/net-vpc/internal-ranges.tf
index 2304919d4..0a78b6717 100644
--- a/modules/net-vpc/internal-ranges.tf
+++ b/modules/net-vpc/internal-ranges.tf
@@ -52,12 +52,10 @@ locals {
}
}
}
-
internal_ranges = merge(
{ for r in var.internal_ranges : r.name => r },
local._factory_internal_ranges
)
-
internal_ranges_ids = {
for k, v in google_network_connectivity_internal_range.internal_range :
k => v.id
@@ -65,14 +63,15 @@ locals {
}
resource "google_network_connectivity_internal_range" "internal_range" {
- provider = google-beta
- for_each = local.internal_ranges
- project = var.project_id
- name = each.value.name
- network = local.network.id
-
- description = each.value.description
- ip_cidr_range = each.value.ip_cidr_range
+ provider = google-beta
+ for_each = local.internal_ranges
+ project = local.project_id
+ name = each.value.name
+ network = local.network.id
+ description = each.value.description
+ ip_cidr_range = try(
+ local.ctx.cidr_ranges[each.value.ip_cidr_range], each.value.ip_cidr_range
+ )
labels = each.value.labels
usage = each.value.usage
peering = each.value.peering
diff --git a/modules/net-vpc/main.tf b/modules/net-vpc/main.tf
index 6fa23edd9..446505cb5 100644
--- a/modules/net-vpc/main.tf
+++ b/modules/net-vpc/main.tf
@@ -15,11 +15,14 @@
*/
locals {
- ctx = {
+ _ctx = {
for k, v in var.context : k => {
for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv
}
}
+ ctx = merge(local._ctx, {
+ locations = merge(local._ctx.regions, local._ctx.locations)
+ })
ctx_p = "$"
network = (
var.vpc_reuse == null
@@ -56,19 +59,28 @@ locals {
peer_network = (
var.peering_config == null
? null
- : element(reverse(split("/", var.peering_config.peer_vpc_self_link)), 0)
+ : (
+ startswith(var.peering_config.peer_vpc_self_link, "$networks:")
+ ? lookup(
+ local.ctx.networks,
+ var.peering_config.peer_vpc_self_link,
+ var.peering_config.peer_vpc_self_link
+ )
+ : element(reverse(split("/", var.peering_config.peer_vpc_self_link)), 0)
+ )
)
+ project_id = lookup(local.ctx.project_ids, var.project_id, var.project_id)
}
data "google_compute_network" "network" {
count = try(var.vpc_reuse.use_data_source, null) == true ? 1 : 0
name = var.name
- project = var.project_id
+ project = local.project_id
}
resource "google_compute_network" "network" {
count = var.vpc_reuse == null ? 1 : 0
- project = var.project_id
+ project = local.project_id
name = var.name
description = var.description
auto_create_subnetworks = var.auto_create_subnetworks
@@ -81,11 +93,15 @@ resource "google_compute_network" "network" {
}
resource "google_compute_network_peering" "local" {
- provider = google-beta
- count = var.peering_config == null ? 0 : 1
- name = "${var.name}-${local.peer_network}"
- network = local.network.self_link
- peer_network = var.peering_config.peer_vpc_self_link
+ provider = google-beta
+ count = var.peering_config == null ? 0 : 1
+ name = "${var.name}-${local.peer_network}"
+ network = local.network.self_link
+ peer_network = lookup(
+ local.ctx.networks,
+ var.peering_config.peer_vpc_self_link,
+ var.peering_config.peer_vpc_self_link
+ )
export_custom_routes = var.peering_config.export_routes
import_custom_routes = var.peering_config.import_routes
}
@@ -97,8 +113,12 @@ resource "google_compute_network_peering" "remote" {
? 1
: 0
)
- name = "${local.peer_network}-${var.name}"
- network = var.peering_config.peer_vpc_self_link
+ name = "${local.peer_network}-${var.name}"
+ network = lookup(
+ local.ctx.networks,
+ var.peering_config.peer_vpc_self_link,
+ var.peering_config.peer_vpc_self_link
+ )
peer_network = local.network.self_link
export_custom_routes = var.peering_config.import_routes
import_custom_routes = var.peering_config.export_routes
@@ -108,7 +128,7 @@ resource "google_compute_network_peering" "remote" {
resource "google_compute_shared_vpc_host_project" "shared_vpc_host" {
provider = google-beta
count = var.shared_vpc_host ? 1 : 0
- project = var.project_id
+ project = local.project_id
depends_on = [local.network]
}
@@ -119,14 +139,14 @@ resource "google_compute_shared_vpc_service_project" "service_projects" {
? var.shared_vpc_service_projects
: []
)
- host_project = var.project_id
- service_project = each.value
+ host_project = local.project_id
+ service_project = lookup(local.ctx.project_ids, each.value, each.value)
depends_on = [google_compute_shared_vpc_host_project.shared_vpc_host]
}
resource "google_dns_policy" "default" {
count = var.dns_policy == null ? 0 : 1
- project = var.project_id
+ project = local.project_id
name = var.name
enable_inbound_forwarding = try(var.dns_policy.inbound, null)
enable_logging = try(var.dns_policy.logging, null)
@@ -145,7 +165,7 @@ resource "google_dns_policy" "default" {
)
iterator = ns
content {
- ipv4_address = ns.value
+ ipv4_address = lookup(local.ctx.addresses, ns.value, ns.value)
forwarding_path = "private"
}
}
@@ -157,7 +177,7 @@ resource "google_dns_policy" "default" {
)
iterator = ns
content {
- ipv4_address = ns.value
+ ipv4_address = lookup(local.ctx.addresses, ns.value, ns.value)
}
}
}
diff --git a/modules/net-vpc/psa.tf b/modules/net-vpc/psa.tf
index 959193896..8b9855896 100644
--- a/modules/net-vpc/psa.tf
+++ b/modules/net-vpc/psa.tf
@@ -44,7 +44,8 @@ locals {
})
}
psa_configs_ranges = {
- for v in local._psa_configs_ranges : v.key => v.value
+ for v in local._psa_configs_ranges :
+ v.key => lookup(local.ctx.cidr_ranges, v.value, v.value)
}
psa_peered_domains = {
for v in local._psa_peered_domains : v.key => v
@@ -53,7 +54,7 @@ locals {
resource "google_compute_global_address" "psa_ranges" {
for_each = local.psa_configs_ranges
- project = var.project_id
+ project = local.project_id
network = local.network.id
name = each.key
purpose = "VPC_PEERING"
diff --git a/modules/net-vpc/routes.tf b/modules/net-vpc/routes.tf
index e8fd8812b..c31440aee 100644
--- a/modules/net-vpc/routes.tf
+++ b/modules/net-vpc/routes.tf
@@ -50,36 +50,42 @@ locals {
}
resource "google_compute_route" "gateway" {
- for_each = local.routes.gateway
- project = var.project_id
- network = local.network.name
- name = "${var.name}-${each.key}"
- description = each.value.description
- dest_range = each.value.dest_range
+ for_each = local.routes.gateway
+ project = local.project_id
+ network = local.network.name
+ name = "${var.name}-${each.key}"
+ description = each.value.description
+ dest_range = lookup(
+ local.ctx.cidr_ranges, each.value.dest_range, each.value.dest_range
+ )
priority = each.value.priority
tags = each.value.tags
next_hop_gateway = each.value.next_hop
}
resource "google_compute_route" "ilb" {
- for_each = local.routes.ilb
- project = var.project_id
- network = local.network.name
- name = "${var.name}-${each.key}"
- description = each.value.description
- dest_range = each.value.dest_range
+ for_each = local.routes.ilb
+ project = local.project_id
+ network = local.network.name
+ name = "${var.name}-${each.key}"
+ description = each.value.description
+ dest_range = lookup(
+ local.ctx.cidr_ranges, each.value.dest_range, each.value.dest_range
+ )
priority = each.value.priority
tags = each.value.tags
next_hop_ilb = each.value.next_hop
}
resource "google_compute_route" "instance" {
- for_each = local.routes.instance
- project = var.project_id
- network = local.network.name
- name = "${var.name}-${each.key}"
- description = each.value.description
- dest_range = each.value.dest_range
+ for_each = local.routes.instance
+ project = local.project_id
+ network = local.network.name
+ name = "${var.name}-${each.key}"
+ description = each.value.description
+ dest_range = lookup(
+ local.ctx.cidr_ranges, each.value.dest_range, each.value.dest_range
+ )
priority = each.value.priority
tags = each.value.tags
next_hop_instance = each.value.next_hop
@@ -89,23 +95,29 @@ resource "google_compute_route" "instance" {
resource "google_compute_route" "ip" {
for_each = local.routes.ip
- project = var.project_id
+ project = local.project_id
network = local.network.name
name = "${var.name}-${each.key}"
description = each.value.description
- dest_range = each.value.dest_range
- priority = each.value.priority
- tags = each.value.tags
- next_hop_ip = each.value.next_hop
+ dest_range = lookup(
+ local.ctx.cidr_ranges, each.value.dest_range, each.value.dest_range
+ )
+ priority = each.value.priority
+ tags = each.value.tags
+ next_hop_ip = lookup(
+ local.ctx.addresses, each.value.next_hop, each.value.next_hop
+ )
}
resource "google_compute_route" "vpn_tunnel" {
- for_each = local.routes.vpn_tunnel
- project = var.project_id
- network = local.network.name
- name = "${var.name}-${each.key}"
- description = each.value.description
- dest_range = each.value.dest_range
+ for_each = local.routes.vpn_tunnel
+ project = local.project_id
+ network = local.network.name
+ name = "${var.name}-${each.key}"
+ description = each.value.description
+ dest_range = lookup(
+ local.ctx.cidr_ranges, each.value.dest_range, each.value.dest_range
+ )
priority = each.value.priority
tags = each.value.tags
next_hop_vpn_tunnel = each.value.next_hop
@@ -113,7 +125,7 @@ resource "google_compute_route" "vpn_tunnel" {
resource "google_network_connectivity_policy_based_route" "default" {
for_each = var.policy_based_routes
- project = var.project_id
+ project = local.project_id
network = local.network.id
name = "${var.name}-${each.key}"
description = each.value.description
diff --git a/modules/net-vpc/subnets.tf b/modules/net-vpc/subnets.tf
index 5d3ff70f0..6d6f5891e 100644
--- a/modules/net-vpc/subnets.tf
+++ b/modules/net-vpc/subnets.tf
@@ -23,7 +23,7 @@ locals {
}
_factory_data = {
for k, v in local._factory_data_raw : k => merge(v, {
- region_computed = lookup(local.ctx.regions, v.region, v.region)
+ region_computed = lookup(local.ctx.locations, v.region, v.region)
})
}
_factory_path = try(pathexpand(var.factories_config.subnets_folder), null)
@@ -88,7 +88,9 @@ locals {
_is_proxy_only = try(v.proxy_only == true, false)
}
}
-
+ _iam_subnets = concat(
+ var.subnets, var.subnets_psc, var.subnets_proxy_only, values(local._factory_subnets)
+ )
all_subnets = merge(
{ for k, v in google_compute_subnetwork.subnetwork : k => v },
{ for k, v in google_compute_subnetwork.proxy_only : k => v },
@@ -96,22 +98,21 @@ locals {
)
subnet_iam = flatten(concat(
[
- for s in concat(var.subnets, var.subnets_psc, var.subnets_proxy_only, values(local._factory_subnets)) : [
- for role, members in s.iam :
- {
- role = role
+ for s in local._iam_subnets : [
+ for role, members in s.iam : {
+ role = lookup(local.ctx.custom_roles, role, role)
members = members
- subnet = "${s.region}/${s.name}"
+ subnet = "${lookup(local.ctx.locations, s.region, s.region)}/${s.name}"
}
]
],
))
subnet_iam_bindings = merge([
- for s in concat(var.subnets, var.subnets_psc, var.subnets_proxy_only, values(local._factory_subnets)) : {
+ for s in local._iam_subnets : {
for key, data in s.iam_bindings :
key => {
- role = data.role
- subnet = "${s.region}/${s.name}"
+ role = lookup(local.ctx.custom_roles, data.role, data.role)
+ subnet = "${lookup(local.ctx.locations, s.region, s.region)}/${s.name}"
members = data.members
condition = data.condition
}
@@ -121,42 +122,60 @@ locals {
# In other words, if you have multiple additive bindings with the
# same name, only one will be used
subnet_iam_bindings_additive = merge([
- for s in concat(var.subnets, var.subnets_psc, var.subnets_proxy_only, values(local._factory_subnets)) : {
+ for s in local._iam_subnets : {
for key, data in s.iam_bindings_additive :
key => {
- role = data.role
- subnet = "${s.region}/${s.name}"
+ role = lookup(local.ctx.custom_roles, data.role, data.role)
+ subnet = "${lookup(local.ctx.locations, s.region, s.region)}/${s.name}"
member = data.member
condition = data.condition
}
}
]...)
subnets = merge(
- { for s in var.subnets : "${s.region}/${s.name}" => s },
+ {
+ for s in var.subnets :
+ "${lookup(local.ctx.locations, s.region, s.region)}/${s.name}" => s
+ },
{ for k, v in local._factory_subnets : k => v if v._is_regular }
)
subnets_proxy_only = merge(
- { for s in var.subnets_proxy_only : "${s.region}/${s.name}" => s },
+ {
+ for s in var.subnets_proxy_only :
+ "${lookup(local.ctx.locations, s.region, s.region)}/${s.name}" => s
+ },
{ for k, v in local._factory_subnets : k => v if v._is_proxy_only },
)
subnets_private_nat = merge(
- { for s in var.subnets_private_nat : "${s.region}/${s.name}" => s },
+ {
+ for s in var.subnets_private_nat :
+ "${lookup(local.ctx.locations, s.region, s.region)}/${s.name}" => s
+ },
# { for k, v in local._factory_subnets : k => v if v._is_proxy_only },
)
subnets_psc = merge(
- { for s in var.subnets_psc : "${s.region}/${s.name}" => s },
+ {
+ for s in var.subnets_psc :
+ "${lookup(local.ctx.locations, s.region, s.region)}/${s.name}" => s
+ },
{ for k, v in local._factory_subnets : k => v if v._is_psc }
)
}
resource "google_compute_subnetwork" "subnetwork" {
- provider = google-beta
- for_each = local.subnets
- project = var.project_id
- network = local.network.name
- name = each.value.name
- region = each.value.region
- ip_cidr_range = try(each.value.ipv6.ipv6_only, false) ? null : each.value.ip_cidr_range
+ provider = google-beta
+ for_each = local.subnets
+ project = local.project_id
+ network = local.network.name
+ name = each.value.name
+ region = lookup(
+ local.ctx.locations, each.value.region, each.value.region
+ )
+ ip_cidr_range = (
+ try(each.value.ipv6.ipv6_only, false)
+ ? null
+ : try(local.ctx.cidr_ranges[each.value.ip_cidr_range], each.value.ip_cidr_range)
+ )
allow_subnet_cidr_routes_overlap = each.value.allow_subnet_cidr_routes_overlap
reserved_internal_range = (
each.value.reserved_internal_range != null
@@ -189,8 +208,13 @@ resource "google_compute_subnetwork" "subnetwork" {
dynamic "secondary_ip_range" {
for_each = each.value.secondary_ip_ranges == null ? {} : each.value.secondary_ip_ranges
content {
- range_name = secondary_ip_range.key
- ip_cidr_range = try(secondary_ip_range.value.ip_cidr_range, secondary_ip_range.value)
+ range_name = secondary_ip_range.key
+ ip_cidr_range = try(
+ local.ctx.cidr_ranges[secondary_ip_range.value.ip_cidr_range],
+ secondary_ip_range.value.ip_cidr_range,
+ local.ctx.cidr_ranges[secondary_ip_range.value],
+ secondary_ip_range.value
+ )
reserved_internal_range = (
try(secondary_ip_range.value.reserved_internal_range, null) != null
? "networkconnectivity.googleapis.com/${try(local.internal_ranges_ids[secondary_ip_range.value.reserved_internal_range], secondary_ip_range.value.reserved_internal_range)}"
@@ -215,12 +239,16 @@ resource "google_compute_subnetwork" "subnetwork" {
}
resource "google_compute_subnetwork" "proxy_only" {
- for_each = local.subnets_proxy_only
- project = var.project_id
- network = local.network.name
- name = each.value.name
- region = each.value.region
- ip_cidr_range = each.value.ip_cidr_range
+ for_each = local.subnets_proxy_only
+ project = local.project_id
+ network = local.network.name
+ name = each.value.name
+ region = lookup(
+ local.ctx.locations, each.value.region, each.value.region
+ )
+ ip_cidr_range = lookup(
+ local.ctx.cidr_ranges, each.value.ip_cidr_range, each.value.ip_cidr_range
+ )
description = (
# Set description to an empty string (eg "") to create subnet without a description.
each.value.description == null
@@ -232,12 +260,16 @@ resource "google_compute_subnetwork" "proxy_only" {
}
resource "google_compute_subnetwork" "private_nat" {
- for_each = local.subnets_private_nat
- project = var.project_id
- network = local.network.name
- name = each.value.name
- region = each.value.region
- ip_cidr_range = each.value.ip_cidr_range
+ for_each = local.subnets_private_nat
+ project = local.project_id
+ network = local.network.name
+ name = each.value.name
+ region = lookup(
+ local.ctx.locations, each.value.region, each.value.region
+ )
+ ip_cidr_range = lookup(
+ local.ctx.cidr_ranges, each.value.ip_cidr_range, each.value.ip_cidr_range
+ )
description = (
# Set description to an empty string (eg "") to create subnet without a description.
each.value.description == null
@@ -248,12 +280,16 @@ resource "google_compute_subnetwork" "private_nat" {
}
resource "google_compute_subnetwork" "psc" {
- for_each = local.subnets_psc
- project = var.project_id
- network = local.network.name
- name = each.value.name
- region = each.value.region
- ip_cidr_range = each.value.ip_cidr_range
+ for_each = local.subnets_psc
+ project = local.project_id
+ network = local.network.name
+ name = each.value.name
+ region = lookup(
+ local.ctx.locations, each.value.region, each.value.region
+ )
+ ip_cidr_range = lookup(
+ local.ctx.cidr_ranges, each.value.ip_cidr_range, each.value.ip_cidr_range
+ )
description = (
# Set description to an empty string (eg "") to create subnet without a description.
each.value.description == null
@@ -269,24 +305,30 @@ resource "google_compute_subnetwork_iam_binding" "authoritative" {
for binding in local.subnet_iam :
"${binding.subnet}.${binding.role}" => binding
}
- project = var.project_id
+ project = local.project_id
subnetwork = local.all_subnets[each.value.subnet].name
region = local.all_subnets[each.value.subnet].region
role = each.value.role
- members = each.value.members
+ members = [
+ for m in each.value.members : lookup(local.ctx.iam_principals, m, m)
+ ]
}
resource "google_compute_subnetwork_iam_binding" "bindings" {
for_each = local.subnet_iam_bindings
- project = var.project_id
+ project = local.project_id
subnetwork = local.all_subnets[each.value.subnet].name
region = local.all_subnets[each.value.subnet].region
role = each.value.role
- members = each.value.members
+ members = [
+ for m in each.value.members : lookup(local.ctx.iam_principals, m, m)
+ ]
dynamic "condition" {
for_each = each.value.condition == null ? [] : [""]
content {
- expression = each.value.condition.expression
+ expression = templatestring(
+ each.value.condition.expression, var.context.condition_vars
+ )
title = each.value.condition.title
description = each.value.condition.description
}
@@ -295,15 +337,19 @@ resource "google_compute_subnetwork_iam_binding" "bindings" {
resource "google_compute_subnetwork_iam_member" "bindings" {
for_each = local.subnet_iam_bindings_additive
- project = var.project_id
+ project = local.project_id
subnetwork = local.all_subnets[each.value.subnet].name
region = local.all_subnets[each.value.subnet].region
role = each.value.role
- member = each.value.member
+ member = lookup(
+ local.ctx.iam_principals, each.value.member, each.value.member
+ )
dynamic "condition" {
for_each = each.value.condition == null ? [] : [""]
content {
- expression = each.value.condition.expression
+ expression = templatestring(
+ each.value.condition.expression, var.context.condition_vars
+ )
title = each.value.condition.title
description = each.value.condition.description
}
@@ -313,7 +359,7 @@ resource "google_compute_subnetwork_iam_member" "bindings" {
resource "google_compute_network_attachment" "default" {
provider = google-beta
for_each = var.network_attachments
- project = var.project_id
+ project = local.project_id
region = google_compute_subnetwork.subnetwork[each.value.subnet].region
name = each.key
description = each.value.description
diff --git a/modules/net-vpc/variables.tf b/modules/net-vpc/variables.tf
index 646a75212..1f7c2e02f 100644
--- a/modules/net-vpc/variables.tf
+++ b/modules/net-vpc/variables.tf
@@ -23,10 +23,23 @@ variable "auto_create_subnetworks" {
variable "context" {
description = "Context-specific interpolations."
type = object({
- regions = optional(map(string), {})
+ addresses = optional(map(string), {})
+ cidr_ranges = optional(map(string), {})
+ condition_vars = optional(map(map(string)), {})
+ custom_roles = optional(map(string), {})
+ iam_principals = optional(map(string), {})
+ locations = optional(map(string), {})
+ networks = optional(map(string), {})
+ # legacy context
+ regions = optional(map(string), {})
+ project_ids = optional(map(string), {})
})
default = {}
nullable = false
+ validation {
+ condition = length(var.context.regions) == 0 || length(var.context.locations) == 0
+ error_message = "Only one of locations, regions can be used."
+ }
}
variable "create_googleapis_routes" {
diff --git a/modules/organization/README.md b/modules/organization/README.md
index 0ee6f52eb..2ad352edb 100644
--- a/modules/organization/README.md
+++ b/modules/organization/README.md
@@ -385,12 +385,12 @@ module "org" {
source = "./fabric/modules/organization"
organization_id = var.organization_id
custom_roles = {
- "myRole" = [
+ "myRole${replace(var.prefix, "/[^a-zA-Z0-9_\\.]/", "")}" = [
"compute.instances.list",
]
}
iam = {
- (module.org.custom_role_id.myRole) = ["group:${var.group_email}"]
+ (module.org.custom_role_id["myRole${replace(var.prefix, "/[^a-zA-Z0-9_\\.]/", "")}"]) = ["group:${var.group_email}"]
}
}
# tftest modules=1 resources=2 inventory=roles.yaml e2e serial
diff --git a/modules/project/README.md b/modules/project/README.md
index 3400561eb..fba6147fc 100644
--- a/modules/project/README.md
+++ b/modules/project/README.md
@@ -39,6 +39,7 @@ This module implements the creation and management of one GCP project including
- [Files](#files)
- [Variables](#variables)
- [Outputs](#outputs)
+- [Fixtures](#fixtures)
## Basic Project Creation
@@ -78,10 +79,6 @@ IAM also supports variable interpolation for both roles and principals, via the
The `iam` variable is based on role keys and is typically used for service accounts, or where member values can be dynamic and would create potential problems in the underlying `for_each` cycle.
```hcl
-locals {
- gke_service_account = "my_gke_service_account"
-}
-
module "project" {
source = "./fabric/modules/project"
billing_account = var.billing_account_id
@@ -94,22 +91,19 @@ module "project" {
]
context = {
custom_roles = {
- my_role = "organizations/1234567890/roles/myRole"
+ my_role = google_organization_iam_custom_role.custom_role.id # or module.organization.custom_roles["my_role"].id
}
iam_principals = {
- org_admins = "group:gcp-organization-admins@example.com"
+ org_admins = "group:${var.group_email}"
}
}
iam = {
- "roles/container.hostServiceAgentUser" = [
- "serviceAccount:${local.gke_service_account}"
- ]
"$custom_roles:my_role" = [
"$iam_principals:org_admins"
]
}
}
-# tftest modules=1 resources=8 inventory=iam-authoritative.yaml
+# tftest fixtures=fixtures/organization-custom-role.tf inventory=iam-authoritative.yaml e2e
```
The `iam_by_principals` variable uses [principals](https://cloud.google.com/iam/docs/principal-identifiers) as keys and is a convenient way to assign roles to humans following Google's best practices. The end result is readable code that also serves as documentation.
@@ -123,10 +117,10 @@ module "project" {
prefix = var.prefix
context = {
custom_roles = {
- my_role = "organizations/1234567890/roles/myRole"
+ my_role = google_organization_iam_custom_role.custom_role.id # or module.organization.custom_roles["my_role"].id
}
iam_principals = {
- org_admins = "group:gcp-organization-admins@example.com"
+ org_admins = "group:${var.group_email}"
}
}
iam_by_principals = {
@@ -142,7 +136,7 @@ module "project" {
]
}
}
-# tftest modules=1 resources=7 inventory=iam-group.yaml e2e
+# tftest fixtures=fixtures/organization-custom-role.tf inventory=iam-group.yaml e2e
```
The `iam_bindings` variable behaves like a more verbose version of `iam`, and allows setting binding-level IAM conditions.
@@ -158,11 +152,13 @@ module "project" {
"stackdriver.googleapis.com"
]
context = {
- custom_roles = {
- my_role = "organizations/1234567890/roles/myRole"
+ condition_vars = {
+ custom_roles = {
+ my_role = google_organization_iam_custom_role.custom_role.id # or module.organization.custom_roles["my_role"].id
+ }
}
iam_principals = {
- org_admins = "group:gcp-organization-admins@example.com"
+ org_admins = "group:${var.group_email}"
}
}
iam_bindings = {
@@ -173,19 +169,19 @@ module "project" {
]
role = "roles/resourcemanager.projectIamAdmin"
condition = {
- title = "delegated_network_user_one"
+ title = "delegated_custom_role"
expression = <<-END
api.getAttribute(
'iam.googleapis.com/modifiedGrantsByRole', []
).hasOnly([
- 'roles/compute.networkAdmin'
+ '$${custom_roles.my_role}'
])
END
}
}
}
}
-# tftest modules=1 resources=3 inventory=iam-bindings.yaml e2e
+# tftest fixtures=fixtures/organization-custom-role.tf inventory=iam-bindings.yaml e2e
```
### Additive IAM
@@ -206,7 +202,7 @@ module "project" {
]
context = {
iam_principals = {
- org_admins = "group:gcp-organization-admins@example.com"
+ org_admins = "group:${var.group_email}"
}
}
iam_bindings_additive = {
@@ -1248,11 +1244,37 @@ module "project" {
"compute.instances.list",
]
}
+ context = {
+ condition_vars = {
+ custom_roles = {
+ my_role = "organizations/1234567890/roles/myRole"
+ }
+ }
+ }
iam = {
(module.project.custom_role_id.myRole) = ["group:${var.group_email}"]
}
+ iam_bindings = {
+ iam_admin_conditional = {
+ members = [
+ "group:${var.group_email}",
+ "$iam_principals:org_admins"
+ ]
+ role = "roles/resourcemanager.projectIamAdmin"
+ condition = {
+ title = "delegated_custom_role"
+ expression = <<-END
+ api.getAttribute(
+ 'iam.googleapis.com/modifiedGrantsByRole', []
+ ).hasOnly([
+ '$${custom_roles.my_role}'
+ ])
+ END
+ }
+ }
+ }
}
-# tftest modules=1 resources=3
+# tftest inventory=custom-role-iam.yaml
```
### Custom Roles Factory
@@ -1939,4 +1961,8 @@ alerts:
| [sink_writer_identities](outputs.tf#L182) | Writer identities created for each sink. | |
| [tag_keys](outputs.tf#L189) | Tag key resources. | |
| [tag_values](outputs.tf#L198) | Tag value resources. | |
+
+## Fixtures
+
+- [organization-custom-role.tf](../../tests/fixtures/organization-custom-role.tf)
diff --git a/modules/secure-source-manager-instance/README.md b/modules/secure-source-manager-instance/README.md
index 42f0e52d2..73b102fd6 100644
--- a/modules/secure-source-manager-instance/README.md
+++ b/modules/secure-source-manager-instance/README.md
@@ -11,6 +11,7 @@ This module allows to create a Secure Source Manager instance and repositories i
- [Private instance](#private-instance)
- [IAM](#iam)
- [Branch Protection Rules](#branch-protection-rules)
+ - [Initial Configuration](#initial-configuration)
- [Variables](#variables)
- [Outputs](#outputs)
@@ -195,6 +196,26 @@ module "ssm_instance" {
}
# tftest modules=1 resources=3 inventory=branch-protection-rules.yaml
```
+
+### Initial Configuration
+
+```hcl
+module "ssm_instance" {
+ source = "./fabric/modules/secure-source-manager-instance"
+ project_id = var.project_id
+ instance_id = "my-instance"
+ location = var.region
+ repositories = {
+ my-repository = {
+ initial_config = {
+ default_branch = "main"
+ gitignores = ["terraform.tfstate"]
+ }
+ }
+ }
+}
+# tftest inventory=initial-config.yaml
+```
## Variables
@@ -203,7 +224,7 @@ module "ssm_instance" {
| [instance_id](variables.tf#L23) | Instance ID. | string | ✓ | |
| [location](variables.tf#L40) | Location. | string | ✓ | |
| [project_id](variables.tf#L55) | Project ID. | string | ✓ | |
-| [repositories](variables.tf#L60) | Repositories. | map(object({…})) | ✓ | |
+| [repositories](variables.tf#L60) | Repositories. | map(object({…})) | ✓ | |
| [iam](variables-iam.tf#L17) | IAM bindings. | map(list(string)) | | {} |
| [iam_bindings](variables-iam.tf#L23) | IAM bindings. | map(object({…})) | | {} |
| [iam_bindings_additive](variables-iam.tf#L32) | IAM bindings. | map(object({…})) | | {} |
diff --git a/modules/secure-source-manager-instance/variables.tf b/modules/secure-source-manager-instance/variables.tf
index 8a2b097e3..49cd81393 100644
--- a/modules/secure-source-manager-instance/variables.tf
+++ b/modules/secure-source-manager-instance/variables.tf
@@ -72,7 +72,7 @@ variable "repositories" {
})), {})
initial_config = optional(object({
default_branch = optional(string)
- gitignores = optional(string)
+ gitignores = optional(list(string))
license = optional(string)
readme = optional(string)
}))
diff --git a/tests/fixtures/organization-custom-role.tf b/tests/fixtures/organization-custom-role.tf
new file mode 100644
index 000000000..f2beb11ab
--- /dev/null
+++ b/tests/fixtures/organization-custom-role.tf
@@ -0,0 +1,20 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+resource "google_organization_iam_custom_role" "custom_role" {
+ role_id = "fixtureRole${replace(var.prefix, "/[^a-zA-Z0-9_\\.]/", "")}"
+ org_id = trimprefix(var.organization_id, "organizations/")
+ title = "fixtureRole"
+ permissions = ["compute.instances.list"]
+}
diff --git a/tests/fixtures/shared-vpc.tf b/tests/fixtures/shared-vpc.tf
index 2fc18972d..ae995b372 100644
--- a/tests/fixtures/shared-vpc.tf
+++ b/tests/fixtures/shared-vpc.tf
@@ -56,7 +56,7 @@ module "project-service" {
shared_vpc_service_config = {
host_project = module.project-host.project_id
# reuse the list of services from the module's outputs
- service_iam_grants = module.project-service.services
+ service_iam_grants = [for service in module.project-service.services : "$service_agents:${service}"]
}
}
diff --git a/tests/modules/cloud_run_v2/examples/service-vpc-access-connector-create-sharedvpc.yaml b/tests/modules/cloud_run_v2/examples/service-vpc-access-connector-create-sharedvpc.yaml
index 480c4433a..16efec979 100644
--- a/tests/modules/cloud_run_v2/examples/service-vpc-access-connector-create-sharedvpc.yaml
+++ b/tests/modules/cloud_run_v2/examples/service-vpc-access-connector-create-sharedvpc.yaml
@@ -39,11 +39,20 @@ values:
subnet:
- name: fixture-subnet-28
project_id: test-host
+ # those IAM grants from shared-vpc.tf fixture are necessary for successful connector deployment
+ module.project-service.google_project_iam_member.shared_vpc_host_robots["roles/compute.networkUser:cloudservices"]:
+ condition: []
+ project: test-host
+ role: roles/compute.networkUser
+ module.project-service.google_project_iam_member.shared_vpc_host_robots["roles/compute.networkUser:vpcaccess"]:
+ condition: []
+ project: test-host
+ role: roles/compute.networkUser
counts:
google_cloud_run_v2_service: 1
google_vpc_access_connector: 1
modules: 4
- resources: 55
+ resources: 59
outputs: {}
diff --git a/tests/modules/compute_vm/context-template-regional.tfvars b/tests/modules/compute_vm/context-template-regional.tfvars
new file mode 100644
index 000000000..fc11cce4f
--- /dev/null
+++ b/tests/modules/compute_vm/context-template-regional.tfvars
@@ -0,0 +1,62 @@
+attached_disks = [{
+ name = "data-0"
+ size = 10
+ }
+]
+context = {
+ addresses = {
+ ext-test-0 = "35.10.10.10"
+ int-test-0 = "10.0.0.10"
+ }
+ custom_roles = {
+ myrole_one = "organizations/366118655033/roles/myRoleOne"
+ }
+ iam_principals = {
+ mygroup = "group:test-group@example.com"
+ }
+ kms_keys = {
+ test = "projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute"
+ }
+ locations = {
+ ew8a = "europe-west8-a"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ subnets = {
+ test = "projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce"
+ }
+ tag_values = {
+ "test/one" = "tagValues/1234567890"
+ }
+}
+create_template = {
+ regional = true
+}
+encryption = {
+ encrypt_boot = true
+ kms_key_self_link = "$kms_keys:test"
+}
+iam = {
+ "$custom_roles:myrole_one" = [
+ "$iam_principals:mygroup"
+ ]
+}
+name = "test"
+network_interfaces = [{
+ network = "$networks:test"
+ subnetwork = "$subnets:test"
+ nat = true
+ addresses = {
+ external = "$addresses:ext-test-0"
+ internal = "$addresses:int-test-0"
+ }
+}]
+project_id = "$project_ids:test"
+tag_bindings = {
+ foo = "$tag_values:test/one"
+}
+zone = "$locations:ew8a"
diff --git a/tests/modules/compute_vm/context-template-regional.yaml b/tests/modules/compute_vm/context-template-regional.yaml
new file mode 100644
index 000000000..073ca8c5c
--- /dev/null
+++ b/tests/modules/compute_vm/context-template-regional.yaml
@@ -0,0 +1,122 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ google_compute_instance_iam_binding.default["$custom_roles:myrole_one"]:
+ condition: []
+ instance_name: test
+ members:
+ - group:test-group@example.com
+ project: foo-test-0
+ role: organizations/366118655033/roles/myRoleOne
+ zone: europe-west8-a
+ google_compute_region_instance_template.default[0]:
+ advanced_machine_features: []
+ can_ip_forward: false
+ description: Managed by the compute-vm Terraform module.
+ disk:
+ - auto_delete: true
+ boot: true
+ disk_encryption_key:
+ - kms_key_self_link: projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute
+ kms_key_service_account: null
+ disk_name: null
+ disk_size_gb: 10
+ disk_type: pd-balanced
+ guest_os_features: null
+ labels: null
+ resource_manager_tags: null
+ resource_policies: null
+ source: null
+ source_image: projects/debian-cloud/global/images/family/debian-11
+ source_image_encryption_key: []
+ source_snapshot: null
+ source_snapshot_encryption_key: []
+ - auto_delete: true
+ device_name: data-0
+ disk_encryption_key:
+ - kms_key_self_link: projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute
+ kms_key_service_account: null
+ disk_name: data-0
+ disk_size_gb: 10
+ disk_type: pd-balanced
+ guest_os_features: null
+ labels: null
+ mode: READ_WRITE
+ resource_manager_tags: null
+ resource_policies: null
+ source: null
+ source_image_encryption_key: []
+ source_snapshot: null
+ source_snapshot_encryption_key: []
+ type: PERSISTENT
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ enable_display: null
+ guest_accelerator: []
+ instance_description: null
+ key_revocation_action_type: null
+ labels: null
+ machine_type: f1-micro
+ metadata: null
+ metadata_startup_script: null
+ min_cpu_platform: null
+ name_prefix: test-
+ network_interface:
+ - access_config:
+ - nat_ip: 35.10.10.10
+ alias_ip_range: []
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ network_ip: 10.0.0.10
+ nic_type: null
+ queue_count: null
+ subnetwork: projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce
+ network_performance_config: []
+ partner_metadata: null
+ project: foo-test-0
+ region: europe-west8
+ reservation_affinity: []
+ resource_manager_tags: null
+ resource_policies: null
+ scheduling:
+ - automatic_restart: true
+ availability_domain: null
+ graceful_shutdown: []
+ host_error_timeout_seconds: null
+ instance_termination_action: null
+ local_ssd_recovery_timeout: []
+ maintenance_interval: null
+ max_run_duration: []
+ min_node_cpus: null
+ node_affinities: []
+ on_host_maintenance: MIGRATE
+ on_instance_stop_action: []
+ preemptible: false
+ provisioning_model: STANDARD
+ termination_time: null
+ service_account:
+ - scopes:
+ - https://www.googleapis.com/auth/devstorage.read_only
+ - https://www.googleapis.com/auth/logging.write
+ - https://www.googleapis.com/auth/monitoring.write
+ shielded_instance_config: []
+ tags: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+counts:
+ google_compute_instance_iam_binding: 1
+ google_compute_region_instance_template: 1
+ modules: 0
+ resources: 2
diff --git a/tests/modules/compute_vm/context-template.tfvars b/tests/modules/compute_vm/context-template.tfvars
new file mode 100644
index 000000000..3ee07484a
--- /dev/null
+++ b/tests/modules/compute_vm/context-template.tfvars
@@ -0,0 +1,60 @@
+attached_disks = [{
+ name = "data-0"
+ size = 10
+ }
+]
+context = {
+ addresses = {
+ ext-test-0 = "35.10.10.10"
+ int-test-0 = "10.0.0.10"
+ }
+ custom_roles = {
+ myrole_one = "organizations/366118655033/roles/myRoleOne"
+ }
+ iam_principals = {
+ mygroup = "group:test-group@example.com"
+ }
+ kms_keys = {
+ test = "projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute"
+ }
+ locations = {
+ ew8a = "europe-west8-a"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ subnets = {
+ test = "projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce"
+ }
+ tag_values = {
+ "test/one" = "tagValues/1234567890"
+ }
+}
+create_template = {}
+encryption = {
+ encrypt_boot = true
+ kms_key_self_link = "$kms_keys:test"
+}
+iam = {
+ "$custom_roles:myrole_one" = [
+ "$iam_principals:mygroup"
+ ]
+}
+name = "test"
+network_interfaces = [{
+ network = "$networks:test"
+ subnetwork = "$subnets:test"
+ nat = true
+ addresses = {
+ external = "$addresses:ext-test-0"
+ internal = "$addresses:int-test-0"
+ }
+}]
+project_id = "$project_ids:test"
+tag_bindings = {
+ foo = "$tag_values:test/one"
+}
+zone = "$locations:ew8a"
diff --git a/tests/modules/compute_vm/context-template.yaml b/tests/modules/compute_vm/context-template.yaml
new file mode 100644
index 000000000..edd9a7003
--- /dev/null
+++ b/tests/modules/compute_vm/context-template.yaml
@@ -0,0 +1,123 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ google_compute_instance_iam_binding.default["$custom_roles:myrole_one"]:
+ condition: []
+ instance_name: test
+ members:
+ - group:test-group@example.com
+ project: foo-test-0
+ role: organizations/366118655033/roles/myRoleOne
+ zone: europe-west8-a
+ google_compute_instance_template.default[0]:
+ advanced_machine_features: []
+ can_ip_forward: false
+ description: Managed by the compute-vm Terraform module.
+ disk:
+ - auto_delete: true
+ boot: true
+ disk_encryption_key:
+ - kms_key_self_link: projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute
+ kms_key_service_account: null
+ disk_name: null
+ disk_size_gb: 10
+ disk_type: pd-balanced
+ guest_os_features: null
+ labels: null
+ resource_manager_tags: null
+ resource_policies: null
+ source: null
+ source_image: projects/debian-cloud/global/images/family/debian-11
+ source_image_encryption_key: []
+ source_snapshot: null
+ source_snapshot_encryption_key: []
+ - auto_delete: true
+ device_name: data-0
+ disk_encryption_key:
+ - kms_key_self_link: projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute
+ kms_key_service_account: null
+ disk_name: data-0
+ disk_size_gb: 10
+ disk_type: pd-balanced
+ guest_os_features: null
+ labels: null
+ mode: READ_WRITE
+ resource_manager_tags: null
+ resource_policies: null
+ source: null
+ source_image_encryption_key: []
+ source_snapshot: null
+ source_snapshot_encryption_key: []
+ type: PERSISTENT
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ enable_display: null
+ guest_accelerator: []
+ instance_description: null
+ key_revocation_action_type: null
+ labels: null
+ machine_type: f1-micro
+ metadata: null
+ metadata_startup_script: null
+ min_cpu_platform: null
+ name_prefix: test-
+ network_interface:
+ - access_config:
+ - nat_ip: 35.10.10.10
+ alias_ip_range: []
+ ipv6_access_config: []
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ network_ip: 10.0.0.10
+ nic_type: null
+ queue_count: null
+ subnetwork: projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce
+ network_performance_config: []
+ partner_metadata: null
+ project: foo-test-0
+ region: europe-west8
+ reservation_affinity: []
+ resource_manager_tags: null
+ resource_policies: null
+ scheduling:
+ - automatic_restart: true
+ availability_domain: null
+ graceful_shutdown: []
+ host_error_timeout_seconds: null
+ instance_termination_action: null
+ local_ssd_recovery_timeout: []
+ maintenance_interval: null
+ max_run_duration: []
+ min_node_cpus: null
+ node_affinities: []
+ on_host_maintenance: MIGRATE
+ on_instance_stop_action: []
+ preemptible: false
+ provisioning_model: STANDARD
+ termination_time: null
+ service_account:
+ - scopes:
+ - https://www.googleapis.com/auth/devstorage.read_only
+ - https://www.googleapis.com/auth/logging.write
+ - https://www.googleapis.com/auth/monitoring.write
+ shielded_instance_config: []
+ tags: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+counts:
+ google_compute_instance_iam_binding: 1
+ google_compute_instance_template: 1
+ modules: 0
+ resources: 2
diff --git a/tests/modules/compute_vm/context-vm.tfvars b/tests/modules/compute_vm/context-vm.tfvars
new file mode 100644
index 000000000..91295c4d8
--- /dev/null
+++ b/tests/modules/compute_vm/context-vm.tfvars
@@ -0,0 +1,59 @@
+attached_disks = [{
+ name = "data-0"
+ size = 10
+ }
+]
+context = {
+ addresses = {
+ ext-test-0 = "35.10.10.10"
+ int-test-0 = "10.0.0.10"
+ }
+ custom_roles = {
+ myrole_one = "organizations/366118655033/roles/myRoleOne"
+ }
+ iam_principals = {
+ mygroup = "group:test-group@example.com"
+ }
+ kms_keys = {
+ test = "projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute"
+ }
+ locations = {
+ ew8a = "europe-west8-a"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ subnets = {
+ test = "projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce"
+ }
+ tag_values = {
+ "test/one" = "tagValues/1234567890"
+ }
+}
+encryption = {
+ encrypt_boot = true
+ kms_key_self_link = "$kms_keys:test"
+}
+iam = {
+ "$custom_roles:myrole_one" = [
+ "$iam_principals:mygroup"
+ ]
+}
+name = "test"
+network_interfaces = [{
+ network = "$networks:test"
+ subnetwork = "$subnets:test"
+ nat = true
+ addresses = {
+ external = "$addresses:ext-test-0"
+ internal = "$addresses:int-test-0"
+ }
+}]
+project_id = "$project_ids:test"
+tag_bindings = {
+ foo = "$tag_values:test/one"
+}
+zone = "$locations:ew8a"
diff --git a/tests/modules/compute_vm/context-vm.yaml b/tests/modules/compute_vm/context-vm.yaml
new file mode 100644
index 000000000..252e886cf
--- /dev/null
+++ b/tests/modules/compute_vm/context-vm.yaml
@@ -0,0 +1,164 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ google_compute_disk.disks["data-0"]:
+ architecture: null
+ async_primary_disk: []
+ create_snapshot_before_destroy: false
+ create_snapshot_before_destroy_prefix: null
+ description: null
+ disk_encryption_key:
+ - kms_key_self_link: projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute
+ kms_key_service_account: null
+ raw_key: null
+ rsa_encrypted_key: null
+ effective_labels:
+ disk_name: data-0
+ disk_type: pd-balanced
+ goog-terraform-provisioned: 'true'
+ image: null
+ labels:
+ disk_name: data-0
+ disk_type: pd-balanced
+ name: test-data-0
+ params: []
+ project: foo-test-0
+ size: 10
+ snapshot: null
+ source_disk: null
+ source_image_encryption_key: []
+ source_instant_snapshot: null
+ source_snapshot_encryption_key: []
+ source_storage_object: null
+ storage_pool: null
+ terraform_labels:
+ disk_name: data-0
+ disk_type: pd-balanced
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ type: pd-balanced
+ zone: europe-west8-a
+ google_compute_instance.default[0]:
+ advanced_machine_features: []
+ allow_stopping_for_update: true
+ attached_disk:
+ - device_name: data-0
+ disk_encryption_key_raw: null
+ disk_encryption_key_rsa: null
+ disk_encryption_service_account: null
+ force_attach: null
+ mode: READ_WRITE
+ source: test-data-0
+ boot_disk:
+ - auto_delete: true
+ disk_encryption_key_raw: null
+ disk_encryption_key_rsa: null
+ disk_encryption_service_account: null
+ force_attach: null
+ initialize_params:
+ - enable_confidential_compute: null
+ image: projects/debian-cloud/global/images/family/debian-11
+ resource_manager_tags: null
+ size: 10
+ source_image_encryption_key: []
+ source_snapshot_encryption_key: []
+ storage_pool: null
+ type: pd-balanced
+ interface: null
+ kms_key_self_link: projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute
+ mode: READ_WRITE
+ can_ip_forward: false
+ deletion_protection: false
+ description: Managed by the compute-vm Terraform module.
+ desired_status: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ enable_display: false
+ hostname: null
+ instance_encryption_key: []
+ key_revocation_action_type: null
+ labels: null
+ machine_type: f1-micro
+ metadata: null
+ metadata_startup_script: null
+ name: test
+ network_interface:
+ - access_config:
+ - nat_ip: 35.10.10.10
+ public_ptr_domain_name: null
+ alias_ip_range: []
+ ipv6_access_config: []
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ network_ip: 10.0.0.10
+ nic_type: null
+ queue_count: null
+ security_policy: null
+ subnetwork: projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce
+ network_performance_config: []
+ params: []
+ partner_metadata: null
+ project: foo-test-0
+ resource_policies: null
+ scheduling:
+ - automatic_restart: true
+ availability_domain: null
+ graceful_shutdown: []
+ host_error_timeout_seconds: null
+ instance_termination_action: null
+ local_ssd_recovery_timeout: []
+ maintenance_interval: null
+ max_run_duration: []
+ min_node_cpus: null
+ node_affinities: []
+ on_host_maintenance: MIGRATE
+ on_instance_stop_action: []
+ preemptible: false
+ provisioning_model: STANDARD
+ termination_time: null
+ scratch_disk: []
+ service_account:
+ - scopes:
+ - https://www.googleapis.com/auth/devstorage.read_only
+ - https://www.googleapis.com/auth/logging.write
+ - https://www.googleapis.com/auth/monitoring.write
+ shielded_instance_config: []
+ tags: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ zone: europe-west8-a
+ google_compute_instance_iam_binding.default["$custom_roles:myrole_one"]:
+ condition: []
+ instance_name: test
+ members:
+ - group:test-group@example.com
+ project: foo-test-0
+ role: organizations/366118655033/roles/myRoleOne
+ zone: europe-west8-a
+ google_tags_location_tag_binding.disks["data-0/foo"]:
+ location: europe-west8-a
+ tag_value: tagValues/1234567890
+ timeouts: null
+ google_tags_location_tag_binding.instance["foo"]:
+ location: europe-west8-a
+ tag_value: tagValues/1234567890
+ timeouts: null
+counts:
+ google_compute_disk: 1
+ google_compute_instance: 1
+ google_compute_instance_iam_binding: 1
+ google_tags_location_tag_binding: 2
+ modules: 0
+ resources: 5
diff --git a/tests/modules/compute_vm/tftest.yaml b/tests/modules/compute_vm/tftest.yaml
new file mode 100644
index 000000000..61f5f1dd4
--- /dev/null
+++ b/tests/modules/compute_vm/tftest.yaml
@@ -0,0 +1,19 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module: modules/compute-vm
+tests:
+ context-template:
+ context-template-regional:
+ context-vm:
diff --git a/tests/modules/dns/context.tfvars b/tests/modules/dns/context.tfvars
new file mode 100644
index 000000000..26a3575b2
--- /dev/null
+++ b/tests/modules/dns/context.tfvars
@@ -0,0 +1,30 @@
+context = {
+ custom_roles = {
+ myrole = "organizations/366118655033/roles/myRoleOne"
+ }
+ iam_principals = {
+ mygroup = "group:test-group@example.com"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+}
+project_id = "$project_ids:test"
+name = "test-example"
+zone_config = {
+ domain = "test.example."
+ private = {
+ client_networks = ["$networks:test"]
+ }
+}
+recordsets = {
+ "A localhost" = { records = ["127.0.0.1"] }
+ "A myhost" = { ttl = 600, records = ["10.0.0.120"] }
+}
+iam = {
+ "$custom_roles:myrole" = ["$iam_principals:mygroup"]
+}
+
diff --git a/tests/modules/dns/context.yaml b/tests/modules/dns/context.yaml
new file mode 100644
index 000000000..6c986b75b
--- /dev/null
+++ b/tests/modules/dns/context.yaml
@@ -0,0 +1,69 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ google_dns_managed_zone.dns_managed_zone[0]:
+ cloud_logging_config:
+ - enable_logging: false
+ description: Terraform managed.
+ dns_name: test.example.
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ force_destroy: false
+ forwarding_config: []
+ labels: null
+ name: test-example
+ peering_config: []
+ private_visibility_config:
+ - gke_clusters: []
+ networks:
+ - network_url: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ project: foo-test-0
+ reverse_lookup: false
+ service_directory_config: []
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ visibility: private
+ google_dns_managed_zone_iam_binding.iam_bindings["$custom_roles:myrole"]:
+ condition: []
+ members:
+ - group:test-group@example.com
+ project: foo-test-0
+ role: organizations/366118655033/roles/myRoleOne
+ google_dns_record_set.dns_record_set["A localhost"]:
+ managed_zone: test-example
+ name: localhost.test.example.
+ project: foo-test-0
+ routing_policy: []
+ rrdatas:
+ - 127.0.0.1
+ ttl: 300
+ type: A
+ google_dns_record_set.dns_record_set["A myhost"]:
+ managed_zone: test-example
+ name: myhost.test.example.
+ project: foo-test-0
+ routing_policy: []
+ rrdatas:
+ - 10.0.0.120
+ ttl: 600
+ type: A
+
+counts:
+ google_dns_managed_zone: 1
+ google_dns_managed_zone_iam_binding: 1
+ google_dns_record_set: 2
+ modules: 0
+ resources: 4
diff --git a/tests/modules/dns/tftest.yaml b/tests/modules/dns/tftest.yaml
new file mode 100644
index 000000000..afeb43bda
--- /dev/null
+++ b/tests/modules/dns/tftest.yaml
@@ -0,0 +1,17 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module: modules/dns
+tests:
+ context:
diff --git a/tests/modules/dns_response_policy/context.tfvars b/tests/modules/dns_response_policy/context.tfvars
new file mode 100644
index 000000000..d9440a11f
--- /dev/null
+++ b/tests/modules/dns_response_policy/context.tfvars
@@ -0,0 +1,23 @@
+context = {
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+}
+project_id = "$project_ids:test"
+name = "googleapis"
+networks = {
+ landing = "$networks:test"
+}
+rules = {
+ pubsub = {
+ dns_name = "pubsub.googleapis.com."
+ local_data = {
+ A = {
+ rrdatas = ["199.36.153.4", "199.36.153.5"]
+ }
+ }
+ }
+}
diff --git a/tests/modules/dns_response_policy/context.yaml b/tests/modules/dns_response_policy/context.yaml
new file mode 100644
index 000000000..44f708cc9
--- /dev/null
+++ b/tests/modules/dns_response_policy/context.yaml
@@ -0,0 +1,44 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ google_dns_response_policy.default[0]:
+ description: Terraform managed.
+ gke_clusters: []
+ networks:
+ - network_url: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ project: foo-test-0
+ response_policy_name: googleapis
+ timeouts: null
+ google_dns_response_policy_rule.default["pubsub"]:
+ behavior: null
+ dns_name: pubsub.googleapis.com.
+ local_data:
+ - local_datas:
+ - name: pubsub.googleapis.com.
+ rrdatas:
+ - 199.36.153.4
+ - 199.36.153.5
+ ttl: null
+ type: A
+ project: foo-test-0
+ response_policy: googleapis
+ rule_name: pubsub
+ timeouts: null
+
+counts:
+ google_dns_response_policy: 1
+ google_dns_response_policy_rule: 1
+ modules: 0
+ resources: 2
diff --git a/tests/modules/dns_response_policy/tftest.yaml b/tests/modules/dns_response_policy/tftest.yaml
new file mode 100644
index 000000000..185bfb83f
--- /dev/null
+++ b/tests/modules/dns_response_policy/tftest.yaml
@@ -0,0 +1,17 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module: modules/dns-response-policy
+tests:
+ context:
diff --git a/tests/modules/net_cloudnat/context.tfvars b/tests/modules/net_cloudnat/context.tfvars
new file mode 100644
index 000000000..78b8af838
--- /dev/null
+++ b/tests/modules/net_cloudnat/context.tfvars
@@ -0,0 +1,28 @@
+context = {
+ addresses = {
+ test = "35.10.10.10"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ subnets = {
+ test = "projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce"
+ }
+}
+addresses = ["$addresses:test"]
+config_source_subnetworks = {
+ all = false
+ subnetworks = [{
+ self_link = "$subnets:test"
+ }]
+}
+name = "test"
+project_id = "$project_ids:test"
+region = "$locations:ew8"
+router_network = "$networks:test"
diff --git a/tests/modules/net_cloudnat/context.yaml b/tests/modules/net_cloudnat/context.yaml
new file mode 100644
index 000000000..3faec0cad
--- /dev/null
+++ b/tests/modules/net_cloudnat/context.yaml
@@ -0,0 +1,61 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ google_compute_router.router[0]:
+ bgp: []
+ description: null
+ encrypted_interconnect_router: null
+ md5_authentication_keys: []
+ name: test-nat
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ project: foo-test-0
+ region: europe-west8
+ timeouts: null
+ google_compute_router_nat.nat:
+ enable_dynamic_port_allocation: false
+ enable_endpoint_independent_mapping: true
+ icmp_idle_timeout_sec: 30
+ initial_nat_ips: null
+ log_config:
+ - enable: false
+ filter: ALL
+ max_ports_per_vm: 65536
+ name: test
+ nat64_subnetwork: []
+ nat_ip_allocate_option: MANUAL_ONLY
+ nat_ips:
+ - 35.10.10.10
+ project: foo-test-0
+ region: europe-west8
+ router: test-nat
+ rules: []
+ source_subnetwork_ip_ranges_to_nat: LIST_OF_SUBNETWORKS
+ source_subnetwork_ip_ranges_to_nat64: null
+ subnetwork:
+ - name: projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce
+ secondary_ip_range_names: []
+ source_ip_ranges_to_nat:
+ - ALL_IP_RANGES
+ tcp_established_idle_timeout_sec: 1200
+ tcp_time_wait_timeout_sec: 120
+ tcp_transitory_idle_timeout_sec: 30
+ timeouts: null
+ type: PUBLIC
+ udp_idle_timeout_sec: 30
+counts:
+ google_compute_router: 1
+ google_compute_router_nat: 1
+ modules: 0
+ resources: 2
diff --git a/tests/modules/net_cloudnat/tftest.yaml b/tests/modules/net_cloudnat/tftest.yaml
new file mode 100644
index 000000000..8f75c0ecf
--- /dev/null
+++ b/tests/modules/net_cloudnat/tftest.yaml
@@ -0,0 +1,17 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module: modules/net-cloudnat
+tests:
+ context:
diff --git a/tests/modules/net_firewall_policy/context-g.tfvars b/tests/modules/net_firewall_policy/context-g.tfvars
new file mode 100644
index 000000000..7acda3c67
--- /dev/null
+++ b/tests/modules/net_firewall_policy/context-g.tfvars
@@ -0,0 +1,52 @@
+context = {
+ cidr_ranges = {
+ rfc1918-10 = "10.0.0.0/8"
+ }
+ folder_ids = {
+ test = "folders/1234567890"
+ }
+ iam_principals = {
+ test = "serviceAccount:test@test-project.iam.gserviceaccount.com"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ tag_values = {
+ "test" = "tagValues/1234567890"
+ }
+}
+name = "test-1"
+parent_id = "$project_ids:test"
+region = "global"
+attachments = {
+ test = "$networks:test"
+}
+egress_rules = {
+ smtp = {
+ priority = 900
+ target_service_accounts = ["$iam_principals:test"]
+ match = {
+ destination_ranges = ["$cidr_ranges:rfc1918-10"]
+ layer4_configs = [{ protocol = "tcp", ports = ["25"] }]
+ source_tags = ["$tag_values:test"]
+ }
+ }
+}
+ingress_rules = {
+ icmp = {
+ priority = 1000
+ enable_logging = true
+ target_resources = ["$networks:test"]
+ target_tags = ["$tag_values:test"]
+ match = {
+ source_ranges = ["$cidr_ranges:rfc1918-10"]
+ layer4_configs = [{ protocol = "icmp" }]
+ }
+ }
+}
diff --git a/tests/modules/net_firewall_policy/context-g.yaml b/tests/modules/net_firewall_policy/context-g.yaml
new file mode 100644
index 000000000..2a7569356
--- /dev/null
+++ b/tests/modules/net_firewall_policy/context-g.yaml
@@ -0,0 +1,99 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ google_compute_network_firewall_policy.net-global[0]:
+ description: null
+ name: test-1
+ project: foo-test-0
+ timeouts: null
+ google_compute_network_firewall_policy_association.net-global["test"]:
+ attachment_target: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ firewall_policy: test-1
+ name: test-1-test
+ project: foo-test-0
+ timeouts: null
+ google_compute_network_firewall_policy_rule.net-global["egress/smtp"]:
+ action: deny
+ description: null
+ direction: EGRESS
+ disabled: false
+ enable_logging: null
+ firewall_policy: test-1
+ match:
+ - dest_address_groups: null
+ dest_fqdns: null
+ dest_ip_ranges:
+ - 10.0.0.0/8
+ dest_region_codes: null
+ dest_threat_intelligences: null
+ layer4_configs:
+ - ip_protocol: tcp
+ ports:
+ - '25'
+ src_address_groups: null
+ src_fqdns: null
+ src_ip_ranges: null
+ src_region_codes: null
+ src_secure_tags:
+ - name: tagValues/1234567890
+ src_threat_intelligences: null
+ priority: 900
+ project: foo-test-0
+ rule_name: smtp
+ security_profile_group: null
+ target_secure_tags: []
+ target_service_accounts:
+ - serviceAccount:test@test-project.iam.gserviceaccount.com
+ timeouts: null
+ tls_inspect: null
+ google_compute_network_firewall_policy_rule.net-global["ingress/icmp"]:
+ action: allow
+ description: null
+ direction: INGRESS
+ disabled: false
+ enable_logging: true
+ firewall_policy: test-1
+ match:
+ - dest_address_groups: null
+ dest_fqdns: null
+ dest_ip_ranges: null
+ dest_region_codes: null
+ dest_threat_intelligences: null
+ layer4_configs:
+ - ip_protocol: icmp
+ ports: null
+ src_address_groups: null
+ src_fqdns: null
+ src_ip_ranges:
+ - 10.0.0.0/8
+ src_region_codes: null
+ src_secure_tags: []
+ src_threat_intelligences: null
+ priority: 1000
+ project: foo-test-0
+ rule_name: icmp
+ security_profile_group: null
+ target_secure_tags:
+ - name: tagValues/1234567890
+ target_service_accounts: null
+ timeouts: null
+ tls_inspect: null
+
+counts:
+ google_compute_network_firewall_policy: 1
+ google_compute_network_firewall_policy_association: 1
+ google_compute_network_firewall_policy_rule: 2
+ modules: 0
+ resources: 4
diff --git a/tests/modules/net_firewall_policy/context-h.tfvars b/tests/modules/net_firewall_policy/context-h.tfvars
new file mode 100644
index 000000000..80eb44543
--- /dev/null
+++ b/tests/modules/net_firewall_policy/context-h.tfvars
@@ -0,0 +1,49 @@
+context = {
+ cidr_ranges = {
+ rfc1918-10 = "10.0.0.0/8"
+ }
+ folder_ids = {
+ test = "folders/1234567890"
+ }
+ iam_principals = {
+ test = "serviceAccount:test@test-project.iam.gserviceaccount.com"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ tag_values = {
+ "test/one" = "tagValues/1234567890"
+ }
+}
+name = "test-1"
+parent_id = "$folder_ids:test"
+attachments = {
+ test = "$folder_ids:test"
+}
+egress_rules = {
+ smtp = {
+ priority = 900
+ match = {
+ destination_ranges = ["$cidr_ranges:rfc1918-10"]
+ layer4_configs = [{ protocol = "tcp", ports = ["25"] }]
+ }
+ }
+}
+ingress_rules = {
+ icmp = {
+ priority = 1000
+ enable_logging = true
+ target_resources = ["$networks:test"]
+ target_service_accounts = ["$iam_principals:test"]
+ match = {
+ source_ranges = ["$cidr_ranges:rfc1918-10"]
+ layer4_configs = [{ protocol = "icmp" }]
+ }
+ }
+}
diff --git a/tests/modules/net_firewall_policy/context-h.yaml b/tests/modules/net_firewall_policy/context-h.yaml
new file mode 100644
index 000000000..ebcbcc539
--- /dev/null
+++ b/tests/modules/net_firewall_policy/context-h.yaml
@@ -0,0 +1,92 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ google_compute_firewall_policy.hierarchical[0]:
+ description: null
+ parent: folders/1234567890
+ short_name: test-1
+ timeouts: null
+ google_compute_firewall_policy_association.hierarchical["test"]:
+ attachment_target: folders/1234567890
+ name: test-1-test
+ timeouts: null
+ google_compute_firewall_policy_rule.hierarchical["egress/smtp"]:
+ action: deny
+ description: null
+ direction: EGRESS
+ disabled: false
+ enable_logging: null
+ match:
+ - dest_address_groups: null
+ dest_fqdns: null
+ dest_ip_ranges:
+ - 10.0.0.0/8
+ dest_region_codes: null
+ dest_threat_intelligences: null
+ layer4_configs:
+ - ip_protocol: tcp
+ ports:
+ - '25'
+ src_address_groups: null
+ src_fqdns: null
+ src_ip_ranges: null
+ src_region_codes: null
+ src_secure_tags: []
+ src_threat_intelligences: null
+ priority: 900
+ security_profile_group: null
+ target_resources: null
+ target_secure_tags: []
+ target_service_accounts: null
+ timeouts: null
+ tls_inspect: null
+ google_compute_firewall_policy_rule.hierarchical["ingress/icmp"]:
+ action: allow
+ description: null
+ direction: INGRESS
+ disabled: false
+ enable_logging: true
+ match:
+ - dest_address_groups: null
+ dest_fqdns: null
+ dest_ip_ranges: null
+ dest_region_codes: null
+ dest_threat_intelligences: null
+ layer4_configs:
+ - ip_protocol: icmp
+ ports: null
+ src_address_groups: null
+ src_fqdns: null
+ src_ip_ranges:
+ - 10.0.0.0/8
+ src_region_codes: null
+ src_secure_tags: []
+ src_threat_intelligences: null
+ priority: 1000
+ security_profile_group: null
+ target_resources:
+ - projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ target_secure_tags: []
+ target_service_accounts:
+ - serviceAccount:test@test-project.iam.gserviceaccount.com
+ timeouts: null
+ tls_inspect: null
+
+counts:
+ google_compute_firewall_policy: 1
+ google_compute_firewall_policy_association: 1
+ google_compute_firewall_policy_rule: 2
+ modules: 0
+ resources: 4
diff --git a/tests/modules/net_firewall_policy/context-r.tfvars b/tests/modules/net_firewall_policy/context-r.tfvars
new file mode 100644
index 000000000..9630d37be
--- /dev/null
+++ b/tests/modules/net_firewall_policy/context-r.tfvars
@@ -0,0 +1,52 @@
+context = {
+ cidr_ranges = {
+ rfc1918-10 = "10.0.0.0/8"
+ }
+ folder_ids = {
+ test = "folders/1234567890"
+ }
+ iam_principals = {
+ test = "serviceAccount:test@test-project.iam.gserviceaccount.com"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ tag_values = {
+ "test" = "tagValues/1234567890"
+ }
+}
+name = "test-1"
+parent_id = "$project_ids:test"
+region = "$locations:ew8"
+attachments = {
+ test = "$networks:test"
+}
+egress_rules = {
+ smtp = {
+ priority = 900
+ target_service_accounts = ["$iam_principals:test"]
+ match = {
+ destination_ranges = ["$cidr_ranges:rfc1918-10"]
+ layer4_configs = [{ protocol = "tcp", ports = ["25"] }]
+ source_tags = ["$tag_values:test"]
+ }
+ }
+}
+ingress_rules = {
+ icmp = {
+ priority = 1000
+ enable_logging = true
+ target_resources = ["$networks:test"]
+ target_tags = ["$tag_values:test"]
+ match = {
+ source_ranges = ["$cidr_ranges:rfc1918-10"]
+ layer4_configs = [{ protocol = "icmp" }]
+ }
+ }
+}
diff --git a/tests/modules/net_firewall_policy/context-r.yaml b/tests/modules/net_firewall_policy/context-r.yaml
new file mode 100644
index 000000000..d68a05777
--- /dev/null
+++ b/tests/modules/net_firewall_policy/context-r.yaml
@@ -0,0 +1,103 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ google_compute_region_network_firewall_policy.net-regional[0]:
+ description: null
+ name: test-1
+ project: foo-test-0
+ region: europe-west8
+ timeouts: null
+ google_compute_region_network_firewall_policy_association.net-regional["test"]:
+ attachment_target: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ firewall_policy: test-1
+ name: test-1-test
+ project: foo-test-0
+ region: europe-west8
+ timeouts: null
+ google_compute_region_network_firewall_policy_rule.net-regional["egress/smtp"]:
+ action: deny
+ description: null
+ direction: EGRESS
+ disabled: false
+ enable_logging: null
+ firewall_policy: test-1
+ match:
+ - dest_address_groups: null
+ dest_fqdns: null
+ dest_ip_ranges:
+ - 10.0.0.0/8
+ dest_region_codes: null
+ dest_threat_intelligences: null
+ layer4_configs:
+ - ip_protocol: tcp
+ ports:
+ - '25'
+ src_address_groups: null
+ src_fqdns: null
+ src_ip_ranges: null
+ src_region_codes: null
+ src_secure_tags:
+ - name: tagValues/1234567890
+ src_threat_intelligences: null
+ priority: 900
+ project: foo-test-0
+ region: europe-west8
+ rule_name: smtp
+ security_profile_group: null
+ target_secure_tags: []
+ target_service_accounts:
+ - serviceAccount:test@test-project.iam.gserviceaccount.com
+ timeouts: null
+ tls_inspect: null
+ google_compute_region_network_firewall_policy_rule.net-regional["ingress/icmp"]:
+ action: allow
+ description: null
+ direction: INGRESS
+ disabled: false
+ enable_logging: true
+ firewall_policy: test-1
+ match:
+ - dest_address_groups: null
+ dest_fqdns: null
+ dest_ip_ranges: null
+ dest_region_codes: null
+ dest_threat_intelligences: null
+ layer4_configs:
+ - ip_protocol: icmp
+ ports: null
+ src_address_groups: null
+ src_fqdns: null
+ src_ip_ranges:
+ - 10.0.0.0/8
+ src_region_codes: null
+ src_secure_tags: []
+ src_threat_intelligences: null
+ priority: 1000
+ project: foo-test-0
+ region: europe-west8
+ rule_name: icmp
+ security_profile_group: null
+ target_secure_tags:
+ - name: tagValues/1234567890
+ target_service_accounts: null
+ timeouts: null
+ tls_inspect: null
+
+counts:
+ google_compute_region_network_firewall_policy: 1
+ google_compute_region_network_firewall_policy_association: 1
+ google_compute_region_network_firewall_policy_rule: 2
+ modules: 0
+ resources: 4
diff --git a/tests/modules/net_firewall_policy/tftest.yaml b/tests/modules/net_firewall_policy/tftest.yaml
new file mode 100644
index 000000000..192932f44
--- /dev/null
+++ b/tests/modules/net_firewall_policy/tftest.yaml
@@ -0,0 +1,19 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module: modules/net-firewall-policy
+tests:
+ context-g:
+ context-h:
+ context-r:
diff --git a/tests/modules/net_lb_int/context.tfvars b/tests/modules/net_lb_int/context.tfvars
new file mode 100644
index 000000000..c2aff4baf
--- /dev/null
+++ b/tests/modules/net_lb_int/context.tfvars
@@ -0,0 +1,42 @@
+context = {
+ addresses = {
+ test = "10.0.0.10"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ subnets = {
+ test = "projects/foo-dev-net-spoke-0/regions/europe-west8/subnetworks/gce"
+ test-nat = "projects/foo-dev-net-spoke-0/regions/europe-west8/subnetworks/test-nat"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+}
+project_id = "$project_ids:test"
+region = "$locations:ew8"
+name = "test"
+vpc_config = {
+ network = "$networks:test"
+ subnetwork = "$subnets:test"
+}
+backends = [{
+ group = "foo"
+ failover = false
+}]
+forwarding_rules_config = {
+ "" = {
+ address = "$addresses:test"
+ }
+}
+service_attachments = {
+ "" = {
+ nat_subnets = ["$subnets:test-nat"]
+ }
+}
diff --git a/tests/modules/net_lb_int/context.yaml b/tests/modules/net_lb_int/context.yaml
new file mode 100644
index 000000000..ceebc8374
--- /dev/null
+++ b/tests/modules/net_lb_int/context.yaml
@@ -0,0 +1,126 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ google_compute_forwarding_rule.default[""]:
+ all_ports: true
+ allow_global_access: true
+ allow_psc_global_access: null
+ description: null
+ ip_address: 10.0.0.10
+ ip_collection: null
+ ip_protocol: TCP
+ is_mirroring_collector: null
+ labels: null
+ load_balancing_scheme: INTERNAL
+ name: test
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ no_automate_dns_zone: null
+ ports: null
+ project: foo-test-0
+ recreate_closed_psc: false
+ region: europe-west8
+ service_label: null
+ source_ip_ranges: null
+ subnetwork: projects/foo-dev-net-spoke-0/regions/europe-west8/subnetworks/gce
+ target: null
+ timeouts: null
+ google_compute_health_check.default[0]:
+ check_interval_sec: 5
+ description: Terraform managed.
+ grpc_health_check: []
+ grpc_tls_health_check: []
+ healthy_threshold: 2
+ http2_health_check: []
+ http_health_check: []
+ https_health_check: []
+ name: test
+ project: foo-test-0
+ source_regions: null
+ ssl_health_check: []
+ tcp_health_check:
+ - port: null
+ port_name: null
+ port_specification: USE_SERVING_PORT
+ proxy_header: NONE
+ request: null
+ response: null
+ timeout_sec: 5
+ timeouts: null
+ unhealthy_threshold: 2
+ google_compute_region_backend_service.default:
+ affinity_cookie_ttl_sec: null
+ backend:
+ - balancing_mode: CONNECTION
+ capacity_scaler: null
+ custom_metrics: []
+ description: Terraform managed.
+ failover: false
+ group: foo
+ max_connections: null
+ max_connections_per_endpoint: null
+ max_connections_per_instance: null
+ max_rate: null
+ max_rate_per_endpoint: null
+ max_rate_per_instance: null
+ max_utilization: null
+ circuit_breakers: []
+ connection_draining_timeout_sec: 300
+ connection_tracking_policy: []
+ consistent_hash: []
+ custom_metrics: []
+ description: Terraform managed.
+ dynamic_forwarding: []
+ enable_cdn: null
+ failover_policy: []
+ ha_policy: []
+ iap:
+ - enabled: false
+ oauth2_client_id: null
+ oauth2_client_secret: null
+ ip_address_selection_policy: null
+ load_balancing_scheme: INTERNAL
+ locality_lb_policy: null
+ name: test
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ outlier_detection: []
+ project: foo-test-0
+ protocol: UNSPECIFIED
+ region: europe-west8
+ security_policy: null
+ strong_session_affinity_cookie: []
+ subsetting: []
+ timeouts: null
+ google_compute_service_attachment.default[""]:
+ connection_preference: ACCEPT_MANUAL
+ consumer_accept_lists: []
+ consumer_reject_lists: null
+ description: Terraform managed.
+ domain_names: null
+ enable_proxy_protocol: false
+ name: test
+ nat_subnets:
+ - projects/foo-dev-net-spoke-0/regions/europe-west8/subnetworks/test-nat
+ project: foo-test-0
+ region: europe-west8
+ send_propagated_connection_limit_if_zero: false
+ timeouts: null
+
+counts:
+ google_compute_forwarding_rule: 1
+ google_compute_health_check: 1
+ google_compute_region_backend_service: 1
+ google_compute_service_attachment: 1
+ modules: 0
+ resources: 4
diff --git a/tests/modules/net_lb_int/tftest.yaml b/tests/modules/net_lb_int/tftest.yaml
index 5cc14ee22..0837391d7 100644
--- a/tests/modules/net_lb_int/tftest.yaml
+++ b/tests/modules/net_lb_int/tftest.yaml
@@ -15,5 +15,6 @@
module: modules/net-lb-int
tests:
+ context:
defaults:
forwarding-rule:
diff --git a/tests/modules/net_vpc/context.tfvars b/tests/modules/net_vpc/context.tfvars
new file mode 100644
index 000000000..c54ff5fef
--- /dev/null
+++ b/tests/modules/net_vpc/context.tfvars
@@ -0,0 +1,96 @@
+context = {
+ addresses = {
+ dns-external = "8.8.8.8"
+ dns-internal = "10.10.10.10"
+ test = "10.20.20.20"
+ }
+ cidr_ranges = {
+ rfc1918-10 = "10.0.0.0/8"
+ rfc1918-172 = "172.16.10.0/12"
+ rfc1918-192 = "192.168.0.0/16"
+ test = "8.8.8.8/32"
+ }
+ condition_vars = {
+ organization = {
+ id = 1234567890
+ }
+ }
+ custom_roles = {
+ myrole = "organizations/366118655033/roles/myRoleOne"
+ }
+ iam_principals = {
+ test = "serviceAccount:test@test-project.iam.gserviceaccount.com"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+}
+dns_policy = {
+ inbound = true
+ outbound = {
+ private_ns = ["$addresses:dns-internal"]
+ public_ns = ["$addresses:dns-external"]
+ }
+}
+internal_ranges = [
+ {
+ name = "pods-range"
+ usage = "FOR_VPC"
+ peering = "FOR_SELF"
+ ip_cidr_range = "$cidr_ranges:rfc1918-172"
+ }
+]
+project_id = "$project_ids:test"
+routes = {
+ next-hop = {
+ description = "Route to internal range."
+ dest_range = "$cidr_ranges:test"
+ next_hop_type = "ip"
+ next_hop = "$addresses:test"
+ }
+}
+subnets = [
+ {
+ name = "production"
+ region = "$locations:ew8"
+ reserved_internal_range = "pods-range"
+ iam = {
+ "$custom_roles:myrole" = [
+ "iam_principals:test"
+ ]
+ }
+ iam_bindings = {
+ myrole_two = {
+ role = "$custom_roles:myrole"
+ members = [
+ "$iam_principals:test"
+ ]
+ condition = {
+ title = "Test"
+ expression = "resource.matchTag('$${organization.id}/environment', 'development')"
+ }
+ }
+ }
+ iam_bindings_additive = {
+ myrole_two = {
+ role = "$custom_roles:myrole"
+ member = "$iam_principals:test"
+ }
+ }
+ secondary_ip_ranges = {
+ pods = {
+ reserved_internal_range = "pods-range"
+ }
+ # Mixed configuration: some ranges use internal ranges, others use CIDR
+ traditional = {
+ ip_cidr_range = "$cidr_ranges:rfc1918-192"
+ }
+ }
+ }
+]
diff --git a/tests/modules/net_vpc/context.yaml b/tests/modules/net_vpc/context.yaml
new file mode 100644
index 000000000..36beb5c4c
--- /dev/null
+++ b/tests/modules/net_vpc/context.yaml
@@ -0,0 +1,175 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ google_compute_network.network[0]:
+ auto_create_subnetworks: false
+ delete_default_routes_on_create: false
+ description: Terraform-managed.
+ enable_ula_internal_ipv6: null
+ name: test
+ network_firewall_policy_enforcement_order: AFTER_CLASSIC_FIREWALL
+ network_profile: null
+ params: []
+ project: foo-test-0
+ routing_mode: GLOBAL
+ timeouts: null
+ google_compute_route.gateway["directpath-googleapis"]:
+ description: Terraform-managed.
+ dest_range: 34.126.0.0/18
+ name: test-directpath-googleapis
+ network: test
+ next_hop_gateway: default-internet-gateway
+ next_hop_ilb: null
+ next_hop_instance: null
+ next_hop_vpn_tunnel: null
+ params: []
+ priority: 1000
+ project: foo-test-0
+ tags: null
+ timeouts: null
+ google_compute_route.gateway["private-googleapis"]:
+ description: Terraform-managed.
+ dest_range: 199.36.153.8/30
+ name: test-private-googleapis
+ network: test
+ next_hop_gateway: default-internet-gateway
+ next_hop_ilb: null
+ next_hop_instance: null
+ next_hop_vpn_tunnel: null
+ params: []
+ priority: 1000
+ project: foo-test-0
+ tags: null
+ timeouts: null
+ google_compute_route.gateway["restricted-googleapis"]:
+ description: Terraform-managed.
+ dest_range: 199.36.153.4/30
+ name: test-restricted-googleapis
+ network: test
+ next_hop_gateway: default-internet-gateway
+ next_hop_ilb: null
+ next_hop_instance: null
+ next_hop_vpn_tunnel: null
+ params: []
+ priority: 1000
+ project: foo-test-0
+ tags: null
+ timeouts: null
+ google_compute_route.ip["next-hop"]:
+ description: Route to internal range.
+ dest_range: 8.8.8.8/32
+ name: test-next-hop
+ network: test
+ next_hop_gateway: null
+ next_hop_ilb: null
+ next_hop_instance: null
+ next_hop_ip: 10.20.20.20
+ next_hop_vpn_tunnel: null
+ params: []
+ priority: 1000
+ project: foo-test-0
+ tags: null
+ timeouts: null
+ google_compute_subnetwork.subnetwork["europe-west8/production"]:
+ description: Terraform-managed.
+ ip_collection: null
+ ipv6_access_type: null
+ log_config: []
+ name: production
+ network: test
+ params: []
+ private_ip_google_access: true
+ project: foo-test-0
+ region: europe-west8
+ role: null
+ secondary_ip_range:
+ - range_name: pods
+ - ip_cidr_range: 192.168.0.0/16
+ range_name: traditional
+ reserved_internal_range: null
+ send_secondary_ip_range_if_empty: true
+ timeouts: null
+ google_compute_subnetwork_iam_binding.authoritative["europe-west8/production.organizations/366118655033/roles/myRoleOne"]:
+ condition: []
+ members:
+ - iam_principals:test
+ project: foo-test-0
+ region: europe-west8
+ role: organizations/366118655033/roles/myRoleOne
+ subnetwork: production
+ google_compute_subnetwork_iam_binding.bindings["myrole_two"]:
+ condition:
+ - description: null
+ expression: resource.matchTag('1234567890/environment', 'development')
+ title: Test
+ members:
+ - serviceAccount:test@test-project.iam.gserviceaccount.com
+ project: foo-test-0
+ region: europe-west8
+ role: organizations/366118655033/roles/myRoleOne
+ subnetwork: production
+ google_compute_subnetwork_iam_member.bindings["myrole_two"]:
+ condition: []
+ member: serviceAccount:test@test-project.iam.gserviceaccount.com
+ project: foo-test-0
+ region: europe-west8
+ role: organizations/366118655033/roles/myRoleOne
+ subnetwork: production
+ google_dns_policy.default[0]:
+ alternative_name_server_config:
+ - target_name_servers:
+ - forwarding_path: ''
+ ipv4_address: 8.8.8.8
+ - forwarding_path: private
+ ipv4_address: 10.10.10.10
+ description: Managed by Terraform
+ enable_inbound_forwarding: true
+ enable_logging: null
+ name: test
+ networks:
+ - {}
+ project: foo-test-0
+ timeouts: null
+ google_network_connectivity_internal_range.internal_range["pods-range"]:
+ allocation_options: []
+ description: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ exclude_cidr_ranges: null
+ immutable: null
+ ip_cidr_range: 172.16.10.0/12
+ labels: null
+ migration: []
+ name: pods-range
+ overlaps: null
+ peering: FOR_SELF
+ prefix_length: null
+ project: foo-test-0
+ target_cidr_range: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ usage: FOR_VPC
+
+counts:
+ google_compute_network: 1
+ google_compute_route: 4
+ google_compute_subnetwork: 1
+ google_compute_subnetwork_iam_binding: 2
+ google_compute_subnetwork_iam_member: 1
+ google_dns_policy: 1
+ google_network_connectivity_internal_range: 1
+ modules: 0
+ resources: 11
diff --git a/tests/modules/net_vpc/tftest.yaml b/tests/modules/net_vpc/tftest.yaml
index 5e9668ea4..a8cfbe17b 100644
--- a/tests/modules/net_vpc/tftest.yaml
+++ b/tests/modules/net_vpc/tftest.yaml
@@ -17,6 +17,7 @@ common_tfvars:
- common.tfvars
tests:
+ context:
shared_vpc:
psa_routes_export:
psa_routes_import:
diff --git a/tests/modules/net_vpc_firewall/context.tfvars b/tests/modules/net_vpc_firewall/context.tfvars
new file mode 100644
index 000000000..782617fe3
--- /dev/null
+++ b/tests/modules/net_vpc_firewall/context.tfvars
@@ -0,0 +1,47 @@
+context = {
+ cidr_ranges = {
+ rfc1918-10 = "10.0.0.0/8"
+ }
+ iam_principals = {
+ test = "serviceAccount:test@test-project.iam.gserviceaccount.com"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+}
+project_id = "$project_ids:test"
+network = "$networks:test"
+attachments = {
+ test = "$networks:test"
+}
+default_rules_config = {
+ admin_ranges = ["$cidr_ranges:rfc1918-10"]
+ http_ranges = ["$cidr_ranges:rfc1918-10"]
+ https_ranges = ["$cidr_ranges:rfc1918-10"]
+ ssh_ranges = ["$cidr_ranges:rfc1918-10"]
+}
+egress_rules = {
+ allow-egress-rfc1918 = {
+ deny = false
+ description = "Allow egress."
+ destination_ranges = [
+ "$cidr_ranges:rfc1918-10", "172.16.0.0/12", "192.168.0.0/16"
+ ]
+ source_ranges = ["$cidr_ranges:rfc1918-10"]
+ targets = ["$iam_principals:test"]
+ use_service_accounts = true
+ }
+}
+ingress_rules = {
+ allow-ingress-tag = {
+ description = "Allow ingress."
+ destination_ranges = ["$cidr_ranges:rfc1918-10"]
+ source_ranges = ["$cidr_ranges:rfc1918-10"]
+ sources = ["$iam_principals:test"]
+ targets = ["$iam_principals:test"]
+ use_service_accounts = true
+ }
+}
diff --git a/tests/modules/net_vpc_firewall/context.yaml b/tests/modules/net_vpc_firewall/context.yaml
new file mode 100644
index 000000000..2ad022a5a
--- /dev/null
+++ b/tests/modules/net_vpc_firewall/context.yaml
@@ -0,0 +1,157 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ google_compute_firewall.allow-admins[0]:
+ allow:
+ - ports: []
+ protocol: all
+ deny: []
+ description: Access from the admin subnet to all subnets.
+ disabled: null
+ log_config: []
+ name: dev-spoke-0-ingress-admins
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ params: []
+ priority: 1000
+ project: foo-test-0
+ source_ranges:
+ - 10.0.0.0/8
+ source_service_accounts: null
+ source_tags: null
+ target_service_accounts: null
+ target_tags: null
+ timeouts: null
+ google_compute_firewall.allow-tag-http[0]:
+ allow:
+ - ports:
+ - '80'
+ protocol: tcp
+ deny: []
+ description: Allow http to machines with matching tags.
+ disabled: null
+ log_config: []
+ name: dev-spoke-0-ingress-tag-http
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ params: []
+ priority: 1000
+ project: foo-test-0
+ source_ranges:
+ - 10.0.0.0/8
+ source_service_accounts: null
+ source_tags: null
+ target_service_accounts: null
+ target_tags:
+ - http-server
+ timeouts: null
+ google_compute_firewall.allow-tag-https[0]:
+ allow:
+ - ports:
+ - '443'
+ protocol: tcp
+ deny: []
+ description: Allow http to machines with matching tags.
+ disabled: null
+ log_config: []
+ name: dev-spoke-0-ingress-tag-https
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ params: []
+ priority: 1000
+ project: foo-test-0
+ source_ranges:
+ - 10.0.0.0/8
+ source_service_accounts: null
+ source_tags: null
+ target_service_accounts: null
+ target_tags:
+ - https-server
+ timeouts: null
+ google_compute_firewall.allow-tag-ssh[0]:
+ allow:
+ - ports:
+ - '22'
+ protocol: tcp
+ deny: []
+ description: Allow SSH to machines with matching tags.
+ disabled: null
+ log_config: []
+ name: dev-spoke-0-ingress-tag-ssh
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ params: []
+ priority: 1000
+ project: foo-test-0
+ source_ranges:
+ - 10.0.0.0/8
+ source_service_accounts: null
+ source_tags: null
+ target_service_accounts: null
+ target_tags:
+ - ssh
+ timeouts: null
+ google_compute_firewall.custom-rules["allow-egress-rfc1918"]:
+ allow:
+ - ports: []
+ protocol: all
+ deny: []
+ description: Allow egress.
+ destination_ranges:
+ - 10.0.0.0/8
+ - 172.16.0.0/12
+ - 192.168.0.0/16
+ direction: EGRESS
+ disabled: false
+ log_config: []
+ name: allow-egress-rfc1918
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ params: []
+ priority: 1000
+ project: foo-test-0
+ source_ranges:
+ - 10.0.0.0/8
+ source_service_accounts: null
+ source_tags: null
+ target_service_accounts:
+ - serviceAccount:test@test-project.iam.gserviceaccount.com
+ target_tags: null
+ timeouts: null
+ google_compute_firewall.custom-rules["allow-ingress-tag"]:
+ allow:
+ - ports: []
+ protocol: all
+ deny: []
+ description: Allow ingress.
+ destination_ranges:
+ - 10.0.0.0/8
+ direction: INGRESS
+ disabled: false
+ log_config: []
+ name: allow-ingress-tag
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ params: []
+ priority: 1000
+ project: foo-test-0
+ source_ranges:
+ - 10.0.0.0/8
+ source_service_accounts:
+ - serviceAccount:test@test-project.iam.gserviceaccount.com
+ source_tags: null
+ target_service_accounts:
+ - serviceAccount:test@test-project.iam.gserviceaccount.com
+ target_tags: null
+ timeouts: null
+
+counts:
+ google_compute_firewall: 6
+ modules: 0
+ resources: 6
diff --git a/tests/modules/net_vpc_firewall/tftest.yaml b/tests/modules/net_vpc_firewall/tftest.yaml
new file mode 100644
index 000000000..5223867ff
--- /dev/null
+++ b/tests/modules/net_vpc_firewall/tftest.yaml
@@ -0,0 +1,17 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module: modules/net-vpc-firewall
+tests:
+ context:
diff --git a/tests/modules/organization/examples/roles.yaml b/tests/modules/organization/examples/roles.yaml
index a6ca7851b..c4c8ba5ed 100644
--- a/tests/modules/organization/examples/roles.yaml
+++ b/tests/modules/organization/examples/roles.yaml
@@ -12,21 +12,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-values:
- module.org.google_organization_iam_binding.authoritative["organizations/1122334455/roles/myRole"]:
+values:
+ module.org.google_organization_iam_binding.authoritative["organizations/1122334455/roles/myRoletest"]:
condition: []
members:
- group:organization-admins@example.org
org_id: '1122334455'
- role: organizations/1122334455/roles/myRole
- module.org.google_organization_iam_custom_role.roles["myRole"]:
+ role: organizations/1122334455/roles/myRoletest
+ module.org.google_organization_iam_custom_role.roles["myRoletest"]:
description: Terraform-managed.
org_id: '1122334455'
permissions:
- compute.instances.list
- role_id: myRole
+ role_id: myRoletest
stage: GA
- title: Custom role myRole
+ title: Custom role myRoletest
counts:
google_organization_iam_binding: 1
diff --git a/tests/modules/project/examples/custom-role-iam.yaml b/tests/modules/project/examples/custom-role-iam.yaml
new file mode 100644
index 000000000..26b7645af
--- /dev/null
+++ b/tests/modules/project/examples/custom-role-iam.yaml
@@ -0,0 +1,64 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ module.project.google_project.project[0]:
+ auto_create_network: false
+ billing_account: null
+ deletion_policy: DELETE
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ folder_id: null
+ labels: null
+ name: project
+ org_id: null
+ project_id: project
+ tags: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ module.project.google_project_iam_binding.authoritative["projects/project/roles/myRole"]:
+ condition: []
+ members:
+ - group:organization-admins@example.org
+ project: project
+ role: projects/project/roles/myRole
+ module.project.google_project_iam_binding.bindings["iam_admin_conditional"]:
+ condition:
+ - description: null
+ expression: "api.getAttribute(\n 'iam.googleapis.com/modifiedGrantsByRole',\
+ \ []\n).hasOnly([\n 'organizations/1234567890/roles/myRole'\n])\n"
+ title: delegated_custom_role
+ members:
+ - $iam_principals:org_admins
+ - group:organization-admins@example.org
+ project: project
+ role: roles/resourcemanager.projectIamAdmin
+ module.project.google_project_iam_custom_role.roles["myRole"]:
+ description: Terraform-managed.
+ permissions:
+ - compute.instances.list
+ project: project
+ role_id: myRole
+ stage: GA
+ title: Custom role myRole
+
+counts:
+ google_project: 1
+ google_project_iam_binding: 2
+ google_project_iam_custom_role: 1
+ modules: 1
+ resources: 4
+
+outputs: {}
diff --git a/tests/modules/project/examples/iam-authoritative.yaml b/tests/modules/project/examples/iam-authoritative.yaml
index 0f7e9ec7e..b45207006 100644
--- a/tests/modules/project/examples/iam-authoritative.yaml
+++ b/tests/modules/project/examples/iam-authoritative.yaml
@@ -31,19 +31,12 @@ values:
module.project.google_project_iam_binding.authoritative["$custom_roles:my_role"]:
condition: []
members:
- - group:gcp-organization-admins@example.com
+ - group:organization-admins@example.org
project: test-project
- role: organizations/1234567890/roles/myRole
- module.project.google_project_iam_binding.authoritative["roles/container.hostServiceAgentUser"]:
- condition: []
- members:
- - serviceAccount:my_gke_service_account
- project: test-project
- role: roles/container.hostServiceAgentUser
counts:
google_project: 1
- google_project_iam_binding: 2
+ google_project_iam_binding: 1
google_project_iam_member: 2
google_project_service: 2
google_project_service_identity: 1
diff --git a/tests/modules/project/examples/iam-bindings-additive.yaml b/tests/modules/project/examples/iam-bindings-additive.yaml
index 05c06dca1..3753799de 100644
--- a/tests/modules/project/examples/iam-bindings-additive.yaml
+++ b/tests/modules/project/examples/iam-bindings-additive.yaml
@@ -35,7 +35,7 @@ values:
role: roles/owner
module.project.google_project_iam_member.bindings["org-admins-viewer"]:
condition: []
- member: group:gcp-organization-admins@example.com
+ member: group:organization-admins@example.org
project: test-project
role: roles/viewer
diff --git a/tests/modules/project/examples/iam-bindings.yaml b/tests/modules/project/examples/iam-bindings.yaml
index c1dc8d53a..c924441a9 100644
--- a/tests/modules/project/examples/iam-bindings.yaml
+++ b/tests/modules/project/examples/iam-bindings.yaml
@@ -31,11 +31,8 @@ values:
module.project.google_project_iam_binding.bindings["iam_admin_conditional"]:
condition:
- description: null
- expression: "api.getAttribute(\n 'iam.googleapis.com/modifiedGrantsByRole',\
- \ []\n).hasOnly([\n 'roles/compute.networkAdmin'\n])\n"
- title: delegated_network_user_one
+ title: delegated_custom_role
members:
- - group:gcp-organization-admins@example.com
- group:organization-admins@example.org
project: test-project
role: roles/resourcemanager.projectIamAdmin
@@ -45,6 +42,6 @@ counts:
google_project_iam_binding: 1
google_project_service: 1
modules: 1
- resources: 3
+ resources: 4
outputs: {}
diff --git a/tests/modules/project/examples/iam-group.yaml b/tests/modules/project/examples/iam-group.yaml
index c61c44fcb..a9c11b916 100644
--- a/tests/modules/project/examples/iam-group.yaml
+++ b/tests/modules/project/examples/iam-group.yaml
@@ -18,7 +18,6 @@ values:
members:
- group:organization-admins@example.org
project: test-project
- role: organizations/1234567890/roles/myRole
module.project.google_project_iam_binding.authoritative["roles/cloudasset.owner"]:
condition: []
members:
@@ -46,7 +45,7 @@ values:
module.project.google_project_iam_binding.authoritative["roles/owner"]:
condition: []
members:
- - group:gcp-organization-admins@example.com
+ - group:organization-admins@example.org
project: test-project
role: roles/owner
@@ -54,4 +53,4 @@ counts:
google_project: 1
google_project_iam_binding: 6
modules: 1
- resources: 7
+ resources: 8
diff --git a/tests/modules/secure_source_manager_instance/examples/initial-config.yaml b/tests/modules/secure_source_manager_instance/examples/initial-config.yaml
new file mode 100644
index 000000000..5ed7b5961
--- /dev/null
+++ b/tests/modules/secure_source_manager_instance/examples/initial-config.yaml
@@ -0,0 +1,52 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ module.ssm_instance.google_secure_source_manager_instance.instance[0]:
+ deletion_policy: PREVENT
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ instance_id: my-instance
+ kms_key: null
+ labels: null
+ location: europe-west8
+ private_config:
+ - ca_pool: null
+ is_private: true
+ project: project-id
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ workforce_identity_federation_config: []
+ module.ssm_instance.google_secure_source_manager_repository.repositories["my-repository"]:
+ deletion_policy: PREVENT
+ description: null
+ initial_config:
+ - default_branch: main
+ gitignores:
+ - terraform.tfstate
+ license: null
+ readme: null
+ location: europe-west8
+ project: project-id
+ repository_id: my-repository
+ timeouts: null
+
+counts:
+ google_secure_source_manager_instance: 1
+ google_secure_source_manager_repository: 1
+ modules: 1
+ resources: 2
+
+outputs: {}