diff --git a/internal/services/compute/orchestrated_virtual_machine_scale_set.go b/internal/services/compute/orchestrated_virtual_machine_scale_set.go index 5b843b2eb43d..61941b0233b4 100644 --- a/internal/services/compute/orchestrated_virtual_machine_scale_set.go +++ b/internal/services/compute/orchestrated_virtual_machine_scale_set.go @@ -65,8 +65,7 @@ func OrchestratedVirtualMachineScaleSetWindowsConfigurationSchema() *pluginsdk.S "computer_name_prefix": computerPrefixWindowsSchema(), - // I am only commenting this out as this is going to be supported in the next release of the API in October 2021 - // "additional_unattend_content": additionalUnattendContentSchema(), + "additional_unattend_content": additionalUnattendContentSchema(), // TODO 4.0: change this from enable_* to *_enabled "enable_automatic_updates": { @@ -930,8 +929,9 @@ func expandOrchestratedVirtualMachineScaleSetOsProfileWithWindowsConfiguration(i osProfile.Secrets = expandWindowsSecrets(secrets) } - // I am only commenting this out as this is going to be supported in the next release of the API in October 2021 - // winConfig.AdditionalUnattendContent = expandWindowsConfigurationAdditionalUnattendContent(input["additional_unattend_content"].([]interface{})) + if additionalUnattendContents := input["additional_unattend_content"].([]interface{}); len(additionalUnattendContents) > 0 { + winConfig.AdditionalUnattendContent = expandWindowsConfigurationAdditionalUnattendContent(input["additional_unattend_content"].([]interface{})) + } winConfig.EnableAutomaticUpdates = utils.Bool(input["enable_automatic_updates"].(bool)) winConfig.ProvisionVMAgent = utils.Bool(input["provision_vm_agent"].(bool)) winRmListenersRaw := input["winrm_listener"].(*pluginsdk.Set).List() @@ -998,25 +998,24 @@ func expandOrchestratedVirtualMachineScaleSetOsProfileWithLinuxConfiguration(inp return &osProfile } -// I am only commenting this out as this is going to be supported in the next release of the API version 2021-10-01 -// func expandWindowsConfigurationAdditionalUnattendContent(input []interface{}) *[]compute.AdditionalUnattendContent { -// output := make([]compute.AdditionalUnattendContent, 0) +func expandWindowsConfigurationAdditionalUnattendContent(input []interface{}) *[]compute.AdditionalUnattendContent { + output := make([]compute.AdditionalUnattendContent, 0) -// for _, v := range input { -// raw := v.(map[string]interface{}) + for _, v := range input { + raw := v.(map[string]interface{}) -// output = append(output, compute.AdditionalUnattendContent{ -// SettingName: compute.SettingNames(raw["setting"].(string)), -// Content: utils.String(raw["content"].(string)), + output = append(output, compute.AdditionalUnattendContent{ + SettingName: compute.SettingNames(raw["setting"].(string)), + Content: utils.String(raw["content"].(string)), -// // no other possible values -// PassName: compute.PassNamesOobeSystem, -// ComponentName: compute.ComponentNamesMicrosoftWindowsShellSetup, -// }) -// } + // no other possible values + PassName: compute.PassNamesOobeSystem, + ComponentName: compute.ComponentNamesMicrosoftWindowsShellSetup, + }) + } -// return &output -// } + return &output +} func ExpandOrchestratedVirtualMachineScaleSetNetworkInterface(input []interface{}) (*[]compute.VirtualMachineScaleSetNetworkConfiguration, error) { output := make([]compute.VirtualMachineScaleSetNetworkConfiguration, 0) @@ -1703,10 +1702,9 @@ func flattenOrchestratedVirtualMachineScaleSetWindowsConfiguration(input *comput output["computer_name_prefix"] = *v } - // I am only commenting this out as this is going to be supported in the next release of the API in October 2021 - // if v := winConfig.AdditionalUnattendContent; v != nil { - // output["additional_unattend_content"] = flattenWindowsConfigurationAdditionalUnattendContent(winConfig) - // } + if v := winConfig.AdditionalUnattendContent; v != nil { + output["additional_unattend_content"] = flattenWindowsConfigurationAdditionalUnattendContent(winConfig, d) + } if v := winConfig.EnableAutomaticUpdates; v != nil { output["enable_automatic_updates"] = *v @@ -1909,24 +1907,38 @@ func FlattenOrchestratedVirtualMachineScaleSetDataDisk(input *[]compute.VirtualM return output } -// I am only commenting this out as this is going to be supported in the next release of the API in October 2021 -// func flattenWindowsConfigurationAdditionalUnattendContent(input *compute.WindowsConfiguration) []interface{} { -// if input == nil { -// return []interface{}{} -// } - -// output := make([]interface{}, 0) -// for _, v := range *input.AdditionalUnattendContent { -// // content isn't returned by the API since it's sensitive data so we need to look it up later -// // where we can pull it out of the state file. -// output = append(output, map[string]interface{}{ -// "content": "", -// "setting": string(v.SettingName), -// }) -// } - -// return output -// } +func flattenWindowsConfigurationAdditionalUnattendContent(input *compute.WindowsConfiguration, d *pluginsdk.ResourceData) []interface{} { + if input == nil { + return []interface{}{} + } + + existing := make([]interface{}, 0) + if v, ok := d.GetOk("os_profile.0.windows_configuration.0.additional_unattend_content"); ok { + existing = v.([]interface{}) + } + + output := make([]interface{}, 0) + for i, v := range *input.AdditionalUnattendContent { + // content isn't returned by the API since it's sensitive data so we need to pull from the state file. + content := "" + if len(existing) > i { + existingVal := existing[i] + existingRaw, ok := existingVal.(map[string]interface{}) + if ok { + contentRaw, ok := existingRaw["content"] + if ok { + content = contentRaw.(string) + } + } + } + output = append(output, map[string]interface{}{ + "content": content, + "setting": string(v.SettingName), + }) + } + + return output +} func FlattenOrchestratedVirtualMachineScaleSetOSDisk(input *compute.VirtualMachineScaleSetOSDisk) []interface{} { if input == nil { diff --git a/internal/services/compute/orchestrated_virtual_machine_scale_set_resource_test.go b/internal/services/compute/orchestrated_virtual_machine_scale_set_resource_test.go index 9f2738dc2ea2..84cebda93045 100644 --- a/internal/services/compute/orchestrated_virtual_machine_scale_set_resource_test.go +++ b/internal/services/compute/orchestrated_virtual_machine_scale_set_resource_test.go @@ -196,6 +196,21 @@ func TestAccOrchestratedVirtualMachineScaleSet_basicWindows(t *testing.T) { }) } +func TestAccOrchestratedVirtualMachineScaleSet_otherAdditionalUnattendContent(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_orchestrated_virtual_machine_scale_set", "test") + r := OrchestratedVirtualMachineScaleSetResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.otherAdditionalUnattendContent(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("os_profile.0.windows_configuration.0.additional_unattend_content.0.setting").HasValue("FirstLogonCommands"), + ), + }, + }) +} + func TestAccOrchestratedVirtualMachineScaleSet_basicWindowsNoTimezone(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_orchestrated_virtual_machine_scale_set", "test") r := OrchestratedVirtualMachineScaleSetResource{} @@ -1000,6 +1015,85 @@ resource "azurerm_orchestrated_virtual_machine_scale_set" "test" { winrm_listener { protocol = "Http" } + + } + } + + network_interface { + name = "TestNetworkProfile-%[1]d" + primary = true + + ip_configuration { + name = "TestIPConfiguration" + primary = true + subnet_id = azurerm_subnet.test.id + + public_ip_address { + name = "TestPublicIPConfiguration" + domain_name_label = "test-domain-label" + idle_timeout_in_minutes = 4 + } + } + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter-Server-Core" + version = "latest" + } +} +`, data.RandomInteger, data.Locations.Primary, r.natgateway_template(data)) +} + +func (OrchestratedVirtualMachineScaleSetResource) otherAdditionalUnattendContent(data acceptance.TestData) string { + r := OrchestratedVirtualMachineScaleSetResource{} + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-OVMSS-%[1]d" + location = "%[2]s" +} + +%[3]s + +resource "azurerm_orchestrated_virtual_machine_scale_set" "test" { + name = "acctestOVMSS-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + sku_name = "Standard_D1_v2" + instances = 2 + + platform_fault_domain_count = 2 + + os_profile { + windows_configuration { + computer_name_prefix = "testvm" + admin_username = "myadmin" + admin_password = "Passwword1234" + + enable_automatic_updates = true + provision_vm_agent = true + timezone = "W. Europe Standard Time" + + winrm_listener { + protocol = "Http" + } + + additional_unattend_content { + setting = "FirstLogonCommands" + content = "shutdown /r /t 0 /c \"initial reboot\"reboot1" + } + } } diff --git a/website/docs/r/orchestrated_virtual_machine_scale_set.html.markdown b/website/docs/r/orchestrated_virtual_machine_scale_set.html.markdown index ab54c46418a6..2760c786ceac 100644 --- a/website/docs/r/orchestrated_virtual_machine_scale_set.html.markdown +++ b/website/docs/r/orchestrated_virtual_machine_scale_set.html.markdown @@ -171,6 +171,8 @@ A `windows_configuration` block supports the following: * `winrm_listener` - (Optional) One or more `winrm_listener` blocks as defined below. Changing this forces a new resource to be created. +* `additional_unattend_content` - (Optional) One or more `additional_unattend_content` blocks as defined below. Changing this forces a new resource to be created. + --- A `linux_configuration` block supports the following: @@ -211,6 +213,14 @@ A `secret` block supports the following: --- +An `additional_unattend_content` block supports the following: + +* `content` - (Required) The XML formatted content that is added to the unattend.xml file for the specified path and component. Changing this forces a new resource to be created. + +* `setting` - (Required) The name of the setting to which the content applies. Possible values are `AutoLogon` and `FirstLogonCommands`. Changing this forces a new resource to be created. + +--- + A (Windows) `certificate` block supports the following: * `store` - (Required) The certificate store on the Virtual Machine where the certificate should be added.