From a83a10475ba04df3b8f3f030ed3dde85c05e7a2d Mon Sep 17 00:00:00 2001 From: Henry Buckle Date: Sun, 16 May 2021 20:42:54 +0100 Subject: [PATCH] windows_virtual_machine_scale_set: Allow updating license_type without recreation (#11731) Apply same config from #8542 to the scale set resource --- ...l_machine_scale_set_other_resource_test.go | 115 ++++++++++++++++++ ...dows_virtual_machine_scale_set_resource.go | 19 ++- ...ws_virtual_machine_scale_set.html.markdown | 2 +- 3 files changed, 134 insertions(+), 2 deletions(-) diff --git a/azurerm/internal/services/compute/windows_virtual_machine_scale_set_other_resource_test.go b/azurerm/internal/services/compute/windows_virtual_machine_scale_set_other_resource_test.go index 3e09cd30b6202..6bcd35d55bcaf 100644 --- a/azurerm/internal/services/compute/windows_virtual_machine_scale_set_other_resource_test.go +++ b/azurerm/internal/services/compute/windows_virtual_machine_scale_set_other_resource_test.go @@ -730,6 +730,42 @@ func TestAccWindowsVirtualMachineScaleSet_otherHealthProbeUpdate(t *testing.T) { }) } +func TestAccWindowsVirtualMachineScaleSet_otherLicenseTypeUpdated(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_virtual_machine_scale_set", "test") + r := WindowsVirtualMachineScaleSetResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.otherLicenseTypeDefault(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep( + "admin_password", + ), + { + Config: r.otherLicenseType(data, "Windows_Client"), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("license_type").HasValue("Windows_Client"), + ), + }, + data.ImportStep( + "admin_password", + ), + { + Config: r.otherLicenseTypeDefault(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep( + "admin_password", + ), + }) +} + func (WindowsVirtualMachineScaleSetResource) otherAdditionalUnattendContent(data acceptance.TestData) string { template := WindowsVirtualMachineScaleSetResource{}.template(data) return fmt.Sprintf(` @@ -2788,3 +2824,82 @@ resource "azurerm_windows_virtual_machine_scale_set" "test" { } `, r.template(data), data.RandomInteger) } + +func (r WindowsVirtualMachineScaleSetResource) otherLicenseTypeDefault(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_windows_virtual_machine_scale_set" "test" { + name = local.vm_name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "Standard_F2" + instances = 1 + admin_username = "adminuser" + admin_password = "P@ssword1234!" + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2019-Datacenter" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, r.template(data)) +} + +func (r WindowsVirtualMachineScaleSetResource) otherLicenseType(data acceptance.TestData, licenseType string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_windows_virtual_machine_scale_set" "test" { + name = local.vm_name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "Standard_F2" + instances = 1 + admin_username = "adminuser" + admin_password = "P@ssword1234!" + license_type = %q + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2019-Datacenter" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, r.template(data), licenseType) +} diff --git a/azurerm/internal/services/compute/windows_virtual_machine_scale_set_resource.go b/azurerm/internal/services/compute/windows_virtual_machine_scale_set_resource.go index 84025e84cb64e..80721ec59d0df 100644 --- a/azurerm/internal/services/compute/windows_virtual_machine_scale_set_resource.go +++ b/azurerm/internal/services/compute/windows_virtual_machine_scale_set_resource.go @@ -163,12 +163,18 @@ func resourceWindowsVirtualMachineScaleSet() *schema.Resource { "license_type": { Type: schema.TypeString, Optional: true, - ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ "None", "Windows_Client", "Windows_Server", }, false), + DiffSuppressFunc: func(_, old, new string, _ *schema.ResourceData) bool { + if old == "None" && new == "" || old == "" && new == "None" { + return true + } + + return false + }, }, "max_bid_price": { @@ -809,6 +815,17 @@ func resourceWindowsVirtualMachineScaleSetUpdate(d *schema.ResourceData, meta in } } + if d.HasChange("license_type") { + license := d.Get("license_type").(string) + if license == "" { + // Only for create no specification is possible in the API. API does not allow empty string in update. + // So removing attribute license_type from Terraform configuration if it was set to value other than 'None' would lead to an endless loop in apply. + // To allow updating in this case set value explicitly to 'None'. + license = "None" + } + updateProps.VirtualMachineProfile.LicenseType = &license + } + if d.HasChange("automatic_instance_repair") { automaticRepairsPolicyRaw := d.Get("automatic_instance_repair").([]interface{}) automaticRepairsPolicy := ExpandVirtualMachineScaleSetAutomaticRepairsPolicy(automaticRepairsPolicyRaw) diff --git a/website/docs/r/windows_virtual_machine_scale_set.html.markdown b/website/docs/r/windows_virtual_machine_scale_set.html.markdown index d45ba6cf4ec44..0056776f06f90 100644 --- a/website/docs/r/windows_virtual_machine_scale_set.html.markdown +++ b/website/docs/r/windows_virtual_machine_scale_set.html.markdown @@ -144,7 +144,7 @@ The following arguments are supported: * `identity` - (Optional) A `identity` block as defined below. -* `license_type` - (Optional) Specifies the type of on-premise license (also known as [Azure Hybrid Use Benefit](https://docs.microsoft.com/azure/virtual-machines/virtual-machines-windows-hybrid-use-benefit-licensing)) which should be used for this Virtual Machine Scale Set. Possible values are `None`, `Windows_Client` and `Windows_Server`. Changing this forces a new resource to be created. +* `license_type` - (Optional) Specifies the type of on-premise license (also known as [Azure Hybrid Use Benefit](https://docs.microsoft.com/azure/virtual-machines/virtual-machines-windows-hybrid-use-benefit-licensing)) which should be used for this Virtual Machine Scale Set. Possible values are `None`, `Windows_Client` and `Windows_Server`. * `max_bid_price` - (Optional) The maximum price you're willing to pay for each Virtual Machine in this Scale Set, in US Dollars; which must be greater than the current spot price. If this bid price falls below the current spot price the Virtual Machines in the Scale Set will be evicted using the `eviction_policy`. Defaults to `-1`, which means that each Virtual Machine in the Scale Set should not be evicted for price reasons.