From d5628fae9b73fdc2b5eeb00bf617f2d1ce9e5dc2 Mon Sep 17 00:00:00 2001 From: Yichun Ma Date: Mon, 3 Jul 2023 14:14:03 +0800 Subject: [PATCH 1/3] `azurerm_linux_virtual_machine` `azurerm_windows_virtual_machine` - support `bypass_platform_safety_checks_on_user_schedule_enabled` and `reboot_setting` --- .../compute/linux_virtual_machine_resource.go | 126 +++++++++- ...nux_virtual_machine_resource_other_test.go | 219 ++++++++++++++++++ .../windows_virtual_machine_resource.go | 110 +++++++++ ...ows_virtual_machine_resource_other_test.go | 207 +++++++++++++++++ .../r/linux_virtual_machine.html.markdown | 8 + .../r/windows_virtual_machine.html.markdown | 8 + 6 files changed, 675 insertions(+), 3 deletions(-) diff --git a/internal/services/compute/linux_virtual_machine_resource.go b/internal/services/compute/linux_virtual_machine_resource.go index 7c9f8ead1036..45e7d5a7fcc8 100644 --- a/internal/services/compute/linux_virtual_machine_resource.go +++ b/internal/services/compute/linux_virtual_machine_resource.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" @@ -128,6 +129,12 @@ func resourceLinuxVirtualMachine() *pluginsdk.Resource { "boot_diagnostics": bootDiagnosticsSchema(), + "bypass_platform_safety_checks_on_user_schedule_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "capacity_reservation_group_id": { Type: pluginsdk.TypeString, Optional: true, @@ -282,6 +289,16 @@ func resourceLinuxVirtualMachine() *pluginsdk.Resource { }, }, + "reboot_setting": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(compute.LinuxVMGuestPatchAutomaticByPlatformRebootSettingAlways), + string(compute.LinuxVMGuestPatchAutomaticByPlatformRebootSettingIfRequired), + string(compute.LinuxVMGuestPatchAutomaticByPlatformRebootSettingNever), + }, false), + }, + "secret": linuxSecretSchema(), "secure_boot_enabled": { @@ -517,13 +534,14 @@ func resourceLinuxVirtualMachineCreate(d *pluginsdk.ResourceData, meta interface params.VirtualMachineProperties.LicenseType = utils.String(v.(string)) } - if v, ok := d.GetOk("patch_mode"); ok { - if v.(string) == string(compute.LinuxVMGuestPatchModeAutomaticByPlatform) && !provisionVMAgent { + patchMode := d.Get("patch_mode").(string) + if patchMode != "" { + if patchMode == string(compute.LinuxVMGuestPatchModeAutomaticByPlatform) && !provisionVMAgent { return fmt.Errorf("%q cannot be set to %q when %q is set to %q", "patch_mode", "AutomaticByPlatform", "provision_vm_agent", "false") } params.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings = &compute.LinuxPatchSettings{ - PatchMode: compute.LinuxVMGuestPatchMode(v.(string)), + PatchMode: compute.LinuxVMGuestPatchMode(patchMode), } } @@ -538,6 +556,38 @@ func resourceLinuxVirtualMachineCreate(d *pluginsdk.ResourceData, meta interface params.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AssessmentMode = compute.LinuxPatchAssessmentMode(v.(string)) } + if d.Get("bypass_platform_safety_checks_on_user_schedule_enabled").(bool) { + if patchMode != string(compute.LinuxVMGuestPatchModeAutomaticByPlatform) { + return fmt.Errorf("`patch_mode` must be set to `AutomaticByPlatform` when `bypass_platform_safety_checks_on_user_schedule_enabled` is set to `true`") + } + + if params.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings == nil { + params.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings = &compute.LinuxPatchSettings{} + } + + if params.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AutomaticByPlatformSettings == nil { + params.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AutomaticByPlatformSettings = &compute.LinuxVMGuestPatchAutomaticByPlatformSettings{} + } + + params.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AutomaticByPlatformSettings.BypassPlatformSafetyChecksOnUserSchedule = pointer.To(true) + } + + if v, ok := d.GetOk("reboot_setting"); ok { + if patchMode != string(compute.LinuxVMGuestPatchModeAutomaticByPlatform) { + return fmt.Errorf("`patch_mode` must be set to `AutomaticByPlatform` when `reboot_setting` is specified") + } + + if params.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings == nil { + params.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings = &compute.LinuxPatchSettings{} + } + + if params.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AutomaticByPlatformSettings == nil { + params.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AutomaticByPlatformSettings = &compute.LinuxVMGuestPatchAutomaticByPlatformSettings{} + } + + params.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AutomaticByPlatformSettings.RebootSetting = compute.LinuxVMGuestPatchAutomaticByPlatformRebootSetting(v.(string)) + } + secureBootEnabled := d.Get("secure_boot_enabled").(bool) vtpmEnabled := d.Get("vtpm_enabled").(bool) if securityEncryptionType != "" { @@ -842,6 +892,19 @@ func resourceLinuxVirtualMachineRead(d *pluginsdk.ResourceData, meta interface{} assessmentMode = string(patchSettings.AssessmentMode) } d.Set("patch_assessment_mode", assessmentMode) + + bypassPlatformSafetyChecksOnUserScheduleEnabled := false + rebootSetting := "" + if patchSettings := config.PatchSettings; patchSettings != nil && patchSettings.AutomaticByPlatformSettings != nil { + if v := patchSettings.AutomaticByPlatformSettings.BypassPlatformSafetyChecksOnUserSchedule; v != nil { + bypassPlatformSafetyChecksOnUserScheduleEnabled = pointer.From(v) + } + if v := patchSettings.AutomaticByPlatformSettings.RebootSetting; v != "" { + rebootSetting = string(v) + } + } + d.Set("bypass_platform_safety_checks_on_user_schedule_enabled", bypassPlatformSafetyChecksOnUserScheduleEnabled) + d.Set("reboot_setting", rebootSetting) } if err := d.Set("secret", flattenLinuxSecrets(profile.Secrets)); err != nil { @@ -1243,6 +1306,63 @@ func resourceLinuxVirtualMachineUpdate(d *pluginsdk.ResourceData, meta interface update.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AssessmentMode = compute.LinuxPatchAssessmentMode(assessmentMode) } + isPatchModeAutomaticByPlatform := d.Get("patch_mode") == string(compute.LinuxVMGuestPatchModeAutomaticByPlatform) + bypassPlatformSafetyChecksOnUserScheduleEnabled := d.Get("bypass_platform_safety_checks_on_user_schedule_enabled").(bool) + if bypassPlatformSafetyChecksOnUserScheduleEnabled && !isPatchModeAutomaticByPlatform { + return fmt.Errorf("`patch_mode` must be set to `AutomaticByPlatform` when `bypass_platform_safety_checks_on_user_schedule_enabled` is set to `true`") + } + if d.HasChange("bypass_platform_safety_checks_on_user_schedule_enabled") { + shouldUpdate = true + + if update.VirtualMachineProperties.OsProfile == nil { + update.VirtualMachineProperties.OsProfile = &compute.OSProfile{} + } + + if update.VirtualMachineProperties.OsProfile.LinuxConfiguration == nil { + update.VirtualMachineProperties.OsProfile.LinuxConfiguration = &compute.LinuxConfiguration{} + } + + if update.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings == nil { + update.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings = &compute.LinuxPatchSettings{} + } + + if isPatchModeAutomaticByPlatform { + if update.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AutomaticByPlatformSettings == nil { + update.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AutomaticByPlatformSettings = &compute.LinuxVMGuestPatchAutomaticByPlatformSettings{} + } + + update.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AutomaticByPlatformSettings.BypassPlatformSafetyChecksOnUserSchedule = pointer.To(bypassPlatformSafetyChecksOnUserScheduleEnabled) + } + } + + rebootSetting := d.Get("reboot_setting").(string) + if rebootSetting != "" && !isPatchModeAutomaticByPlatform { + return fmt.Errorf("`patch_mode` must be set to `AutomaticByPlatform` when `reboot_setting` is specified") + } + if d.HasChange("reboot_setting") { + shouldUpdate = true + + if update.VirtualMachineProperties.OsProfile == nil { + update.VirtualMachineProperties.OsProfile = &compute.OSProfile{} + } + + if update.VirtualMachineProperties.OsProfile.LinuxConfiguration == nil { + update.VirtualMachineProperties.OsProfile.LinuxConfiguration = &compute.LinuxConfiguration{} + } + + if update.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings == nil { + update.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings = &compute.LinuxPatchSettings{} + } + + if isPatchModeAutomaticByPlatform { + if update.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AutomaticByPlatformSettings == nil { + update.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AutomaticByPlatformSettings = &compute.LinuxVMGuestPatchAutomaticByPlatformSettings{} + } + + update.VirtualMachineProperties.OsProfile.LinuxConfiguration.PatchSettings.AutomaticByPlatformSettings.RebootSetting = compute.LinuxVMGuestPatchAutomaticByPlatformRebootSetting(rebootSetting) + } + } + if d.HasChange("allow_extension_operations") { allowExtensionOperations := d.Get("allow_extension_operations").(bool) diff --git a/internal/services/compute/linux_virtual_machine_resource_other_test.go b/internal/services/compute/linux_virtual_machine_resource_other_test.go index 14215ed30a0d..eb5748265753 100644 --- a/internal/services/compute/linux_virtual_machine_resource_other_test.go +++ b/internal/services/compute/linux_virtual_machine_resource_other_test.go @@ -822,6 +822,79 @@ func TestAccLinuxVirtualMachine_otherPatchAssessmentModeUpdate(t *testing.T) { }) } +func TestAccLinuxVirtualMachine_otherBypassPlatformSafetyChecksOnUserSchedule(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine", "test") + r := LinuxVirtualMachineResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.otherBypassPlatformSafetyChecksOnUserSchedule(data, false), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("bypass_platform_safety_checks_on_user_schedule_enabled").HasValue("false"), + ), + }, + data.ImportStep(), + { + Config: r.otherBypassPlatformSafetyChecksOnUserSchedule(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.otherBypassPlatformSafetyChecksOnUserScheduleRemoved(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.otherBypassPlatformSafetyChecksOnUserSchedule(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccLinuxVirtualMachine_otherRebootSetting(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine", "test") + r := LinuxVirtualMachineResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.otherRebootSetting(data, "Always"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.otherRebootSetting(data, "IfRequired"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.otherRebootSettingRemoved(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.otherRebootSetting(data, "Never"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (r LinuxVirtualMachineResource) otherPatchMode(data acceptance.TestData, patchMode string) string { return fmt.Sprintf(` %s @@ -2638,3 +2711,149 @@ resource "azurerm_linux_virtual_machine" "test" { } `, r.template(data), data.RandomInteger) } + +func (r LinuxVirtualMachineResource) otherBypassPlatformSafetyChecksOnUserSchedule(data acceptance.TestData, enabled bool) string { + return fmt.Sprintf(` +%s + +resource "azurerm_linux_virtual_machine" "test" { + name = "acctestVM-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + admin_ssh_key { + username = "adminuser" + public_key = local.first_public_key + } + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + patch_mode = "AutomaticByPlatform" + bypass_platform_safety_checks_on_user_schedule_enabled = %t +} +`, r.template(data), data.RandomInteger, enabled) +} + +func (r LinuxVirtualMachineResource) otherBypassPlatformSafetyChecksOnUserScheduleRemoved(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_linux_virtual_machine" "test" { + name = "acctestVM-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + admin_ssh_key { + username = "adminuser" + public_key = local.first_public_key + } + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + patch_mode = "ImageDefault" +} +`, r.template(data), data.RandomInteger) +} + +func (r LinuxVirtualMachineResource) otherRebootSetting(data acceptance.TestData, rebootSetting string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_linux_virtual_machine" "test" { + name = "acctestVM-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + admin_ssh_key { + username = "adminuser" + public_key = local.first_public_key + } + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + patch_mode = "AutomaticByPlatform" + reboot_setting = "%s" +} +`, r.template(data), data.RandomInteger, rebootSetting) +} + +func (r LinuxVirtualMachineResource) otherRebootSettingRemoved(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_linux_virtual_machine" "test" { + name = "acctestVM-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + admin_ssh_key { + username = "adminuser" + public_key = local.first_public_key + } + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + patch_mode = "ImageDefault" +} +`, r.template(data), data.RandomInteger) +} diff --git a/internal/services/compute/windows_virtual_machine_resource.go b/internal/services/compute/windows_virtual_machine_resource.go index 94545e420da7..77004de6f576 100644 --- a/internal/services/compute/windows_virtual_machine_resource.go +++ b/internal/services/compute/windows_virtual_machine_resource.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" @@ -129,6 +130,12 @@ func resourceWindowsVirtualMachine() *pluginsdk.Resource { "boot_diagnostics": bootDiagnosticsSchema(), + "bypass_platform_safety_checks_on_user_schedule_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "capacity_reservation_group_id": { Type: pluginsdk.TypeString, Optional: true, @@ -299,6 +306,16 @@ func resourceWindowsVirtualMachine() *pluginsdk.Resource { }, }, + "reboot_setting": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(compute.WindowsVMGuestPatchAutomaticByPlatformRebootSettingAlways), + string(compute.WindowsVMGuestPatchAutomaticByPlatformRebootSettingIfRequired), + string(compute.WindowsVMGuestPatchAutomaticByPlatformRebootSettingNever), + }, false), + }, + "secret": windowsSecretSchema(), "secure_boot_enabled": { @@ -585,6 +602,30 @@ func resourceWindowsVirtualMachineCreate(d *pluginsdk.ResourceData, meta interfa AssessmentMode: compute.WindowsPatchAssessmentMode(assessmentMode), } + if d.Get("bypass_platform_safety_checks_on_user_schedule_enabled").(bool) { + if patchMode != string(compute.WindowsVMGuestPatchModeAutomaticByPlatform) { + return fmt.Errorf("`patch_mode` must be set to `AutomaticByPlatform` when `bypass_platform_safety_checks_on_user_schedule_enabled` is set to `true`") + } + + if params.OsProfile.WindowsConfiguration.PatchSettings.AutomaticByPlatformSettings == nil { + params.OsProfile.WindowsConfiguration.PatchSettings.AutomaticByPlatformSettings = &compute.WindowsVMGuestPatchAutomaticByPlatformSettings{} + } + + params.OsProfile.WindowsConfiguration.PatchSettings.AutomaticByPlatformSettings.BypassPlatformSafetyChecksOnUserSchedule = pointer.To(true) + } + + if v, ok := d.GetOk("reboot_setting"); ok { + if patchMode != string(compute.WindowsVMGuestPatchModeAutomaticByPlatform) { + return fmt.Errorf("`patch_mode` must be set to `AutomaticByPlatform` when `reboot_setting` is specified") + } + + if params.OsProfile.WindowsConfiguration.PatchSettings.AutomaticByPlatformSettings == nil { + params.OsProfile.WindowsConfiguration.PatchSettings.AutomaticByPlatformSettings = &compute.WindowsVMGuestPatchAutomaticByPlatformSettings{} + } + + params.OsProfile.WindowsConfiguration.PatchSettings.AutomaticByPlatformSettings.RebootSetting = compute.WindowsVMGuestPatchAutomaticByPlatformRebootSetting(v.(string)) + } + if v, ok := d.GetOk("availability_set_id"); ok { params.AvailabilitySet = &compute.SubResource{ ID: utils.String(v.(string)), @@ -877,16 +918,28 @@ func resourceWindowsVirtualMachineRead(d *pluginsdk.ResourceData, meta interface d.Set("provision_vm_agent", config.ProvisionVMAgent) assessmentMode := string(compute.WindowsPatchAssessmentModeImageDefault) + bypassPlatformSafetyChecksOnUserScheduleEnabled := false + rebootSetting := "" if patchSettings := config.PatchSettings; patchSettings != nil { d.Set("patch_mode", patchSettings.PatchMode) d.Set("hotpatching_enabled", patchSettings.EnableHotpatching) + if patchSettings.AutomaticByPlatformSettings != nil { + if v := patchSettings.AutomaticByPlatformSettings.BypassPlatformSafetyChecksOnUserSchedule; v != nil { + bypassPlatformSafetyChecksOnUserScheduleEnabled = pointer.From(v) + } + if v := patchSettings.AutomaticByPlatformSettings.RebootSetting; v != "" { + rebootSetting = string(v) + } + } if patchSettings.AssessmentMode != "" { assessmentMode = string(patchSettings.AssessmentMode) } } d.Set("patch_assessment_mode", assessmentMode) + d.Set("bypass_platform_safety_checks_on_user_schedule_enabled", bypassPlatformSafetyChecksOnUserScheduleEnabled) + d.Set("reboot_setting", rebootSetting) d.Set("timezone", config.TimeZone) @@ -1112,6 +1165,63 @@ func resourceWindowsVirtualMachineUpdate(d *pluginsdk.ResourceData, meta interfa update.OsProfile.WindowsConfiguration.PatchSettings.AssessmentMode = compute.WindowsPatchAssessmentMode(assessmentMode) } + isPatchModeAutomaticByPlatform := d.Get("patch_mode") == string(compute.WindowsVMGuestPatchModeAutomaticByPlatform) + bypassPlatformSafetyChecksOnUserScheduleEnabled := d.Get("bypass_platform_safety_checks_on_user_schedule_enabled").(bool) + if bypassPlatformSafetyChecksOnUserScheduleEnabled && !isPatchModeAutomaticByPlatform { + return fmt.Errorf("`patch_mode` must be set to `AutomaticByPlatform` when `bypass_platform_safety_checks_on_user_schedule_enabled` is set to `true`") + } + if d.HasChange("bypass_platform_safety_checks_on_user_schedule_enabled") { + shouldUpdate = true + + if update.OsProfile == nil { + update.OsProfile = &compute.OSProfile{} + } + + if update.OsProfile.WindowsConfiguration == nil { + update.OsProfile.WindowsConfiguration = &compute.WindowsConfiguration{} + } + + if update.OsProfile.WindowsConfiguration.PatchSettings == nil { + update.OsProfile.WindowsConfiguration.PatchSettings = &compute.PatchSettings{} + } + + if isPatchModeAutomaticByPlatform { + if update.OsProfile.WindowsConfiguration.PatchSettings.AutomaticByPlatformSettings == nil { + update.OsProfile.WindowsConfiguration.PatchSettings.AutomaticByPlatformSettings = &compute.WindowsVMGuestPatchAutomaticByPlatformSettings{} + } + + update.OsProfile.WindowsConfiguration.PatchSettings.AutomaticByPlatformSettings.BypassPlatformSafetyChecksOnUserSchedule = pointer.To(bypassPlatformSafetyChecksOnUserScheduleEnabled) + } + } + + rebootSetting := d.Get("reboot_setting").(string) + if rebootSetting != "" && !isPatchModeAutomaticByPlatform { + return fmt.Errorf("`patch_mode` must be set to `AutomaticByPlatform` when `reboot_setting` is specified") + } + if d.HasChange("reboot_setting") { + shouldUpdate = true + + if update.OsProfile == nil { + update.OsProfile = &compute.OSProfile{} + } + + if update.OsProfile.WindowsConfiguration == nil { + update.OsProfile.WindowsConfiguration = &compute.WindowsConfiguration{} + } + + if update.OsProfile.WindowsConfiguration.PatchSettings == nil { + update.OsProfile.WindowsConfiguration.PatchSettings = &compute.PatchSettings{} + } + + if isPatchModeAutomaticByPlatform { + if update.VirtualMachineProperties.OsProfile.WindowsConfiguration.PatchSettings.AutomaticByPlatformSettings == nil { + update.VirtualMachineProperties.OsProfile.WindowsConfiguration.PatchSettings.AutomaticByPlatformSettings = &compute.WindowsVMGuestPatchAutomaticByPlatformSettings{} + } + + update.VirtualMachineProperties.OsProfile.WindowsConfiguration.PatchSettings.AutomaticByPlatformSettings.RebootSetting = compute.WindowsVMGuestPatchAutomaticByPlatformRebootSetting(rebootSetting) + } + } + if d.HasChange("hotpatching_enabled") { shouldUpdate = true diff --git a/internal/services/compute/windows_virtual_machine_resource_other_test.go b/internal/services/compute/windows_virtual_machine_resource_other_test.go index d4330fd2dc75..8054971e6752 100644 --- a/internal/services/compute/windows_virtual_machine_resource_other_test.go +++ b/internal/services/compute/windows_virtual_machine_resource_other_test.go @@ -1079,6 +1079,79 @@ func TestAccWindowsVirtualMachine_otherGracefulShutdownEnabled(t *testing.T) { }) } +func TestAccWindowsVirtualMachine_otherBypassPlatformSafetyChecksOnUserSchedule(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_virtual_machine", "test") + r := WindowsVirtualMachineResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.otherBypassPlatformSafetyChecksOnUserSchedule(data, false), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("bypass_platform_safety_checks_on_user_schedule_enabled").HasValue("false"), + ), + }, + data.ImportStep("admin_password"), + { + Config: r.otherBypassPlatformSafetyChecksOnUserSchedule(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("admin_password"), + { + Config: r.otherBypassPlatformSafetyChecksOnUserScheduleRemoved(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("admin_password"), + { + Config: r.otherBypassPlatformSafetyChecksOnUserSchedule(data, true), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("admin_password"), + }) +} + +func TestAccWindowsVirtualMachine_otherRebootSetting(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_virtual_machine", "test") + r := WindowsVirtualMachineResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.otherRebootSetting(data, "Always"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("admin_password"), + { + Config: r.otherRebootSetting(data, "IfRequired"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("admin_password"), + { + Config: r.otherRebootSettingRemoved(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("admin_password"), + { + Config: r.otherRebootSetting(data, "Never"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("admin_password"), + }) +} + func (r WindowsVirtualMachineResource) otherHotpatching(data acceptance.TestData, hotPatch bool) string { return fmt.Sprintf(` %s @@ -3209,3 +3282,137 @@ resource "azurerm_windows_virtual_machine" "test" { } `, data.RandomString, gracefulShutdown, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) } + +func (r WindowsVirtualMachineResource) otherBypassPlatformSafetyChecksOnUserSchedule(data acceptance.TestData, enabled bool) string { + return fmt.Sprintf(` +%s + +resource "azurerm_windows_virtual_machine" "test" { + name = local.vm_name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@$$w0rd1234!" + + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter" + version = "latest" + } + + patch_mode = "AutomaticByPlatform" + bypass_platform_safety_checks_on_user_schedule_enabled = %t +} +`, r.template(data), enabled) +} + +func (r WindowsVirtualMachineResource) otherBypassPlatformSafetyChecksOnUserScheduleRemoved(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_windows_virtual_machine" "test" { + name = local.vm_name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@$$w0rd1234!" + + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter" + version = "latest" + } + + patch_mode = "AutomaticByOS" +} +`, r.template(data)) +} + +func (r WindowsVirtualMachineResource) otherRebootSetting(data acceptance.TestData, rebootSetting string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_windows_virtual_machine" "test" { + name = local.vm_name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@$$w0rd1234!" + + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter" + version = "latest" + } + + patch_mode = "AutomaticByPlatform" + reboot_setting = "%s" +} +`, r.template(data), rebootSetting) +} + +func (r WindowsVirtualMachineResource) otherRebootSettingRemoved(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_windows_virtual_machine" "test" { + name = local.vm_name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@$$w0rd1234!" + + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter" + version = "latest" + } + + patch_mode = "AutomaticByOS" +} +`, r.template(data)) +} diff --git a/website/docs/r/linux_virtual_machine.html.markdown b/website/docs/r/linux_virtual_machine.html.markdown index d81c5a2c70c2..ff6784bdbdd8 100644 --- a/website/docs/r/linux_virtual_machine.html.markdown +++ b/website/docs/r/linux_virtual_machine.html.markdown @@ -126,6 +126,10 @@ The following arguments are supported: * `boot_diagnostics` - (Optional) A `boot_diagnostics` block as defined below. +* `bypass_platform_safety_checks_on_user_schedule_enabled` - (Optional) Specifies whether to skip platform scheduled patch when user schedule is associated to the VM. Defaults to `false`. + +~> **NOTE:** `bypass_platform_safety_checks_on_user_schedule_enabled` can only be set to `true` when `patch_mode` is set to `AutomaticByPlatform`. + * `capacity_reservation_group_id` - (Optional) Specifies the ID of the Capacity Reservation Group which the Virtual Machine should be allocated to. ~> **NOTE:** `capacity_reservation_group_id` cannot be used with `availability_set_id` or `proximity_placement_group_id` @@ -182,6 +186,10 @@ The following arguments are supported: * `proximity_placement_group_id` - (Optional) The ID of the Proximity Placement Group which the Virtual Machine should be assigned to. +* `reboot_setting` - (Optional) Specifies the reboot setting for platform scheduled patch. Possible values are `Always`, `IfRequired` and `Never`. + +~> **NOTE:** `reboot_setting` can only be set when `patch_mode` is set to `AutomaticByPlatform`. + * `secret` - (Optional) One or more `secret` blocks as defined below. * `secure_boot_enabled` - (Optional) Specifies whether secure boot should be enabled on the virtual machine. Changing this forces a new resource to be created. diff --git a/website/docs/r/windows_virtual_machine.html.markdown b/website/docs/r/windows_virtual_machine.html.markdown index 87f4b33d6383..9319b3d0c751 100644 --- a/website/docs/r/windows_virtual_machine.html.markdown +++ b/website/docs/r/windows_virtual_machine.html.markdown @@ -119,6 +119,10 @@ The following arguments are supported: * `boot_diagnostics` - (Optional) A `boot_diagnostics` block as defined below. +* `bypass_platform_safety_checks_on_user_schedule_enabled` - (Optional) Specifies whether to skip platform scheduled patch when user schedule is associated to the VM. Defaults to `false`. + +~> **NOTE:** `bypass_platform_safety_checks_on_user_schedule_enabled` can only be set to `true` when `patch_mode` is set to `AutomaticByPlatform`. + * `capacity_reservation_group_id` - (Optional) Specifies the ID of the Capacity Reservation Group which the Virtual Machine should be allocated to. ~> **NOTE:** `capacity_reservation_group_id` cannot be used with `availability_set_id` or `proximity_placement_group_id` @@ -177,6 +181,10 @@ The following arguments are supported: * `proximity_placement_group_id` - (Optional) The ID of the Proximity Placement Group which the Virtual Machine should be assigned to. +* `reboot_setting` - (Optional) Specifies the reboot setting for platform scheduled patch. Possible values are `Always`, `IfRequired` and `Never`. + +~> **NOTE:** `reboot_setting` can only be set when `patch_mode` is set to `AutomaticByPlatform`. + * `secret` - (Optional) One or more `secret` blocks as defined below. * `secure_boot_enabled` - (Optional) Specifies if Secure Boot and Trusted Launch is enabled for the Virtual Machine. Changing this forces a new resource to be created. From 9e99c179ad96c52deabe0d432ff2b208bc2dc002 Mon Sep 17 00:00:00 2001 From: Yichun Ma Date: Thu, 27 Jul 2023 15:01:40 +0800 Subject: [PATCH 2/3] simplify code --- .../services/compute/linux_virtual_machine_resource.go | 8 ++------ .../services/compute/windows_virtual_machine_resource.go | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/internal/services/compute/linux_virtual_machine_resource.go b/internal/services/compute/linux_virtual_machine_resource.go index 45e7d5a7fcc8..bfeee988a61e 100644 --- a/internal/services/compute/linux_virtual_machine_resource.go +++ b/internal/services/compute/linux_virtual_machine_resource.go @@ -896,12 +896,8 @@ func resourceLinuxVirtualMachineRead(d *pluginsdk.ResourceData, meta interface{} bypassPlatformSafetyChecksOnUserScheduleEnabled := false rebootSetting := "" if patchSettings := config.PatchSettings; patchSettings != nil && patchSettings.AutomaticByPlatformSettings != nil { - if v := patchSettings.AutomaticByPlatformSettings.BypassPlatformSafetyChecksOnUserSchedule; v != nil { - bypassPlatformSafetyChecksOnUserScheduleEnabled = pointer.From(v) - } - if v := patchSettings.AutomaticByPlatformSettings.RebootSetting; v != "" { - rebootSetting = string(v) - } + bypassPlatformSafetyChecksOnUserScheduleEnabled = pointer.From(patchSettings.AutomaticByPlatformSettings.BypassPlatformSafetyChecksOnUserSchedule) + rebootSetting = string(patchSettings.AutomaticByPlatformSettings.RebootSetting) } d.Set("bypass_platform_safety_checks_on_user_schedule_enabled", bypassPlatformSafetyChecksOnUserScheduleEnabled) d.Set("reboot_setting", rebootSetting) diff --git a/internal/services/compute/windows_virtual_machine_resource.go b/internal/services/compute/windows_virtual_machine_resource.go index 77004de6f576..3eb73f600f3f 100644 --- a/internal/services/compute/windows_virtual_machine_resource.go +++ b/internal/services/compute/windows_virtual_machine_resource.go @@ -925,12 +925,8 @@ func resourceWindowsVirtualMachineRead(d *pluginsdk.ResourceData, meta interface d.Set("hotpatching_enabled", patchSettings.EnableHotpatching) if patchSettings.AutomaticByPlatformSettings != nil { - if v := patchSettings.AutomaticByPlatformSettings.BypassPlatformSafetyChecksOnUserSchedule; v != nil { - bypassPlatformSafetyChecksOnUserScheduleEnabled = pointer.From(v) - } - if v := patchSettings.AutomaticByPlatformSettings.RebootSetting; v != "" { - rebootSetting = string(v) - } + bypassPlatformSafetyChecksOnUserScheduleEnabled = pointer.From(patchSettings.AutomaticByPlatformSettings.BypassPlatformSafetyChecksOnUserSchedule) + rebootSetting = string(patchSettings.AutomaticByPlatformSettings.RebootSetting) } if patchSettings.AssessmentMode != "" { assessmentMode = string(patchSettings.AssessmentMode) From 176c6067127d441f869ca9a36ec0d97ddd0ef098 Mon Sep 17 00:00:00 2001 From: Yichun Ma Date: Thu, 27 Jul 2023 15:03:39 +0800 Subject: [PATCH 3/3] update doc --- website/docs/r/linux_virtual_machine.html.markdown | 4 ++-- website/docs/r/windows_virtual_machine.html.markdown | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/website/docs/r/linux_virtual_machine.html.markdown b/website/docs/r/linux_virtual_machine.html.markdown index ff6784bdbdd8..84c2e7768043 100644 --- a/website/docs/r/linux_virtual_machine.html.markdown +++ b/website/docs/r/linux_virtual_machine.html.markdown @@ -126,7 +126,7 @@ The following arguments are supported: * `boot_diagnostics` - (Optional) A `boot_diagnostics` block as defined below. -* `bypass_platform_safety_checks_on_user_schedule_enabled` - (Optional) Specifies whether to skip platform scheduled patch when user schedule is associated to the VM. Defaults to `false`. +* `bypass_platform_safety_checks_on_user_schedule_enabled` - (Optional) Specifies whether to skip platform scheduled patching when a user schedule is associated with the VM. Defaults to `false`. ~> **NOTE:** `bypass_platform_safety_checks_on_user_schedule_enabled` can only be set to `true` when `patch_mode` is set to `AutomaticByPlatform`. @@ -186,7 +186,7 @@ The following arguments are supported: * `proximity_placement_group_id` - (Optional) The ID of the Proximity Placement Group which the Virtual Machine should be assigned to. -* `reboot_setting` - (Optional) Specifies the reboot setting for platform scheduled patch. Possible values are `Always`, `IfRequired` and `Never`. +* `reboot_setting` - (Optional) Specifies the reboot setting for platform scheduled patching. Possible values are `Always`, `IfRequired` and `Never`. ~> **NOTE:** `reboot_setting` can only be set when `patch_mode` is set to `AutomaticByPlatform`. diff --git a/website/docs/r/windows_virtual_machine.html.markdown b/website/docs/r/windows_virtual_machine.html.markdown index 9319b3d0c751..d326f7879cc0 100644 --- a/website/docs/r/windows_virtual_machine.html.markdown +++ b/website/docs/r/windows_virtual_machine.html.markdown @@ -119,7 +119,7 @@ The following arguments are supported: * `boot_diagnostics` - (Optional) A `boot_diagnostics` block as defined below. -* `bypass_platform_safety_checks_on_user_schedule_enabled` - (Optional) Specifies whether to skip platform scheduled patch when user schedule is associated to the VM. Defaults to `false`. +* `bypass_platform_safety_checks_on_user_schedule_enabled` - (Optional) Specifies whether to skip platform scheduled patching when a user schedule is associated with the VM. Defaults to `false`. ~> **NOTE:** `bypass_platform_safety_checks_on_user_schedule_enabled` can only be set to `true` when `patch_mode` is set to `AutomaticByPlatform`. @@ -181,7 +181,7 @@ The following arguments are supported: * `proximity_placement_group_id` - (Optional) The ID of the Proximity Placement Group which the Virtual Machine should be assigned to. -* `reboot_setting` - (Optional) Specifies the reboot setting for platform scheduled patch. Possible values are `Always`, `IfRequired` and `Never`. +* `reboot_setting` - (Optional) Specifies the reboot setting for platform scheduled patching. Possible values are `Always`, `IfRequired` and `Never`. ~> **NOTE:** `reboot_setting` can only be set when `patch_mode` is set to `AutomaticByPlatform`.