diff --git a/modules/compute-vm/README.md b/modules/compute-vm/README.md
index 450ccad73..aea113b74 100644
--- a/modules/compute-vm/README.md
+++ b/modules/compute-vm/README.md
@@ -34,7 +34,8 @@ In both modes, an optional service account can be created and assigned to either
- [Instance group](#instance-group)
- [Instance Schedule](#instance-schedule)
- [Snapshot Schedules](#snapshot-schedules)
- - [Resource Manager Tags](#resource-manager-tags)
+ - [Resource Manager Tags (non-firewall)](#resource-manager-tags-non-firewall)
+ - [Resource Manager Tags (firewall)](#resource-manager-tags-firewall)
- [Variables](#variables)
- [Outputs](#outputs)
@@ -678,15 +679,21 @@ module "instance" {
# tftest modules=1 resources=5 inventory=snapshot-schedule-create.yaml
```
-### Resource Manager Tags
+### Resource Manager Tags (non-firewall)
-Resource manager tags (or "secure tags") bindings are supported with the following limitations:
+Resource manager tags bindings for use in IAM or org policy conditions are supported via the `tag_bindings` variable with the following limitations:
-- a single `tag_bindings` variable is used for both the instance and the boot disk
- tag bindings are not created for attached disks
- tag bindings will not be created for the boot disk if the `use_independent_disk` flag is true
- tag bindings are ignored for instance templates
+The current provider implementation is sub-optimal and forces
+
+- recreation of the instance on tag changes
+- specifying both the key and value where only the value is actually needed
+
+This is an example of setting tag bindings:
+
```hcl
module "simple-vm-example" {
source = "./fabric/modules/compute-vm"
@@ -703,6 +710,33 @@ module "simple-vm-example" {
}
# tftest modules=1 resources=1 inventory=tag-bindings.yaml
```
+
+### Resource Manager Tags (firewall)
+
+Network-scoped resource manager tags (or "secure tags") bindings for use in firewall rules are supported with similar limitations as in the section above, via a separate `tag_bindings_firewall` variable that only applies bindings to the instance and not the boot disk.
+
+This is an example of setting both types of tag bindings:
+
+```hcl
+module "simple-vm-example" {
+ source = "./fabric/modules/compute-vm"
+ project_id = var.project_id
+ zone = "europe-west1-b"
+ name = "test"
+ network_interfaces = [{
+ network = var.vpc.self_link
+ subnetwork = var.subnet.self_link
+ }]
+ tag_bindings = {
+ "tagKeys/1234567890" = "tagValues/7890123456"
+ }
+ # tags here need to be scoped to a VPC
+ tag_bindings_firewall = {
+ "tagKeys/5678901234" = "tagValues/3456789012"
+ }
+}
+# tftest modules=1 resources=1 inventory=tag-bindings.yaml
+```
## Variables
@@ -711,7 +745,7 @@ module "simple-vm-example" {
| [name](variables.tf#L235) | Instance name. | string | ✓ | |
| [network_interfaces](variables.tf#L240) | Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed. | list(object({…})) | ✓ | |
| [project_id](variables.tf#L278) | Project id. | string | ✓ | |
-| [zone](variables.tf#L370) | Compute zone. | string | ✓ | |
+| [zone](variables.tf#L376) | 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#L83) | Boot disk properties. | object({…}) | | {…} |
@@ -734,8 +768,9 @@ module "simple-vm-example" {
| [service_account](variables.tf#L295) | 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#L305) | Shielded VM configuration of the instances. | object({…}) | | null |
| [snapshot_schedules](variables.tf#L315) | Snapshot schedule resource policies that can be attached to disks. | map(object({…})) | | {} |
-| [tag_bindings](variables.tf#L358) | Tag bindings for this instance, in tag key => tag value format. | map(string) | | null |
-| [tags](variables.tf#L364) | Instance network tags for firewall rule targets. | list(string) | | [] |
+| [tag_bindings](variables.tf#L358) | Resource manager tag bindings for this instance, in tag key => tag value format. | map(string) | | null |
+| [tag_bindings_firewall](variables.tf#L364) | Firewall (network scoped) tag bindings for this instance, in tag key => tag value format. | map(string) | | null |
+| [tags](variables.tf#L370) | Instance network tags for firewall rule targets. | list(string) | | [] |
## Outputs
@@ -754,4 +789,3 @@ module "simple-vm-example" {
| [template](outputs.tf#L82) | Template resource. | |
| [template_name](outputs.tf#L87) | Template name. | |
-
diff --git a/modules/compute-vm/main.tf b/modules/compute-vm/main.tf
index e2d4dfcc3..c39db31b3 100644
--- a/modules/compute-vm/main.tf
+++ b/modules/compute-vm/main.tf
@@ -58,7 +58,17 @@ locals {
)
)
}
- termination_action = var.options.spot ? coalesce(var.options.termination_action, "STOP") : null
+ tags_combined = (
+ var.tag_bindings == null && var.tag_bindings_firewall == null
+ ? null
+ : merge(
+ coalesce(var.tag_bindings, {}),
+ coalesce(var.tag_bindings_firewall, {})
+ )
+ )
+ termination_action = (
+ var.options.spot ? coalesce(var.options.termination_action, "STOP") : null
+ )
}
resource "google_compute_disk" "boot" {
@@ -294,9 +304,9 @@ resource "google_compute_instance" "default" {
}
dynamic "params" {
- for_each = var.tag_bindings == null ? [] : [""]
+ for_each = local.tags_combined == null ? [] : [""]
content {
- resource_manager_tags = var.tag_bindings
+ resource_manager_tags = local.tags_combined
}
}
diff --git a/modules/compute-vm/tags.tf b/modules/compute-vm/tags.tf
index cce3b99cc..fccc7e4d2 100644
--- a/modules/compute-vm/tags.tf
+++ b/modules/compute-vm/tags.tf
@@ -16,15 +16,15 @@
# tfdoc:file:description Tag bindings.
-# TODO: re-implement once
-# - the provider accepts a project id in the parent without a permadiff
-# - the disk resource exposes an id that can be used to build the parent
-
-# locals {
-# tag_parent_base = (
-# "//compute.googleapis.com/projects/${var.project_id}/zones/${var.zone}"
-# )
-# }
+# TODO: re-implement once the following have been addressed in the provider
+# - permadiff in google_tags_location_tag_binding which returns a project
+# number in the tag id even when a project id is set
+# - no numeric id exposed from the google_compute_disk resource making it
+# impossible to derive the tag binding parent
+# - google_compute_instance.params.resource_manager_tags and
+# google_compute_instance.boot_disk.initialize_params.resource_manager_tags
+# attributes need a map of tag key => tag value, while only the tag value
+# is really needed by the API
# resource "google_tags_location_tag_binding" "instance" {
# for_each = var.create_template ? {} : coalesce(var.tag_bindings, {})
diff --git a/modules/compute-vm/variables.tf b/modules/compute-vm/variables.tf
index d82ff1d4b..e57dacce9 100644
--- a/modules/compute-vm/variables.tf
+++ b/modules/compute-vm/variables.tf
@@ -356,7 +356,13 @@ variable "snapshot_schedules" {
}
variable "tag_bindings" {
- description = "Tag bindings for this instance, in tag key => tag value format."
+ description = "Resource manager tag bindings for this instance, in tag key => tag value format."
+ type = map(string)
+ default = null
+}
+
+variable "tag_bindings_firewall" {
+ description = "Firewall (network scoped) tag bindings for this instance, in tag key => tag value format."
type = map(string)
default = null
}