Files
hunfabric/modules/compute-vm/template.tf
Suryansh Singhal ba304518f1 Prevent recreation for key_revocation_action in compute-vm module (#3620)
* fix(compute-vm): prevent instance recreation for key_revocation_action_type

The `key_revocation_action_type` attribute in the `google_compute_instance`
resource defaults to `NONE` on the provider side.

When this optional attribute was not explicitly set in the module's
`options` variable, it resolved to `null` in the Terraform configuration.
This caused Terraform to detect a change from `null` to `NONE` on every
plan, leading to unnecessary instance recreation.

This change applies `coalesce` to
`var.options.key_revocation_action_type` in the resource definition so
that it defaults to `NONE` when unset, aligning the configuration with
provider behavior and preventing a permadiff.

* updated the regional compute template too with the key_revocation_action_type

* fixed the formatting
2026-01-05 11:53:47 +01:00

464 lines
15 KiB
HCL

/**
* 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.
*/
locals {
template_create = var.create_template != null
template_regional = try(var.create_template.regional, null) == true
}
resource "google_compute_instance_template" "default" {
provider = google-beta
count = local.template_create && !local.template_regional ? 1 : 0
project = local.project_id
region = local.region
name_prefix = "${var.name}-"
description = var.description
tags = var.tags
machine_type = var.instance_type
min_cpu_platform = var.min_cpu_platform
can_ip_forward = var.can_ip_forward
metadata = var.metadata
metadata_startup_script = var.metadata_startup_script
labels = var.labels
resource_manager_tags = var.tag_bindings_immutable
key_revocation_action_type = var.options.key_revocation_action_type
resource_policies = (
var.resource_policies == null && var.instance_schedule == null
? null
: concat(
coalesce(var.resource_policies, []),
coalesce(local.ischedule, [])
)
)
dynamic "advanced_machine_features" {
for_each = local.advanced_mf != null ? [""] : []
content {
enable_nested_virtualization = local.advanced_mf.enable_nested_virtualization
enable_uefi_networking = local.advanced_mf.enable_uefi_networking
performance_monitoring_unit = local.advanced_mf.performance_monitoring_unit
threads_per_core = local.advanced_mf.threads_per_core
turbo_mode = (
local.advanced_mf.enable_turbo_mode ? "ALL_CORE_MAX" : null
)
visible_core_count = local.advanced_mf.visible_core_count
}
}
disk {
architecture = var.boot_disk.initialize_params.architecture
auto_delete = var.boot_disk.auto_delete
boot = true
disk_size_gb = var.boot_disk.initialize_params.size
disk_type = var.boot_disk.initialize_params.type
provisioned_iops = var.boot_disk.initialize_params.provisioned_iops
provisioned_throughput = var.boot_disk.initialize_params.provisioned_throughput
resource_manager_tags = var.tag_bindings_immutable
source_image = var.boot_disk.initialize_params.image
dynamic "disk_encryption_key" {
for_each = var.encryption != null ? [""] : []
content {
kms_key_self_link = lookup(
local.ctx_kms_keys,
var.encryption.kms_key_self_link,
var.encryption.kms_key_self_link
)
}
}
}
dynamic "confidential_instance_config" {
for_each = var.confidential_compute ? [""] : []
content {
enable_confidential_compute = true
}
}
dynamic "guest_accelerator" {
for_each = local.gpu ? [var.gpu] : []
content {
type = guest_accelerator.value.type
count = guest_accelerator.value.count
}
}
dynamic "disk" {
for_each = local.attached_disks
iterator = config
content {
architecture = config.value.options.architecture
auto_delete = config.value.options.auto_delete
device_name = coalesce(
config.value.device_name, config.value.name, config.key
)
# Cannot use `source` with any of the fields in
# [disk_size_gb disk_name disk_type source_image labels]
disk_type = (
config.value.source_type != "attach" ? config.value.options.type : null
)
disk_size_gb = (
config.value.source_type != "attach" ? config.value.size : null
)
mode = config.value.options.mode
provisioned_iops = config.value.options.provisioned_iops
provisioned_throughput = config.value.options.provisioned_throughput
source_image = (
config.value.source_type == "image" ? config.value.source : null
)
source = (
config.value.source_type == "attach" ? config.value.source : null
)
disk_name = (
config.value.source_type != "attach" ? config.value.name : null
)
resource_manager_tags = var.tag_bindings_immutable
type = "PERSISTENT"
dynamic "disk_encryption_key" {
for_each = var.encryption != null ? [""] : []
content {
kms_key_self_link = lookup(
local.ctx_kms_keys,
var.encryption.kms_key_self_link,
var.encryption.kms_key_self_link
)
}
}
}
}
dynamic "network_interface" {
for_each = var.network_interfaces
iterator = config
content {
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(
local.ctx.addresses[config.value.addresses.external],
config.value.addresses.external,
null
)
}
}
dynamic "alias_ip_range" {
for_each = config.value.alias_ips
iterator = config_alias
content {
subnetwork_range_name = config_alias.key
ip_cidr_range = config_alias.value
}
}
}
}
dynamic "network_interface" {
for_each = var.network_attached_interfaces
content {
network_attachment = network_interface.value
}
}
scheduling {
automatic_restart = !var.options.spot
instance_termination_action = local.termination_action
on_host_maintenance = local.on_host_maintenance
preemptible = var.options.spot
provisioning_model = var.options.spot ? "SPOT" : "STANDARD"
dynamic "max_run_duration" {
for_each = var.options.max_run_duration == null ? [] : [""]
content {
nanos = var.options.max_run_duration.nanos
seconds = var.options.max_run_duration.seconds
}
}
dynamic "node_affinities" {
for_each = var.options.node_affinities
iterator = affinity
content {
key = affinity.key
operator = affinity.value.in ? "IN" : "NOT_IN"
values = affinity.value.values
}
}
dynamic "graceful_shutdown" {
for_each = var.options.graceful_shutdown != null ? [""] : []
content {
enabled = var.options.graceful_shutdown.enabled
dynamic "max_duration" {
for_each = var.options.graceful_shutdown.enabled == true && var.options.graceful_shutdown.max_duration_secs != null ? [""] : []
content {
seconds = var.options.graceful_shutdown.max_duration_secs
nanos = 0
}
}
}
}
}
dynamic "service_account" {
for_each = var.service_account == null ? [] : [""]
content {
email = local.service_account.email
scopes = local.service_account.scopes
}
}
dynamic "shielded_instance_config" {
for_each = var.shielded_config != null ? [var.shielded_config] : []
iterator = config
content {
enable_secure_boot = config.value.enable_secure_boot
enable_vtpm = config.value.enable_vtpm
enable_integrity_monitoring = config.value.enable_integrity_monitoring
}
}
lifecycle {
create_before_destroy = true
}
}
resource "google_compute_region_instance_template" "default" {
provider = google-beta
count = local.template_create && local.template_regional ? 1 : 0
project = local.project_id
region = local.region
name_prefix = "${var.name}-"
description = var.description
tags = var.tags
machine_type = var.instance_type
min_cpu_platform = var.min_cpu_platform
can_ip_forward = var.can_ip_forward
metadata = var.metadata
metadata_startup_script = var.metadata_startup_script
labels = var.labels
resource_manager_tags = var.tag_bindings_immutable
key_revocation_action_type = var.options.key_revocation_action_type
resource_policies = (
var.resource_policies == null && var.instance_schedule == null
? null
: concat(
coalesce(var.resource_policies, []),
coalesce(local.ischedule, [])
)
)
dynamic "advanced_machine_features" {
for_each = local.advanced_mf != null ? [""] : []
content {
enable_nested_virtualization = local.advanced_mf.enable_nested_virtualization
enable_uefi_networking = local.advanced_mf.enable_uefi_networking
performance_monitoring_unit = local.advanced_mf.performance_monitoring_unit
threads_per_core = local.advanced_mf.threads_per_core
turbo_mode = (
local.advanced_mf.enable_turbo_mode ? "ALL_CORE_MAX" : null
)
visible_core_count = local.advanced_mf.visible_core_count
}
}
disk {
architecture = var.boot_disk.initialize_params.architecture
auto_delete = var.boot_disk.auto_delete
boot = true
disk_size_gb = var.boot_disk.initialize_params.size
disk_type = var.boot_disk.initialize_params.type
provisioned_iops = var.boot_disk.initialize_params.provisioned_iops
provisioned_throughput = var.boot_disk.initialize_params.provisioned_throughput
resource_manager_tags = var.tag_bindings_immutable
source_image = var.boot_disk.initialize_params.image
dynamic "disk_encryption_key" {
for_each = var.encryption != null ? [""] : []
content {
kms_key_self_link = try(
local.ctx_kms_keys[var.encryption.kms_key_self_link],
var.encryption.kms_key_self_link
)
}
}
}
dynamic "confidential_instance_config" {
for_each = var.confidential_compute ? [""] : []
content {
enable_confidential_compute = true
}
}
dynamic "guest_accelerator" {
for_each = local.gpu ? [var.gpu] : []
content {
type = guest_accelerator.value.type
count = guest_accelerator.value.count
}
}
dynamic "disk" {
for_each = local.attached_disks
iterator = config
content {
architecture = config.value.options.architecture
auto_delete = config.value.options.auto_delete
device_name = coalesce(
config.value.device_name, config.value.name, config.key
)
# Cannot use `source` with any of the fields in
# [disk_size_gb disk_name disk_type source_image labels]
disk_type = (
config.value.source_type != "attach" ? config.value.options.type : null
)
disk_size_gb = (
config.value.source_type != "attach" ? config.value.size : null
)
mode = config.value.options.mode
provisioned_iops = config.value.options.provisioned_iops
provisioned_throughput = config.value.options.provisioned_throughput
source_image = (
config.value.source_type == "image" ? config.value.source : null
)
source = (
config.value.source_type == "attach" ? config.value.source : null
)
disk_name = (
config.value.source_type != "attach" ? config.value.name : null
)
resource_manager_tags = var.tag_bindings_immutable
type = "PERSISTENT"
dynamic "disk_encryption_key" {
for_each = var.encryption != null ? [""] : []
content {
kms_key_self_link = try(
local.ctx_kms_keys[var.encryption.kms_key_self_link],
var.encryption.kms_key_self_link
)
}
}
}
}
dynamic "network_interface" {
for_each = var.network_interfaces
iterator = config
content {
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(
local.ctx.addresses[config.value.addresses.external],
config.value.addresses.external,
null
)
}
}
dynamic "alias_ip_range" {
for_each = config.value.alias_ips
iterator = config_alias
content {
subnetwork_range_name = config_alias.key
ip_cidr_range = config_alias.value
}
}
}
}
scheduling {
automatic_restart = !var.options.spot
instance_termination_action = local.termination_action
on_host_maintenance = local.on_host_maintenance
preemptible = var.options.spot
provisioning_model = var.options.spot ? "SPOT" : "STANDARD"
dynamic "max_run_duration" {
for_each = var.options.max_run_duration == null ? [] : [""]
content {
nanos = var.options.max_run_duration.nanos
seconds = var.options.max_run_duration.seconds
}
}
dynamic "node_affinities" {
for_each = var.options.node_affinities
iterator = affinity
content {
key = affinity.key
operator = affinity.value.in ? "IN" : "NOT_IN"
values = affinity.value.values
}
}
dynamic "graceful_shutdown" {
for_each = var.options.graceful_shutdown != null ? [""] : []
content {
enabled = var.options.graceful_shutdown.enabled
dynamic "max_duration" {
for_each = var.options.graceful_shutdown.enabled == true && var.options.graceful_shutdown.max_duration_secs != null ? [""] : []
content {
seconds = var.options.graceful_shutdown.max_duration_secs
nanos = 0
}
}
}
}
}
dynamic "service_account" {
for_each = var.service_account == null ? [] : [""]
content {
email = local.service_account.email
scopes = local.service_account.scopes
}
}
dynamic "shielded_instance_config" {
for_each = var.shielded_config != null ? [var.shielded_config] : []
iterator = config
content {
enable_secure_boot = config.value.enable_secure_boot
enable_vtpm = config.value.enable_vtpm
enable_integrity_monitoring = config.value.enable_integrity_monitoring
}
}
lifecycle {
create_before_destroy = true
}
}