How to update WinRM certificate information in Azure after the VM has been created without replacing/recreating the VM

153 views Asked by At

We have existing Azure VM’s created via Terraform. The WinRM certificates have expired and now when we run Terraform it wants to replace the VM’s.

We can generate a new certificate in the Key Vault and update the certificate and WinRM listener directly on the VM, but those changes are not reflected in the Azure configuration, and thus Terraform still wants to replace the VM if we run terraform plan.

All the information we have found to date seems to suggest that the only way to update this information in Azure is to recreate the VM, and the Terraform registry documentation also states that the VM must be replaced.

Is there any way to update the WinRM certificate/Listener information for a VM in Azure without replacing the VM? We would like to be able to run Terraform against the VM’s without replacing the VM’s. Since Terraform pulls its information from Azure it seems like once the certificate expires you can’t use Terraform anymore unless you replace the machine.

Note: our Terraform grabs the latest WinRM certificate information from the Key Vault each time it is run. If the Azure VM configuration has the latest WinRM certificate information, then Terraform will see it as no changes required. That’s why we would like to get the information in Azure updated.

Any help with this issue would be greatly appreciated.

Thanks.

1

There are 1 answers

1
Vinay B On BEST ANSWER

I successfully provisioned the requirement by updating the WinRM certificate information in Azure for the existing VM, without using the lifecycle module to replace or recreate it.

If you want to update the WinRM certificate information for an Azure VM without replacing the VM in Terraform, you might face some difficulties. This is especially true when Terraform manages the VM's configuration and the certificates are stored in Azure Key Vault. The main challenge comes from how Terraform manages its state and reacts to infrastructure changes.

Terraform keeps track of the resources it manages and compares them with its state file. If there are any differences between the actual infrastructure and the state file, Terraform tries to fix them by making changes. For example, if a VM has a WinRM certificate in Terraform's configuration, and someone changes the certificate outside of Terraform, Terraform will notice this change and may recreate the VM to match its state file.

To avoid this problem, we can apply the lifecycle module, which allows the vm to ignore the changes in the specified parameter and stay stable without being recreated or modified according to the changes in the parameter that should be excluded.

My terraform configuration:

provider "azurerm" {
    features {}
}

data "azurerm_client_config" "current" {}


resource "azurerm_resource_group" "example" {
  name     = "demorg-resources"
  location = "West Europe"
}


resource "azurerm_key_vault" "example" {
  name                        = "vksbKeyVault"
  location                    = azurerm_resource_group.example.location
  resource_group_name         = azurerm_resource_group.example.name
  tenant_id                   = data.azurerm_client_config.current.tenant_id
  sku_name                    = "standard"
  soft_delete_retention_days  = 7
  purge_protection_enabled    = false

  network_acls {
    default_action             = "Allow"
    bypass                     = "AzureServices"
  }
}


resource "azurerm_key_vault_access_policy" "example" {
  key_vault_id = azurerm_key_vault.example.id

    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = data.azurerm_client_config.current.object_id

  key_permissions = [
    "Get",
    "List",
    "Update",
    "Create",
    "Import",
    "Delete",
    "Recover",
    "Backup",
    "Restore",
    "Decrypt",
    "Encrypt",
    "UnwrapKey",
    "WrapKey",
    "Verify",
    "Sign",
    "Purge",
    "Release",
    "Rotate",
    "GetRotationPolicy",
    "SetRotationPolicy"
  ]

  secret_permissions = [
    "Get",
    "List",
    "Set",
    "Delete",
    "Recover",
    "Backup",
    "Restore",
    "Purge"
  ]

  certificate_permissions = [
    "Get",
    "List",
    "Update",
    "Create",
    "Import",
    "Delete",
    "Recover",
    "Backup",
    "Restore",
    "ManageContacts",
    "ManageIssuers",
    "GetIssuers",
    "ListIssuers",
    "SetIssuers",
    "DeleteIssuers",
    "Purge"
  ]
}



resource "azurerm_key_vault_certificate" "example" {
  name         = "WinRMvkCertificate"
  key_vault_id = azurerm_key_vault.example.id

  certificate_policy {
    issuer_parameters {
      name = "Self"
    }

    key_properties {
      exportable = true
      key_type   = "RSA"
      key_size   = 2048
      reuse_key  = true
    }

    secret_properties {
      content_type = "application/x-pkcs12"
    }

    x509_certificate_properties {
      subject            = "CN=example.com"
      validity_in_months = 12

      key_usage = [
        "digitalSignature",
        "keyEncipherment"
      ]
    }
  }
}


resource "azurerm_virtual_network" "example" {
  name                = "demovk-vnet"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
}


resource "azurerm_subnet" "example" {
  name                 = "demovk-subnet"
  resource_group_name  = azurerm_resource_group.example.name
  virtual_network_name = azurerm_virtual_network.example.name
  address_prefixes     = ["10.0.1.0/24"]
}


resource "azurerm_network_interface" "example" {
  name                = "demovk-nic"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.example.id
    private_ip_address_allocation = "Dynamic"
  }
}


resource "azurerm_windows_virtual_machine" "example" {
  name                = "demovk-vm"
  resource_group_name = azurerm_resource_group.example.name
  location            = azurerm_resource_group.example.location
  size                = "Standard_F2"
  admin_username      = "adminuser"
  admin_password      = "yourPassword123!"
  network_interface_ids = [azurerm_network_interface.example.id]

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2019-Datacenter"
    version   = "latest"
  }

  computer_name  = "example-vm"
  enable_automatic_updates = true

     lifecycle {
    ignore_changes = [
      winrm_listener
    ]
  }

}

# Azure VM Extension for WinRM Configuration
resource "azurerm_virtual_machine_extension" "winrm_config" {
  name                 = "winrm-config"
  virtual_machine_id   = azurerm_windows_virtual_machine.example.id
  publisher            = "Microsoft.Compute"
  type                 = "CustomScriptExtension"
  type_handler_version = "1.10"

  settings = jsonencode({
    "commandToExecute": "powershell -ExecutionPolicy Unrestricted -File configure-winrm.ps1"
  })

  # Assuming the script is uploaded to a location accessible by the VM
  protected_settings = jsonencode({
    "fileUris": ["https://demovkstor.blob.core.windows.net/democont/configure-winrm.ps1"]
  })
}


# Outputs
output "vm_id" {
  value = azurerm_windows_virtual_machine.example.id
}

To configure the WinRM listener on a Windows Virtual Machine in Azure using Terraform, we can use the Azure VM extension

The extension file for Powershell will be uploaded to the blob storage.

enter image description here

Output:

enter image description here

enter image description here

enter image description here

I have made the necessary changes in the WinVMvkcertificate to replace the certificate with a new version and checked whether those changes would lead to VM recreation or not by re-run the terraform commands.

enter image description here

The only change here was to update the certificate information, not to recreate or modify the VM in any way.