Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/modules/azure.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ lacks. Keep new inputs shaped the AVM way.
| Module | What it does |
|---|---|
| [`azure-ptn-authorization-roledefinition-collection`](../../src/modules/azure-ptn-authorization-roledefinition-collection/) | Many role definitions from one map |
| [`azure-ptn-compute-virtualmachine-windows-fqdn`](../../src/modules/azure-ptn-compute-virtualmachine-windows-fqdn/) | Windows VM in-guest primary DNS suffix set via CustomScriptExtension (so the guest FQDN matches its public DNS name), then applied by an ARM restart the apply blocks on until the VM is running again |
| [`azure-ptn-network-dnszone-records`](../../src/modules/azure-ptn-network-dnszone-records/) | A, AAAA, CAA, CNAME, MX, NS, PTR, SRV and TXT records in an existing public zone |
| [`azure-ptn-network-privatednszone-records`](../../src/modules/azure-ptn-network-privatednszone-records/) | A, AAAA, CNAME, MX, PTR, SRV and TXT records in an existing private zone |
| [`azure-ptn-network-privatednszone-vnet-links`](../../src/modules/azure-ptn-network-privatednszone-vnet-links/) | Virtual network links across many zones |
Expand Down
65 changes: 65 additions & 0 deletions src/modules/azure-ptn-compute-virtualmachine-windows-fqdn/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# =============================================================================
# WINDOWS VM PRIMARY DNS SUFFIX (Microsoft.Compute/virtualMachines/extensions)
# =============================================================================
# Sets the guest primary DNS suffix via a CustomScriptExtension, then applies it
# with an ARM restart. The restart (not an in-guest `shutdown`) is deliberate: an
# in-guest reboot is invisible to ARM, so Terraform can't wait on it; azapi blocks
# on the restart LRO until the VM is running again.
# =============================================================================

locals {
# HKLM\...\Tcpip\Parameters - the keys the Computer Name dialog writes. The
# restart below applies them (the value is read at boot, however triggered).
set_dns_suffix_script = <<-PS
$ErrorActionPreference = 'Stop'
$suffix = '${var.dns_suffix}'
$key = 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters'
Set-ItemProperty -Path $key -Name 'Domain' -Value $suffix -Type String
Set-ItemProperty -Path $key -Name 'NV Domain' -Value $suffix -Type String
Set-ItemProperty -Path $key -Name 'SyncDomainWithMembership' -Value 0 -Type DWord
Write-Output "Primary DNS suffix set to '$suffix'. Reboot required to take effect."
exit 0
PS
}

resource "azurerm_virtual_machine_extension" "dns_suffix" {
name = "SetPrimaryDnsSuffix"
virtual_machine_id = var.virtual_machine_id
publisher = "Microsoft.Compute"
type = "CustomScriptExtension"
type_handler_version = "1.10"
auto_upgrade_minor_version = true
provision_after_extensions = var.provision_after_extensions
tags = var.tags

# -ExecutionPolicy is omitted: it governs script files, not the inline
# -EncodedCommand, so it would be a no-op here.
settings = jsonencode({
commandToExecute = "powershell.exe -NoProfile -EncodedCommand ${textencodebase64(local.set_dns_suffix_script, "UTF-16LE")}"
})
}

# -----------------------------------------------------------------------------
# Apply the suffix with an ARM restart (blocks until the VM is running again)
# -----------------------------------------------------------------------------
# replace_triggered_by keys on .settings so the restart re-fires only when the
# suffix changes - not on tags/handler changes; the action is otherwise inert.

resource "azapi_resource_action" "reboot" {
count = var.reboot ? 1 : 0

type = "Microsoft.Compute/virtualMachines@2024-07-01"
resource_id = var.virtual_machine_id
action = "restart"
method = "POST"

timeouts {
create = "15m"
}

lifecycle {
replace_triggered_by = [azurerm_virtual_machine_extension.dns_suffix.settings]
}

depends_on = [azurerm_virtual_machine_extension.dns_suffix]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
output "extension_id" {
description = "Resource ID of the CustomScriptExtension that sets the primary DNS suffix."
value = azurerm_virtual_machine_extension.dns_suffix.id
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
variable "virtual_machine_id" {
description = "Resource ID of the Windows virtual machine whose in-guest primary DNS suffix this extension sets."
type = string

validation {
condition = can(regex("/providers/Microsoft.Compute/virtualMachines/", var.virtual_machine_id))
error_message = "virtual_machine_id must be a Microsoft.Compute/virtualMachines resource ID."
}
}

variable "dns_suffix" {
description = "Primary DNS suffix to write in-guest, e.g. westeurope.cloudapp.azure.com, so the machine's FQDN matches the public DNS name of its IP. Any lowercase DNS domain is accepted."
type = string

validation {
condition = can(regex("^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,}$", var.dns_suffix))
error_message = "dns_suffix must be a valid lowercase DNS domain (e.g. westeurope.cloudapp.azure.com)."
}
}

variable "provision_after_extensions" {
description = "Names of same-VM extensions this suffix step must provision after — the guest agent holds it (and its reboot) until they finish. Empty means no ordering constraint. Example: [\"AADLoginForWindows\"], so the reboot cannot land mid Entra-join."
type = list(string)
default = []
nullable = false
}

variable "tags" {
description = "Tags applied to the VM extension."
type = map(string)
default = {}
nullable = false
}

variable "reboot" {
description = "Reboot the VM (via an ARM restart) after writing the suffix so it takes effect, and block the apply until the restart completes and the VM is running again. When it's false the module only writes the registry and the suffix stays inactive until the VM is rebooted by other means."
type = bool
default = true
nullable = false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
terraform {
required_version = ">= 1.15"

required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 4.81, < 5.0"
}
azapi = {
source = "Azure/azapi"
version = ">= 2.0, < 3.0"
}
}
}