From bae54cd2277b47f4703d95b18a50ea1145219a02 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 8 Oct 2019 11:25:54 +0200 Subject: [PATCH] New Resource: `azurerm_windows_virtual_machine_scale_set` --- azurerm/helpers/validate/compute.go | 12 +- azurerm/helpers/validate/compute_test.go | 2 +- .../internal/services/compute/validation.go | 4 + .../services/compute/validation_test.go | 68 + .../compute/virtual_machine_scale_set.go | 4 +- azurerm/provider.go | 1 + azurerm/resource_arm_dev_test_lab_schedule.go | 2 +- azurerm/resource_arm_virtual_machine.go | 2 +- ...e_arm_windows_virtual_machine_scale_set.go | 1275 +++++++++++ ...ows_virtual_machine_scale_set_auth_test.go | 79 + ...irtual_machine_scale_set_disk_data_test.go | 798 +++++++ ..._virtual_machine_scale_set_disk_os_test.go | 489 +++++ ...virtual_machine_scale_set_identity_test.go | 463 ++++ ...s_virtual_machine_scale_set_images_test.go | 795 +++++++ ..._virtual_machine_scale_set_network_test.go | 1932 +++++++++++++++++ ...ws_virtual_machine_scale_set_other_test.go | 1884 ++++++++++++++++ ..._virtual_machine_scale_set_scaling_test.go | 941 ++++++++ ..._windows_virtual_machine_scale_set_test.go | 93 + ...ux_virtual_machine_scale_set.html.markdown | 8 +- .../r/virtual_machine_scale_set.html.markdown | 6 + ...ws_virtual_machine_scale_set.html.markdown | 405 ++++ 21 files changed, 9255 insertions(+), 8 deletions(-) create mode 100644 azurerm/resource_arm_windows_virtual_machine_scale_set.go create mode 100644 azurerm/resource_arm_windows_virtual_machine_scale_set_auth_test.go create mode 100644 azurerm/resource_arm_windows_virtual_machine_scale_set_disk_data_test.go create mode 100644 azurerm/resource_arm_windows_virtual_machine_scale_set_disk_os_test.go create mode 100644 azurerm/resource_arm_windows_virtual_machine_scale_set_identity_test.go create mode 100644 azurerm/resource_arm_windows_virtual_machine_scale_set_images_test.go create mode 100644 azurerm/resource_arm_windows_virtual_machine_scale_set_network_test.go create mode 100644 azurerm/resource_arm_windows_virtual_machine_scale_set_other_test.go create mode 100644 azurerm/resource_arm_windows_virtual_machine_scale_set_scaling_test.go create mode 100644 azurerm/resource_arm_windows_virtual_machine_scale_set_test.go create mode 100644 website/docs/r/windows_virtual_machine_scale_set.html.markdown diff --git a/azurerm/helpers/validate/compute.go b/azurerm/helpers/validate/compute.go index 42ce5ff13900..171bc27d2ee9 100644 --- a/azurerm/helpers/validate/compute.go +++ b/azurerm/helpers/validate/compute.go @@ -52,7 +52,17 @@ func SharedImageVersionName(v interface{}, k string) (warnings []string, errors return warnings, errors } +// VirtualMachineTimeZone returns a case-sensitive validation function for the Time Zones for a Virtual Machine func VirtualMachineTimeZone() schema.SchemaValidateFunc { + return virtualMachineTimeZone(false) +} + +// VirtualMachineTimeZone returns a case-insensitive validation function for the Time Zones for a Virtual Machine +func VirtualMachineTimeZoneCaseInsensitive() schema.SchemaValidateFunc { + return virtualMachineTimeZone(true) +} + +func virtualMachineTimeZone(ignoreCase bool) schema.SchemaValidateFunc { // Candidates are listed here: http://jackstromberg.com/2017/01/list-of-time-zones-consumed-by-azure/ candidates := []string{ "", @@ -163,5 +173,5 @@ func VirtualMachineTimeZone() schema.SchemaValidateFunc { "West Pacific Standard Time", "Yakutsk Standard Time", } - return validation.StringInSlice(candidates, true) + return validation.StringInSlice(candidates, ignoreCase) } diff --git a/azurerm/helpers/validate/compute_test.go b/azurerm/helpers/validate/compute_test.go index eda028d7cb08..72095f8200c1 100644 --- a/azurerm/helpers/validate/compute_test.go +++ b/azurerm/helpers/validate/compute_test.go @@ -205,7 +205,7 @@ func TestVirtualMachineTimeZone(t *testing.T) { } for _, tc := range cases { - _, errors := VirtualMachineTimeZone()(tc.Value, "unittest") + _, errors := VirtualMachineTimeZoneCaseInsensitive()(tc.Value, "unittest") if len(errors) != tc.Errors { t.Fatalf("Expected VirtualMachineTimeZone to trigger '%d' errors for '%s' - got '%d'", tc.Errors, tc.Value, len(errors)) diff --git a/azurerm/internal/services/compute/validation.go b/azurerm/internal/services/compute/validation.go index b3708f8c0cbc..0eef4b74aaca 100644 --- a/azurerm/internal/services/compute/validation.go +++ b/azurerm/internal/services/compute/validation.go @@ -10,6 +10,10 @@ func ValidateLinuxName(i interface{}, k string) (warnings []string, errors []err return validateName(64)(i, k) } +func ValidateWindowsName(i interface{}, k string) (warnings []string, errors []error) { + return validateName(16)(i, k) +} + func validateName(maxLength int) func(i interface{}, k string) (warnings []string, errors []error) { return func(i interface{}, k string) (warnings []string, errors []error) { v, ok := i.(string) diff --git a/azurerm/internal/services/compute/validation_test.go b/azurerm/internal/services/compute/validation_test.go index 50fe6ee5549a..d57e97578d15 100644 --- a/azurerm/internal/services/compute/validation_test.go +++ b/azurerm/internal/services/compute/validation_test.go @@ -69,3 +69,71 @@ func TestValidateLinuxName(t *testing.T) { } } } + +func TestValidateWindowsName(t *testing.T) { + testData := []struct { + input string + expected bool + }{ + { + // empty + input: "", + expected: false, + }, + { + // basic example + input: "hello", + expected: true, + }, + { + // can't start with an underscore + input: "_hello", + expected: false, + }, + { + // can't end with a dash + input: "hello-", + expected: false, + }, + { + // can't contain an exclamation mark + input: "hello!", + expected: false, + }, + { + // dash in the middle + input: "malcolm-middle", + expected: true, + }, + { + // can't end with a period + input: "hello.", + expected: false, + }, + { + // 14 chars + input: "abcdefghijklmn", + expected: true, + }, + { + // 15 chars + input: "abcdefghijklmno", + expected: true, + }, + { + // 16 chars + input: "abcdefghijklmnop", + expected: false, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + + _, errors := ValidateWindowsName(v.input, "name") + actual := len(errors) == 0 + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} diff --git a/azurerm/internal/services/compute/virtual_machine_scale_set.go b/azurerm/internal/services/compute/virtual_machine_scale_set.go index 5dcec627ba58..b8fc8c3856ba 100644 --- a/azurerm/internal/services/compute/virtual_machine_scale_set.go +++ b/azurerm/internal/services/compute/virtual_machine_scale_set.go @@ -161,7 +161,7 @@ func VirtualMachineScaleSetIdentitySchema() *schema.Schema { }, "identity_ids": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, @@ -191,7 +191,7 @@ func ExpandVirtualMachineScaleSetIdentity(input []interface{}) (*compute.Virtual Type: compute.ResourceIdentityType(raw["type"].(string)), } - identityIdsRaw := raw["identity_ids"].([]interface{}) + identityIdsRaw := raw["identity_ids"].(*schema.Set).List() identityIds := make(map[string]*compute.VirtualMachineScaleSetIdentityUserAssignedIdentitiesValue) for _, v := range identityIdsRaw { identityIds[v.(string)] = &compute.VirtualMachineScaleSetIdentityUserAssignedIdentitiesValue{} diff --git a/azurerm/provider.go b/azurerm/provider.go index a6b1d37bc4f3..051a7763b0cb 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -459,6 +459,7 @@ func Provider() terraform.ResourceProvider { // 2.0 resources if features.SupportsTwoPointZeroResources() { resources["azurerm_linux_virtual_machine_scale_set"] = resourceArmLinuxVirtualMachineScaleSet() + resources["azurerm_windows_virtual_machine_scale_set"] = resourceArmWindowsVirtualMachineScaleSet() } // avoids this showing up in test output diff --git a/azurerm/resource_arm_dev_test_lab_schedule.go b/azurerm/resource_arm_dev_test_lab_schedule.go index 110d006ac0c8..0e1ff7bc68f0 100644 --- a/azurerm/resource_arm_dev_test_lab_schedule.go +++ b/azurerm/resource_arm_dev_test_lab_schedule.go @@ -132,7 +132,7 @@ func resourceArmDevTestLabSchedules() *schema.Resource { "time_zone_id": { Type: schema.TypeString, Required: true, - ValidateFunc: validate.VirtualMachineTimeZone(), + ValidateFunc: validate.VirtualMachineTimeZoneCaseInsensitive(), }, "notification_settings": { diff --git a/azurerm/resource_arm_virtual_machine.go b/azurerm/resource_arm_virtual_machine.go index 1be32963a53a..ddcc34930dac 100644 --- a/azurerm/resource_arm_virtual_machine.go +++ b/azurerm/resource_arm_virtual_machine.go @@ -468,7 +468,7 @@ func resourceArmVirtualMachine() *schema.Resource { Optional: true, ForceNew: true, DiffSuppressFunc: suppress.CaseDifference, - ValidateFunc: validate.VirtualMachineTimeZone(), + ValidateFunc: validate.VirtualMachineTimeZoneCaseInsensitive(), }, "winrm": { Type: schema.TypeList, diff --git a/azurerm/resource_arm_windows_virtual_machine_scale_set.go b/azurerm/resource_arm_windows_virtual_machine_scale_set.go new file mode 100644 index 000000000000..0f576270b01b --- /dev/null +++ b/azurerm/resource_arm_windows_virtual_machine_scale_set.go @@ -0,0 +1,1275 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + computeSvc "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/base64" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmWindowsVirtualMachineScaleSet() *schema.Resource { + return &schema.Resource{ + Create: resourceArmWindowsVirtualMachineScaleSetCreate, + Read: resourceArmWindowsVirtualMachineScaleSetRead, + Update: resourceArmWindowsVirtualMachineScaleSetUpdate, + Delete: resourceArmWindowsVirtualMachineScaleSetDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + // TODO: exposing requireGuestProvisionSignal once it's available + // https://github.com/Azure/azure-rest-api-specs/pull/7246 + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: computeSvc.ValidateWindowsName, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "location": azure.SchemaLocation(), + + // Required + "admin_username": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "admin_password": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "network_interface": computeSvc.VirtualMachineScaleSetNetworkInterfaceSchema(), + + "os_disk": computeSvc.VirtualMachineScaleSetOSDiskSchema(), + + "instances": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(0), + }, + + "sku": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + // Optional + "additional_capabilities": computeSvc.VirtualMachineScaleSetAdditionalCapabilitiesSchema(), + + "additional_unattend_config": { + Type: schema.TypeList, + Optional: true, + // whilst the SDK supports updating, the API doesn't: + // Code="PropertyChangeNotAllowed" + // Message="Changing property 'windowsConfiguration.additionalUnattendContent' is not allowed." + // Target="windowsConfiguration.additionalUnattendContent + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "content": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + }, + "setting": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(compute.AutoLogon), + string(compute.FirstLogonCommands), + }, false), + }, + }, + }, + }, + + "automatic_os_upgrade_policy": computeSvc.VirtualMachineScaleSetAutomatedOSUpgradePolicySchema(), + + "boot_diagnostics": computeSvc.VirtualMachineScaleSetBootDiagnosticsSchema(), + + "computer_name_prefix": { + Type: schema.TypeString, + Optional: true, + + // Computed since we reuse the VM name if one's not specified + Computed: true, + ForceNew: true, + // note: whilst the portal says 1-15 characters it seems to mirror the rules for the vm name + // (e.g. 1-15 for Windows, 1-63 for Windows) + ValidateFunc: computeSvc.ValidateWindowsName, + }, + + "custom_data": base64.OptionalSchema(), + + "data_disk": computeSvc.VirtualMachineScaleSetDataDiskSchema(), + + "do_not_run_extensions_on_overprovisioned_machines": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "enable_automatic_updates": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "eviction_policy": { + // only applicable when `priority` is set to `Low` + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(compute.Deallocate), + string(compute.Delete), + }, false), + }, + + "health_probe_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "identity": computeSvc.VirtualMachineScaleSetIdentitySchema(), + + "license_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "Windows_Client", + "Windows_Server", + }, false), + }, + + "max_bid_price": { + Type: schema.TypeFloat, + Optional: true, + Default: -1, + }, + + "overprovision": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "plan": computeSvc.PlanSchema(), + + "priority": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: string(compute.Regular), + ValidateFunc: validation.StringInSlice([]string{ + string(compute.Low), + string(compute.Regular), + }, false), + }, + + "provision_vm_agent": { + Type: schema.TypeBool, + Optional: true, + Default: true, + ForceNew: true, + }, + + "proximity_placement_group_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + // the Compute API is broken and returns the Resource Group name in UPPERCASE :shrug: + DiffSuppressFunc: suppress.CaseDifference, + }, + + "rolling_upgrade_policy": computeSvc.VirtualMachineScaleSetRollingUpgradePolicySchema(), + + "secret": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + // whilst this isn't present in the nested object it's required when this is specified + "key_vault_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "certificate": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "store": { + Type: schema.TypeString, + Required: true, + }, + "url": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateKeyVaultChildId, + }, + }, + }, + }, + }, + }, + }, + + "single_placement_group": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "source_image_id": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "source_image_reference": computeSvc.VirtualMachineScaleSetSourceImageReferenceSchema(), + + "tags": tags.Schema(), + + "terraform_should_roll_instances_when_required": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "timezone": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.VirtualMachineTimeZone(), + }, + + "upgrade_mode": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: string(compute.Manual), + ValidateFunc: validation.StringInSlice([]string{ + string(compute.Automatic), + string(compute.Manual), + string(compute.Rolling), + }, false), + }, + + "winrm_listener": { + Type: schema.TypeSet, + Optional: true, + // Whilst the SDK allows you to modify this, the API does not: + // Code="PropertyChangeNotAllowed" + // Message="Changing property 'windowsConfiguration.winRM.listeners' is not allowed." + // Target="windowsConfiguration.winRM.listeners" + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "protocol": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(compute.HTTP), + string(compute.HTTPS), + }, false), + }, + + "certificate_url": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: azure.ValidateKeyVaultChildId, + }, + }, + }, + }, + + "zone_balance": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + + "zones": azure.SchemaZones(), + + // Computed + "unique_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceArmWindowsVirtualMachineScaleSetCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Compute.VMScaleSetClient + ctx := meta.(*ArmClient).StopContext + + resourceGroup := d.Get("resource_group_name").(string) + name := d.Get("name").(string) + + if features.ShouldResourcesBeImported() { + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error checking for existing Windows Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + + if !utils.ResponseWasNotFound(resp.Response) { + return tf.ImportAsExistsError("azurerm_windows_virtual_machine_scale_set", *resp.ID) + } + } + + location := azure.NormalizeLocation(d.Get("location").(string)) + t := d.Get("tags").(map[string]interface{}) + + additionalCapabilitiesRaw := d.Get("additional_capabilities").([]interface{}) + additionalCapabilities := computeSvc.ExpandVirtualMachineScaleSetAdditionalCapabilities(additionalCapabilitiesRaw) + + additionalUnattendConfigRaw := d.Get("additional_unattend_config").([]interface{}) + additionalUnattendConfig := expandWindowsVirtualMachineScaleSetAdditionalUnattendConfig(additionalUnattendConfigRaw) + + bootDiagnosticsRaw := d.Get("boot_diagnostics").([]interface{}) + bootDiagnostics := computeSvc.ExpandVirtualMachineScaleSetBootDiagnostics(bootDiagnosticsRaw) + + dataDisksRaw := d.Get("data_disk").([]interface{}) + dataDisks := computeSvc.ExpandVirtualMachineScaleSetDataDisk(dataDisksRaw) + + identityRaw := d.Get("identity").([]interface{}) + identity, err := computeSvc.ExpandVirtualMachineScaleSetIdentity(identityRaw) + if err != nil { + return fmt.Errorf("Error expanding `identity`: %+v", err) + } + + networkInterfacesRaw := d.Get("network_interface").([]interface{}) + networkInterfaces, err := computeSvc.ExpandVirtualMachineScaleSetNetworkInterface(networkInterfacesRaw) + if err != nil { + return fmt.Errorf("Error expanding `network_interface`: %+v", err) + } + + osDiskRaw := d.Get("os_disk").([]interface{}) + osDisk := computeSvc.ExpandVirtualMachineScaleSetOSDisk(osDiskRaw, compute.Windows) + + planRaw := d.Get("plan").([]interface{}) + plan := computeSvc.ExpandPlan(planRaw) + + sourceImageReferenceRaw := d.Get("source_image_reference").([]interface{}) + sourceImageId := d.Get("source_image_id").(string) + sourceImageReference, err := computeSvc.ExpandVirtualMachineScaleSetSourceImageReference(sourceImageReferenceRaw, sourceImageId) + if err != nil { + return err + } + + healthProbeId := d.Get("health_probe_id").(string) + upgradeMode := compute.UpgradeMode(d.Get("upgrade_mode").(string)) + automaticOSUpgradePolicyRaw := d.Get("automatic_os_upgrade_policy").([]interface{}) + automaticOSUpgradePolicy := computeSvc.ExpandVirtualMachineScaleSetAutomaticUpgradePolicy(automaticOSUpgradePolicyRaw) + rollingUpgradePolicyRaw := d.Get("rolling_upgrade_policy").([]interface{}) + rollingUpgradePolicy := computeSvc.ExpandVirtualMachineScaleSetRollingUpgradePolicy(rollingUpgradePolicyRaw) + + if upgradeMode != compute.Manual && healthProbeId == "" { + return fmt.Errorf("`healthProbeId` must be set when `upgrade_mode` is set to %q", string(upgradeMode)) + } + + if upgradeMode != compute.Automatic && len(automaticOSUpgradePolicyRaw) > 0 { + return fmt.Errorf("An `automatic_os_upgrade_policy` block cannot be specified when `upgrade_mode` is not set to `Automatic`") + } + if upgradeMode == compute.Automatic && len(automaticOSUpgradePolicyRaw) == 0 { + return fmt.Errorf("An `automatic_os_upgrade_policy` block must be specified when `upgrade_mode` is set to `Automatic`") + } + + shouldHaveRollingUpgradePolicy := upgradeMode == compute.Automatic || upgradeMode == compute.Rolling + if !shouldHaveRollingUpgradePolicy && len(rollingUpgradePolicyRaw) > 0 { + return fmt.Errorf("A `rolling_upgrade_policy` block cannot be specified when `upgrade_mode` is set to %q", string(upgradeMode)) + } + if shouldHaveRollingUpgradePolicy && len(rollingUpgradePolicyRaw) == 0 { + return fmt.Errorf("A `rolling_upgrade_policy` block must be specified when `upgrade_mode` is set to %q", string(upgradeMode)) + } + + winRmListenersRaw := d.Get("winrm_listener").(*schema.Set).List() + winRmListeners := expandWindowsVirtualMachineScaleSetWinRMListeners(winRmListenersRaw) + + secretsRaw := d.Get("secret").([]interface{}) + secrets := expandWindowsVirtualMachineScaleSetSecrets(secretsRaw) + + zonesRaw := d.Get("zones").([]interface{}) + zones := azure.ExpandZones(zonesRaw) + + var computerNamePrefix string + if v, ok := d.GetOk("computer_name_prefix"); ok && len(v.(string)) > 0 { + computerNamePrefix = v.(string) + } else { + computerNamePrefix = name + } + + networkProfile := &compute.VirtualMachineScaleSetNetworkProfile{ + NetworkInterfaceConfigurations: networkInterfaces, + } + if healthProbeId != "" { + networkProfile.HealthProbe = &compute.APIEntityReference{ + ID: utils.String(healthProbeId), + } + } + + priority := compute.VirtualMachinePriorityTypes(d.Get("priority").(string)) + upgradePolicy := compute.UpgradePolicy{ + Mode: upgradeMode, + AutomaticOSUpgradePolicy: automaticOSUpgradePolicy, + RollingUpgradePolicy: rollingUpgradePolicy, + } + + virtualMachineProfile := compute.VirtualMachineScaleSetVMProfile{ + Priority: priority, + OsProfile: &compute.VirtualMachineScaleSetOSProfile{ + AdminPassword: utils.String(d.Get("admin_password").(string)), + AdminUsername: utils.String(d.Get("admin_username").(string)), + ComputerNamePrefix: utils.String(computerNamePrefix), + WindowsConfiguration: &compute.WindowsConfiguration{ + ProvisionVMAgent: utils.Bool(d.Get("provision_vm_agent").(bool)), + WinRM: winRmListeners, + }, + Secrets: secrets, + }, + DiagnosticsProfile: bootDiagnostics, + NetworkProfile: networkProfile, + StorageProfile: &compute.VirtualMachineScaleSetStorageProfile{ + ImageReference: sourceImageReference, + OsDisk: osDisk, + DataDisks: dataDisks, + }, + } + + enableAutomaticUpdates := d.Get("enable_automatic_updates").(bool) + if upgradeMode != compute.Automatic { + virtualMachineProfile.OsProfile.WindowsConfiguration.EnableAutomaticUpdates = utils.Bool(enableAutomaticUpdates) + } else if !enableAutomaticUpdates { + return fmt.Errorf("`enable_automatic_updates` must be set to `true` when `upgrade_mode` is set to `Automatic`") + } + + if v, ok := d.Get("max_bid_price").(float64); ok && v > 0 { + if priority != compute.Low { + return fmt.Errorf("`max_bid_price` can only be configured when `priority` is set to `Low`") + } + + virtualMachineProfile.BillingProfile = &compute.BillingProfile{ + MaxPrice: utils.Float(v), + } + } + + if v, ok := d.GetOk("custom_data"); ok { + virtualMachineProfile.OsProfile.CustomData = utils.String(v.(string)) + } + + if evictionPolicyRaw, ok := d.GetOk("eviction_policy"); ok { + if virtualMachineProfile.Priority != compute.Low { + return fmt.Errorf("An `eviction_policy` can only be specified when `priority` is set to `low`") + } + virtualMachineProfile.EvictionPolicy = compute.VirtualMachineEvictionPolicyTypes(evictionPolicyRaw.(string)) + } else if priority == compute.Low { + return fmt.Errorf("An `eviction_policy` must be specified when `priority` is set to `low`") + } + + if len(additionalUnattendConfigRaw) > 0 { + virtualMachineProfile.OsProfile.WindowsConfiguration.AdditionalUnattendContent = additionalUnattendConfig + } + + if v, ok := d.GetOk("license_type"); ok { + virtualMachineProfile.LicenseType = utils.String(v.(string)) + } + + if v, ok := d.GetOk("timezone"); ok { + virtualMachineProfile.OsProfile.WindowsConfiguration.TimeZone = utils.String(v.(string)) + } + + props := compute.VirtualMachineScaleSet{ + Location: utils.String(location), + Sku: &compute.Sku{ + Name: utils.String(d.Get("sku").(string)), + Capacity: utils.Int64(int64(d.Get("instances").(int))), + + // doesn't appear this can be set to anything else, even Promo machines are Standard + Tier: utils.String("Standard"), + }, + Identity: identity, + Plan: plan, + Tags: tags.Expand(t), + VirtualMachineScaleSetProperties: &compute.VirtualMachineScaleSetProperties{ + AdditionalCapabilities: additionalCapabilities, + DoNotRunExtensionsOnOverprovisionedVMs: utils.Bool(d.Get("do_not_run_extensions_on_overprovisioned_machines").(bool)), + Overprovision: utils.Bool(d.Get("overprovision").(bool)), + SinglePlacementGroup: utils.Bool(d.Get("single_placement_group").(bool)), + VirtualMachineProfile: &virtualMachineProfile, + UpgradePolicy: &upgradePolicy, + }, + Zones: zones, + } + + if v, ok := d.GetOk("proximity_placement_group_id"); ok { + props.VirtualMachineScaleSetProperties.ProximityPlacementGroup = &compute.SubResource{ + ID: utils.String(v.(string)), + } + } + + if v, ok := d.GetOk("zone_balance"); ok && v.(bool) { + if len(zonesRaw) == 0 { + return fmt.Errorf("`zone_balance` can only be set to `true` when zones are specified") + } + + props.VirtualMachineScaleSetProperties.ZoneBalance = utils.Bool(v.(bool)) + } + + log.Printf("[DEBUG] Creating Windows Virtual Machine Scale Set %q (Resource Group %q)..", name, resourceGroup) + future, err := client.CreateOrUpdate(ctx, resourceGroup, name, props) + if err != nil { + return fmt.Errorf("Error creating Windows Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + log.Printf("[DEBUG] Waiting for Windows Virtual Machine Scale Set %q (Resource Group %q) to be created..", name, resourceGroup) + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for creation of Windows Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err) + } + log.Printf("[DEBUG] Virtual Machine Scale Set %q (Resource Group %q) was created", name, resourceGroup) + + log.Printf("[DEBUG] Retrieving Virtual Machine Scale Set %q (Resource Group %q)..", name, resourceGroup) + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Error retrieving Windows Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if resp.ID == nil { + return fmt.Errorf("Error retrieving Windows Virtual Machine Scale Set %q (Resource Group %q): ID was nil", name, resourceGroup) + } + d.SetId(*resp.ID) + + return resourceArmWindowsVirtualMachineScaleSetRead(d, meta) +} + +func resourceArmWindowsVirtualMachineScaleSetUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Compute.VMScaleSetClient + ctx := meta.(*ArmClient).StopContext + + id, err := computeSvc.ParseVirtualMachineScaleSetResourceID(d.Id()) + if err != nil { + return err + } + + name := id.Name + resourceGroup := id.Base.ResourceGroup + + updateInstances := false + + // retrieve + existing, err := client.Get(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Error retrieving Windows Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err) + } + if existing.VirtualMachineScaleSetProperties == nil { + return fmt.Errorf("Error retrieving Windows Virtual Machine Scale Set %q (Resource Group %q): `properties` was nil", name, resourceGroup) + } + + updateProps := compute.VirtualMachineScaleSetUpdateProperties{ + VirtualMachineProfile: &compute.VirtualMachineScaleSetUpdateVMProfile{}, + // if an upgrade policy's been configured previously (which it will have) it must be threaded through + // this doesn't matter for Manual - but breaks when updating anything on a Automatic and Rolling Mode Scale Set + UpgradePolicy: existing.VirtualMachineScaleSetProperties.UpgradePolicy, + } + update := compute.VirtualMachineScaleSetUpdate{} + + upgradeMode := compute.UpgradeMode(d.Get("upgrade_mode").(string)) + if d.HasChange("automatic_os_upgrade_policy") || d.HasChange("rolling_upgrade_policy") { + upgradePolicy := compute.UpgradePolicy{ + Mode: upgradeMode, + } + + if d.HasChange("automatic_os_upgrade_policy") { + automaticRaw := d.Get("automatic_os_upgrade_policy").([]interface{}) + upgradePolicy.AutomaticOSUpgradePolicy = computeSvc.ExpandVirtualMachineScaleSetAutomaticUpgradePolicy(automaticRaw) + } + + if d.HasChange("rolling_upgrade_policy") { + rollingRaw := d.Get("rolling_upgrade_policy").([]interface{}) + upgradePolicy.RollingUpgradePolicy = computeSvc.ExpandVirtualMachineScaleSetRollingUpgradePolicy(rollingRaw) + } + + updateProps.UpgradePolicy = &upgradePolicy + } + + priority := compute.VirtualMachinePriorityTypes(d.Get("priority").(string)) + if d.HasChange("max_bid_price") { + if priority != compute.Low { + return fmt.Errorf("`max_bid_price` can only be configured when `priority` is set to `Low`") + } + + updateProps.VirtualMachineProfile.BillingProfile = &compute.BillingProfile{ + MaxPrice: utils.Float(d.Get("max_bid_price").(float64)), + } + } + + if d.HasChange("single_placement_group") { + updateProps.SinglePlacementGroup = utils.Bool(d.Get("single_placement_group").(bool)) + } + + if d.HasChange("enable_automatic_updates") || + d.HasChange("custom_data") || + d.HasChange("provision_vm_agent") || + d.HasChange("secret") || + d.HasChange("timezone") { + osProfile := compute.VirtualMachineScaleSetUpdateOSProfile{} + + if d.HasChange("enable_automatic_updates") || d.HasChange("provision_vm_agent") || d.HasChange("timezone") { + windowsConfig := compute.WindowsConfiguration{} + + if d.HasChange("enable_automatic_updates") { + if upgradeMode == compute.Automatic { + return fmt.Errorf("`enable_automatic_updates` cannot be changed for when `upgrade_mode` is `Automatic`") + } + + windowsConfig.EnableAutomaticUpdates = utils.Bool(d.Get("enable_automatic_updates").(bool)) + } + + if d.HasChange("provision_vm_agent") { + windowsConfig.ProvisionVMAgent = utils.Bool(d.Get("provision_vm_agent").(bool)) + } + + if d.HasChange("timezone") { + windowsConfig.TimeZone = utils.String(d.Get("timezone").(string)) + } + + osProfile.WindowsConfiguration = &windowsConfig + } + + if d.HasChange("custom_data") { + updateInstances = true + + // customData can only be sent if it's a base64 encoded string, + // so it's not possible to remove this without tainting the resource + if v, ok := d.GetOk("custom_data"); ok { + osProfile.CustomData = utils.String(v.(string)) + } + } + + if d.HasChange("secret") { + secretsRaw := d.Get("secret").([]interface{}) + osProfile.Secrets = expandWindowsVirtualMachineScaleSetSecrets(secretsRaw) + } + + updateProps.VirtualMachineProfile.OsProfile = &osProfile + } + + if d.HasChange("data_disk") || d.HasChange("os_disk") || d.HasChange("source_image_id") || d.HasChange("source_image_reference") { + updateInstances = true + + storageProfile := &compute.VirtualMachineScaleSetUpdateStorageProfile{} + + if d.HasChange("data_disk") { + dataDisksRaw := d.Get("data_disk").([]interface{}) + storageProfile.DataDisks = computeSvc.ExpandVirtualMachineScaleSetDataDisk(dataDisksRaw) + } + + if d.HasChange("os_disk") { + osDiskRaw := d.Get("os_disk").([]interface{}) + storageProfile.OsDisk = computeSvc.ExpandVirtualMachineScaleSetOSDiskUpdate(osDiskRaw) + } + + if d.HasChange("source_image_id") || d.HasChange("source_image_reference") { + sourceImageReferenceRaw := d.Get("source_image_reference").([]interface{}) + sourceImageId := d.Get("source_image_id").(string) + sourceImageReference, err := computeSvc.ExpandVirtualMachineScaleSetSourceImageReference(sourceImageReferenceRaw, sourceImageId) + if err != nil { + return err + } + + storageProfile.ImageReference = sourceImageReference + } + + updateProps.VirtualMachineProfile.StorageProfile = storageProfile + } + + if d.HasChange("network_interface") { + networkInterfacesRaw := d.Get("network_interface").([]interface{}) + networkInterfaces, err := computeSvc.ExpandVirtualMachineScaleSetNetworkInterfaceUpdate(networkInterfacesRaw) + if err != nil { + return fmt.Errorf("Error expanding `network_interface`: %+v", err) + } + + // TODO: setting the health probe on update once https://github.com/Azure/azure-rest-api-specs/pull/7355 has been fixed + //healthProbeId := d.Get("health_probe_id").(string) + + updateProps.VirtualMachineProfile.NetworkProfile = &compute.VirtualMachineScaleSetUpdateNetworkProfile{ + NetworkInterfaceConfigurations: networkInterfaces, + } + } + + if d.HasChange("boot_diagnostics") { + updateInstances = true + + bootDiagnosticsRaw := d.Get("boot_diagnostics").([]interface{}) + updateProps.VirtualMachineProfile.DiagnosticsProfile = computeSvc.ExpandVirtualMachineScaleSetBootDiagnostics(bootDiagnosticsRaw) + } + + if d.HasChange("identity") { + identityRaw := d.Get("identity").([]interface{}) + identity, err := computeSvc.ExpandVirtualMachineScaleSetIdentity(identityRaw) + if err != nil { + return fmt.Errorf("Error expanding `identity`: %+v", err) + } + + update.Identity = identity + } + + if d.HasChange("plan") { + planRaw := d.Get("plan").([]interface{}) + update.Plan = computeSvc.ExpandPlan(planRaw) + } + + if d.HasChange("sku") || d.HasChange("instances") { + // in-case ignore_changes is being used, since both fields are required + // look up the current values and override them as needed + sku := existing.Sku + + if d.HasChange("sku") { + updateInstances = true + + sku.Name = utils.String(d.Get("sku").(string)) + } + + if d.HasChange("instances") { + sku.Capacity = utils.Int64(int64(d.Get("instances").(int))) + } + + update.Sku = sku + } + + if d.HasChange("tags") { + update.Tags = tags.Expand(d.Get("tags").(map[string]interface{})) + } + + update.VirtualMachineScaleSetUpdateProperties = &updateProps + + log.Printf("[DEBUG] Updating Windows Virtual Machine Scale Set %q (Resource Group %q)..", name, resourceGroup) + future, err := client.Update(ctx, resourceGroup, name, update) + if err != nil { + return fmt.Errorf("Error updating Windows Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + log.Printf("[DEBUG] Waiting for update of Windows Virtual Machine Scale Set %q (Resource Group %q)..", name, resourceGroup) + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for update of Windows Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err) + } + log.Printf("[DEBUG] Updated Windows Virtual Machine Scale Set %q (Resource Group %q).", name, resourceGroup) + + // if we update the SKU, we also need to subsequently roll the instances using the `UpdateInstances` API + if updateInstances { + if userWantsToRollInstances := d.Get("terraform_should_roll_instances_when_required").(bool); userWantsToRollInstances { + log.Printf("[DEBUG] Rolling the VM Instances for Windows Virtual Machine Scale Set %q (Resource Group %q)..", name, resourceGroup) + instancesClient := meta.(*ArmClient).Compute.VMScaleSetVMsClient + instances, err := instancesClient.ListComplete(ctx, resourceGroup, name, "", "", "") + if err != nil { + return fmt.Errorf("Error listing VM Instances for Windows Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + log.Printf("[DEBUG] Determining instances to roll..") + instanceIdsToRoll := make([]string, 0) + for instances.NotDone() { + instance := instances.Value() + props := instance.VirtualMachineScaleSetVMProperties + if props != nil && instance.InstanceID != nil { + latestModel := props.LatestModelApplied + if latestModel != nil || !*latestModel { + instanceIdsToRoll = append(instanceIdsToRoll, *instance.InstanceID) + } + } + + if err := instances.NextWithContext(ctx); err != nil { + return fmt.Errorf("Error enumerating instances: %s", err) + } + } + + // there's a performance enhancement to do batches here, but this is fine for a first pass + for _, instanceId := range instanceIdsToRoll { + instanceIds := []string{instanceId} + + log.Printf("[DEBUG] Updating Instance %q to the Latest Configuration..", instanceId) + ids := compute.VirtualMachineScaleSetVMInstanceRequiredIDs{ + InstanceIds: &instanceIds, + } + future, err := client.UpdateInstances(ctx, resourceGroup, name, ids) + if err != nil { + return fmt.Errorf("Error updating Instance %q (Windows VM Scale Set %q / Resource Group %q) to the Latest Configuration: %+v", instanceId, name, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for update of Instance %q (Windows VM Scale Set %q / Resource Group %q) to the Latest Configuration: %+v", instanceId, name, resourceGroup, err) + } + log.Printf("[DEBUG] Updated Instance %q to the Latest Configuration.", instanceId) + + // TODO: does this want to be a separate, user-configurable toggle? + log.Printf("[DEBUG] Reimaging Instance %q..", instanceId) + reimageInput := &compute.VirtualMachineScaleSetReimageParameters{ + InstanceIds: &instanceIds, + } + reimageFuture, err := client.Reimage(ctx, resourceGroup, name, reimageInput) + if err != nil { + return fmt.Errorf("Error reimaging Instance %q (Windows VM Scale Set %q / Resource Group %q): %+v", instanceId, name, resourceGroup, err) + } + + if err = reimageFuture.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for reimage of Instance %q (Windows VM Scale Set %q / Resource Group %q): %+v", instanceId, name, resourceGroup, err) + } + log.Printf("[DEBUG] Reimaged Instance %q..", instanceId) + } + + log.Printf("[DEBUG] Rolled the VM Instances for Windows Virtual Machine Scale Set %q (Resource Group %q).", name, resourceGroup) + } else { + log.Printf("[DEBUG] Terraform wants to roll the VM Instances for Windows Virtual Machine Scale Set %q (Resource Group %q) - but user has opted out - skipping..", name, resourceGroup) + } + } + + return resourceArmWindowsVirtualMachineScaleSetRead(d, meta) +} + +func resourceArmWindowsVirtualMachineScaleSetRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Compute.VMScaleSetClient + ctx := meta.(*ArmClient).StopContext + + id, err := computeSvc.ParseVirtualMachineScaleSetResourceID(d.Id()) + if err != nil { + return err + } + + name := id.Name + resourceGroup := id.Base.ResourceGroup + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Windows Virtual Machine Scale Set %q was not found in Resource Group %q - removing from state!", name, resourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving Windows Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resourceGroup) + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + + var skuName *string + var instances int + if resp.Sku != nil { + skuName = resp.Sku.Name + if resp.Sku.Capacity != nil { + instances = int(*resp.Sku.Capacity) + } + } + d.Set("instances", instances) + d.Set("sku", skuName) + + if err := d.Set("identity", computeSvc.FlattenVirtualMachineScaleSetIdentity(resp.Identity)); err != nil { + return fmt.Errorf("Error setting `identity`: %+v", err) + } + + if err := d.Set("plan", computeSvc.FlattenPlan(resp.Plan)); err != nil { + return fmt.Errorf("Error setting `plan`: %+v", err) + } + + if resp.VirtualMachineScaleSetProperties == nil { + return fmt.Errorf("Error retrieving Windows Virtual Machine Scale Set %q (Resource Group %q): `properties` was nil", name, resourceGroup) + } + props := *resp.VirtualMachineScaleSetProperties + + if err := d.Set("additional_capabilities", computeSvc.FlattenVirtualMachineScaleSetAdditionalCapabilities(props.AdditionalCapabilities)); err != nil { + return fmt.Errorf("Error setting `additional_capabilities`: %+v", props.AdditionalCapabilities) + } + + d.Set("do_not_run_extensions_on_overprovisioned_machines", props.DoNotRunExtensionsOnOverprovisionedVMs) + d.Set("overprovision", props.Overprovision) + proximityPlacementGroupId := "" + if props.ProximityPlacementGroup != nil && props.ProximityPlacementGroup.ID != nil { + proximityPlacementGroupId = *props.ProximityPlacementGroup.ID + } + d.Set("proximity_placement_group_id", proximityPlacementGroupId) + d.Set("single_placement_group", props.SinglePlacementGroup) + d.Set("unique_id", props.UniqueID) + d.Set("zone_balance", props.ZoneBalance) + + var upgradeMode compute.UpgradeMode + if policy := props.UpgradePolicy; policy != nil { + upgradeMode = policy.Mode + d.Set("upgrade_mode", string(policy.Mode)) + + flattenedAutomatic := computeSvc.FlattenVirtualMachineScaleSetAutomaticOSUpgradePolicy(policy.AutomaticOSUpgradePolicy) + if err := d.Set("automatic_os_upgrade_policy", flattenedAutomatic); err != nil { + return fmt.Errorf("Error setting `automatic_os_upgrade_policy`: %+v", err) + } + + flattenedRolling := computeSvc.FlattenVirtualMachineScaleSetRollingUpgradePolicy(policy.RollingUpgradePolicy) + if err := d.Set("rolling_upgrade_policy", flattenedRolling); err != nil { + return fmt.Errorf("Error setting `rolling_upgrade_policy`: %+v", err) + } + } + + if profile := props.VirtualMachineProfile; profile != nil { + if err := d.Set("boot_diagnostics", computeSvc.FlattenVirtualMachineScaleSetBootDiagnostics(profile.DiagnosticsProfile)); err != nil { + return fmt.Errorf("Error setting `boot_diagnostics`: %+v", err) + } + + // defaulted since BillingProfile isn't returned if it's unset + maxBidPrice := float64(-1.0) + if profile.BillingProfile != nil && profile.BillingProfile.MaxPrice != nil { + maxBidPrice = *profile.BillingProfile.MaxPrice + } + d.Set("max_bid_price", maxBidPrice) + + d.Set("eviction_policy", string(profile.EvictionPolicy)) + d.Set("license_type", profile.LicenseType) + d.Set("priority", string(profile.Priority)) + + if storageProfile := profile.StorageProfile; storageProfile != nil { + if err := d.Set("os_disk", computeSvc.FlattenVirtualMachineScaleSetOSDisk(storageProfile.OsDisk)); err != nil { + return fmt.Errorf("Error setting `os_disk`: %+v", err) + } + + if err := d.Set("data_disk", computeSvc.FlattenVirtualMachineScaleSetDataDisk(storageProfile.DataDisks)); err != nil { + return fmt.Errorf("Error setting `data_disk`: %+v", err) + } + + if err := d.Set("source_image_reference", computeSvc.FlattenVirtualMachineScaleSetSourceImageReference(storageProfile.ImageReference)); err != nil { + return fmt.Errorf("Error setting `source_image_reference`: %+v", err) + } + + var storageImageId string + if storageProfile.ImageReference != nil && storageProfile.ImageReference.ID != nil { + storageImageId = *storageProfile.ImageReference.ID + } + d.Set("source_image_id", storageImageId) + } + + if osProfile := profile.OsProfile; osProfile != nil { + // admin_password isn't returned, but it's a top level field so we can ignore it without consequence + d.Set("admin_username", osProfile.AdminUsername) + d.Set("computer_name_prefix", osProfile.ComputerNamePrefix) + + if err := d.Set("secret", flattenWindowsVirtualMachineScaleSetSecrets(osProfile.Secrets)); err != nil { + return fmt.Errorf("Error setting `secret`: %+v", err) + } + + if windows := osProfile.WindowsConfiguration; windows != nil { + if err := d.Set("additional_unattend_config", flattenWindowsVirtualMachineScaleSetAdditionalUnattendConfig(windows.AdditionalUnattendContent, d)); err != nil { + return fmt.Errorf("Error setting `additional_unattend_config`: %+v", err) + } + + enableAutomaticUpdates := false + if windows.EnableAutomaticUpdates != nil { + enableAutomaticUpdates = *windows.EnableAutomaticUpdates + } + + // the API requires this is set to 'true' on submission (since it's now required for Windows VMSS's with + // an Automatic Upgrade Mode configured) however it actually returns false from the API.. + // after a bunch of testing the least bad option appears to be not to set this if it's an Automatic Upgrade Mode + if upgradeMode != compute.Automatic { + d.Set("enable_automatic_updates", enableAutomaticUpdates) + } + + d.Set("provision_vm_agent", windows.ProvisionVMAgent) + d.Set("timezone", windows.TimeZone) + + if err := d.Set("winrm_listener", flattenWindowsVirtualMachineScaleSetWinRMListener(windows.WinRM)); err != nil { + return fmt.Errorf("Error setting `winrm_listener`: %+v", err) + } + } + } + + if nwProfile := profile.NetworkProfile; nwProfile != nil { + flattenedNics := computeSvc.FlattenVirtualMachineScaleSetNetworkInterface(nwProfile.NetworkInterfaceConfigurations) + if err := d.Set("network_interface", flattenedNics); err != nil { + return fmt.Errorf("Error setting `network_interface`: %+v", err) + } + + healthProbeId := "" + if nwProfile.HealthProbe != nil && nwProfile.HealthProbe.ID != nil { + healthProbeId = *nwProfile.HealthProbe.ID + } + d.Set("health_probe_id", healthProbeId) + } + } + + if err := d.Set("zones", resp.Zones); err != nil { + return fmt.Errorf("Error setting `zones`: %+v", err) + } + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceArmWindowsVirtualMachineScaleSetDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Compute.VMScaleSetClient + ctx := meta.(*ArmClient).StopContext + + id, err := computeSvc.ParseVirtualMachineScaleSetResourceID(d.Id()) + if err != nil { + return err + } + + name := id.Name + resourceGroup := id.Base.ResourceGroup + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return fmt.Errorf("Error retrieving Windows Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + // Sometimes VMSS's aren't fully deleted when the `Delete` call returns - as such we'll try to scale the cluster + // to 0 nodes first, then delete the cluster - which should ensure there's no Network Interfaces kicking around + // and work around this Azure API bug: + // Original Error: Code="InUseSubnetCannotBeDeleted" Message="Subnet internal is in use by + // /{nicResourceID}/|providers|Microsoft.Compute|virtualMachineScaleSets|acctestvmss-190923101253410278|virtualMachines|0|networkInterfaces|example/ipConfigurations/internal and cannot be deleted. + // In order to delete the subnet, delete all the resources within the subnet. See aka.ms/deletesubnet. + if resp.Sku != nil { + resp.Sku.Capacity = utils.Int64(int64(0)) + + log.Printf("[DEBUG] Scaling instances to 0 prior to deletion - this helps avoids networking issues within Azure") + update := compute.VirtualMachineScaleSetUpdate{ + Sku: resp.Sku, + } + future, err := client.Update(ctx, resourceGroup, name, update) + if err != nil { + return fmt.Errorf("Error updating number of instances in Windows Virtual Machine Scale Set %q (Resource Group %q) to scale to 0: %+v", name, resourceGroup, err) + } + + log.Printf("[DEBUG] Waiting for scaling of instances to 0 prior to deletion - this helps avoids networking issues within Azure") + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + return fmt.Errorf("Error waiting for number of instances in Windows Virtual Machine Scale Set %q (Resource Group %q) to scale to 0: %+v", name, resourceGroup, err) + } + log.Printf("[DEBUG] Scaled instances to 0 prior to deletion - this helps avoids networking issues within Azure") + } else { + log.Printf("[DEBUG] Unable to scale instances to `0` since the `sku` block is nil - trying to delete anyway") + } + + log.Printf("[DEBUG] Deleting Windows Virtual Machine Scale Set %q (Resource Group %q)..", name, resourceGroup) + future, err := client.Delete(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Error deleting Windows Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + log.Printf("[DEBUG] Waiting for deletion of Windows Virtual Machine Scale Set %q (Resource Group %q)..", name, resourceGroup) + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for deletion of Windows Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err) + } + log.Printf("[DEBUG] Deleted Windows Virtual Machine Scale Set %q (Resource Group %q).", name, resourceGroup) + + return nil +} + +func expandWindowsVirtualMachineScaleSetAdditionalUnattendConfig(input []interface{}) *[]compute.AdditionalUnattendContent { + output := make([]compute.AdditionalUnattendContent, 0) + + 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)), + + // no other possible values + PassName: compute.OobeSystem, + ComponentName: compute.MicrosoftWindowsShellSetup, + }) + } + + return &output +} + +func flattenWindowsVirtualMachineScaleSetAdditionalUnattendConfig(input *[]compute.AdditionalUnattendContent, d *schema.ResourceData) []interface{} { + if input == nil { + return []interface{}{} + } + + existing := make([]interface{}, 0) + if v, ok := d.GetOk("additional_unattend_config"); ok { + existing = v.([]interface{}) + } + + output := make([]interface{}, 0) + for i, v := range *input { + // content isn't returned from the API as it's sensitive so we need to look it up + 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 expandWindowsVirtualMachineScaleSetSecrets(input []interface{}) *[]compute.VaultSecretGroup { + output := make([]compute.VaultSecretGroup, 0) + + for _, raw := range input { + v := raw.(map[string]interface{}) + + keyVaultId := v["key_vault_id"].(string) + certificatesRaw := v["certificate"].(*schema.Set).List() + certificates := make([]compute.VaultCertificate, 0) + for _, certificateRaw := range certificatesRaw { + certificateV := certificateRaw.(map[string]interface{}) + + store := certificateV["store"].(string) + url := certificateV["url"].(string) + certificates = append(certificates, compute.VaultCertificate{ + CertificateStore: utils.String(store), + CertificateURL: utils.String(url), + }) + } + + output = append(output, compute.VaultSecretGroup{ + SourceVault: &compute.SubResource{ + ID: utils.String(keyVaultId), + }, + VaultCertificates: &certificates, + }) + } + + return &output +} + +func flattenWindowsVirtualMachineScaleSetSecrets(input *[]compute.VaultSecretGroup) []interface{} { + if input == nil { + return []interface{}{} + } + + output := make([]interface{}, 0) + + for _, v := range *input { + keyVaultId := "" + if v.SourceVault != nil && v.SourceVault.ID != nil { + keyVaultId = *v.SourceVault.ID + } + + certificates := make([]interface{}, 0) + + if v.VaultCertificates != nil { + for _, c := range *v.VaultCertificates { + store := "" + if c.CertificateStore != nil { + store = *c.CertificateStore + } + + url := "" + if c.CertificateURL != nil { + url = *c.CertificateURL + } + + certificates = append(certificates, map[string]interface{}{ + "store": store, + "url": url, + }) + } + } + + output = append(output, map[string]interface{}{ + "key_vault_id": keyVaultId, + "certificate": certificates, + }) + } + + return output +} + +func expandWindowsVirtualMachineScaleSetWinRMListeners(input []interface{}) *compute.WinRMConfiguration { + listeners := make([]compute.WinRMListener, 0) + + for _, v := range input { + raw := v.(map[string]interface{}) + + listener := compute.WinRMListener{ + Protocol: compute.ProtocolTypes(raw["protocol"].(string)), + } + + certificateUrl := raw["certificate_url"].(string) + if certificateUrl != "" { + listener.CertificateURL = utils.String(certificateUrl) + } + + listeners = append(listeners, listener) + } + + return &compute.WinRMConfiguration{ + Listeners: &listeners, + } +} + +func flattenWindowsVirtualMachineScaleSetWinRMListener(input *compute.WinRMConfiguration) []interface{} { + if input == nil || input.Listeners == nil { + return []interface{}{} + } + + output := make([]interface{}, 0) + + for _, v := range *input.Listeners { + certificateUrl := "" + if v.CertificateURL != nil { + certificateUrl = *v.CertificateURL + } + + output = append(output, map[string]interface{}{ + "certificate_url": certificateUrl, + "protocol": string(v.Protocol), + }) + } + + return output +} diff --git a/azurerm/resource_arm_windows_virtual_machine_scale_set_auth_test.go b/azurerm/resource_arm_windows_virtual_machine_scale_set_auth_test.go new file mode 100644 index 000000000000..adbc136bdb56 --- /dev/null +++ b/azurerm/resource_arm_windows_virtual_machine_scale_set_auth_test.go @@ -0,0 +1,79 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMWindowsVirtualMachineScaleSet_authPassword(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 + } + } +} +`, template) +} diff --git a/azurerm/resource_arm_windows_virtual_machine_scale_set_disk_data_test.go b/azurerm/resource_arm_windows_virtual_machine_scale_set_disk_data_test.go new file mode 100644 index 000000000000..4e0eb23dfeb2 --- /dev/null +++ b/azurerm/resource_arm_windows_virtual_machine_scale_set_disk_data_test.go @@ -0,0 +1,798 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskBasic(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskBasic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskCaching(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskCaching(ri, location, "None"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskCaching(ri, location, "ReadOnly"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskCaching(ri, location, "ReadWrite"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskResizing(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + // 30GB + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskResize(ri, location, 30), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // 60GB + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskResize(ri, location, 60), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskMultiple(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskMultiple(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskRemove(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskBasic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskScaling(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + // no disks + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // one disk + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskBasic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // two disks + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskMultiple(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // no disks + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskStorageAccountTypeStandardLRS(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskStorageAccountType(ri, location, "Standard_LRS"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskStorageAccountTypeStandardSSDLRS(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskStorageAccountType(ri, location, "StandardSSD_LRS"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskStorageAccountTypePremiumLRS(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskStorageAccountType(ri, location, "Premium_LRS"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskStorageAccountTypeUltraSSDLRS(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + // Are supported in East US 2, SouthEast Asia, and North Europe, in two availability zones per region + location := testAltLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskStorageAccountTypeUltraSSDLRS(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskWriteAcceleratorEnabled(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskWriteAcceleratorEnabled(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskBasic(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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" + } + + data_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + disk_size_gb = 10 + lun = 10 + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskCaching(rInt int, location, caching string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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" + } + + data_disk { + storage_account_type = "Standard_LRS" + caching = %q + disk_size_gb = 10 + lun = 10 + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template, caching) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskResize(rInt int, location string, diskSizeGb int) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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" + } + + data_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + lun = 10 + disk_size_gb = %d + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template, diskSizeGb) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskMultiple(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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" + } + + data_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + disk_size_gb = 10 + lun = 10 + } + + data_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + disk_size_gb = 10 + lun = 20 + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskStorageAccountType(rInt int, location, storageAccountType string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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_F2s_v2" + 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" + } + + data_disk { + storage_account_type = %q + caching = "None" + disk_size_gb = 10 + lun = 10 + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template, storageAccountType) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskStorageAccountTypeUltraSSDLRS(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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_D2s_v3" + 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" + } + + data_disk { + storage_account_type = "UltraSSD_LRS" + caching = "None" + disk_size_gb = 10 + lun = 10 + } + + additional_capabilities { + ultra_ssd_enabled = true + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDiskWriteAcceleratorEnabled(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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_M8ms" + 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 = "Premium_LRS" + caching = "None" + } + + data_disk { + storage_account_type = "Premium_LRS" + caching = "None" + disk_size_gb = 10 + lun = 10 + write_accelerator_enabled = true + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template) +} diff --git a/azurerm/resource_arm_windows_virtual_machine_scale_set_disk_os_test.go b/azurerm/resource_arm_windows_virtual_machine_scale_set_disk_os_test.go new file mode 100644 index 000000000000..e0d5d1b22121 --- /dev/null +++ b/azurerm/resource_arm_windows_virtual_machine_scale_set_disk_os_test.go @@ -0,0 +1,489 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskCaching(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskCaching(ri, location, "None"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskCaching(ri, location, "ReadOnly"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskCaching(ri, location, "ReadWrite"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskCustomSize(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + // unset + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskCustomSize(ri, location, 128), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // resize a second time to confirm https://github.com/Azure/azure-rest-api-specs/issues/1906 + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskCustomSize(ri, location, 256), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskEphemeral(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskEphemeral(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskStorageAccountTypeStandardLRS(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskStorageAccountType(ri, location, "Standard_LRS"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskStorageAccountTypeStandardSSDLRS(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskStorageAccountType(ri, location, "StandardSSD_LRS"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskStorageAccountTypePremiumLRS(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskStorageAccountType(ri, location, "Premium_LRS"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskWriteAcceleratorEnabled(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskWriteAcceleratorEnabled(ri, location, true), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskCaching(rInt int, location, caching string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = "%s" + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template, caching) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskCustomSize(rInt int, location string, diskSize int) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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" + disk_size_gb = %d + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template, diskSize) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskEphemeral(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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_F8s_v2" # has to be this large for ephemeral disks on Windows + 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 = "ReadOnly" + + diff_disk_settings { + option = "Local" + } + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskStorageAccountType(rInt int, location, storageAccountType string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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_F2s_v2" + 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 = %q + caching = "ReadWrite" + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template, storageAccountType) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDiskWriteAcceleratorEnabled(rInt int, location string, enabled bool) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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_M8ms" + 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 = "Premium_LRS" + caching = "None" + write_accelerator_enabled = %t + } + + network_interface { + name = "example" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template, enabled) +} diff --git a/azurerm/resource_arm_windows_virtual_machine_scale_set_identity_test.go b/azurerm/resource_arm_windows_virtual_machine_scale_set_identity_test.go new file mode 100644 index 000000000000..b03691e96962 --- /dev/null +++ b/azurerm/resource_arm_windows_virtual_machine_scale_set_identity_test.go @@ -0,0 +1,463 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMWindowsVirtualMachineScaleSet_identityNone(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "identity.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_identitySystemAssigned(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_identitySystemAssigned(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "identity.0.principal_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // disable it + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "identity.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_identitySystemAssigned(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "identity.0.principal_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_identityUserAssigned(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_identityUserAssigned(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // disable it + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "identity.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_identityUserAssigned(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // second + Config: testAccAzureRMWindowsVirtualMachineScaleSet_identityUserAssignedUpdated(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_identitySystemAssignedUserAssigned(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_identitySystemAssignedUserAssigned(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "identity.0.principal_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // disable it + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "identity.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_identitySystemAssignedUserAssigned(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "identity.0.principal_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_identitySystemAssigned(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 + } + } + + identity { + type = "SystemAssigned" + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_identityUserAssigned(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_user_assigned_identity" "test" { + name = "acctestuai-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +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 + } + } + + identity { + type = "UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.test.id, + ] + } +} +`, template, rInt) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_identityUserAssignedUpdated(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_user_assigned_identity" "test" { + name = "acctestuai-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_user_assigned_identity" "other" { + name = "acctestuai2-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +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 + } + } + + identity { + type = "UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.test.id, + azurerm_user_assigned_identity.other.id, + ] + } +} +`, template, rInt, rInt) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_identitySystemAssignedUserAssigned(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_user_assigned_identity" "test" { + name = "acctestuai-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +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 + } + } + + identity { + type = "SystemAssigned, UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.test.id, + ] + } +} +`, template, rInt) +} diff --git a/azurerm/resource_arm_windows_virtual_machine_scale_set_images_test.go b/azurerm/resource_arm_windows_virtual_machine_scale_set_images_test.go new file mode 100644 index 000000000000..f68c0b63b315 --- /dev/null +++ b/azurerm/resource_arm_windows_virtual_machine_scale_set_images_test.go @@ -0,0 +1,795 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMWindowsVirtualMachineScaleSet_imagesAutomaticUpdate(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesAutomaticUpdate(ri, location, "2016-Datacenter"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "enable_automatic_updates", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesAutomaticUpdate(ri, location, "2019-Datacenter"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "enable_automatic_updates", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_imagesFromCapturedVirtualMachineImage(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + rString := acctest.RandString(4) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + // provision a standard Virtual Machine with an Unmanaged Disk + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesFromVirtualMachinePrerequisitesWithVM(ri, location, rString), + }, + { + // then delete the Virtual Machine + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesFromVirtualMachinePrerequisites(ri, location, rString), + }, + { + // then capture two images of the Virtual Machine + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesFromVirtualMachinePrerequisitesWithImage(ri, location, rString), + }, + { + // then provision a Virtual Machine Scale Set using this image + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesFromVirtualMachine(ri, location, rString, "first"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // then update the image on this Virtual Machine Scale Set + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesFromVirtualMachine(ri, location, rString, "second"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_imagesManualUpdate(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesManualUpdate(ri, location, "2016-Datacenter"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesManualUpdate(ri, location, "2019-Datacenter"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_imagesManualUpdateExternalRoll(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesManualUpdateExternalRoll(ri, location, "2016-Datacenter"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesManualUpdateExternalRoll(ri, location, "2019-Datacenter"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_imagesRollingUpdate(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesRollingUpdate(ri, location, "2019-Datacenter"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesRollingUpdate(ri, location, "2019-Datacenter"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_imagesPlan(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_imagesPlan(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_imagesAutomaticUpdate(rInt int, location, version string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_public_ip" "test" { + name = "test-ip-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + allocation_method = "Static" +} + +resource "azurerm_lb" "test" { + name = "acctestlb-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + frontend_ip_configuration { + name = "internal" + public_ip_address_id = azurerm_public_ip.test.id + } +} + +resource "azurerm_lb_backend_address_pool" "test" { + name = "test" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + loadbalancer_id = azurerm_lb.test.id +} + +resource "azurerm_lb_nat_pool" "test" { + name = "test" + resource_group_name = azurerm_resource_group.test.name + loadbalancer_id = azurerm_lb.test.id + frontend_ip_configuration_name = "internal" + protocol = "Tcp" + frontend_port_start = 80 + frontend_port_end = 81 + backend_port = 8080 +} + +resource "azurerm_lb_probe" "test" { + resource_group_name = azurerm_resource_group.test.name + loadbalancer_id = azurerm_lb.test.id + name = "acctest-lb-probe" + port = 22 + protocol = "Tcp" +} + +resource "azurerm_lb_rule" "test" { + name = "AccTestLBRule" + resource_group_name = azurerm_resource_group.test.name + loadbalancer_id = azurerm_lb.test.id + probe_id = azurerm_lb_probe.test.id + backend_address_pool_id = azurerm_lb_backend_address_pool.test.id + frontend_ip_configuration_name = "internal" + protocol = "Tcp" + frontend_port = 22 + backend_port = 22 +} + +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!" + health_probe_id = azurerm_lb_probe.test.id + upgrade_mode = "Automatic" + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "%s" + 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 + load_balancer_backend_address_pool_ids = [ azurerm_lb_backend_address_pool.test.id ] + load_balancer_inbound_nat_rules_ids = [ azurerm_lb_nat_pool.test.id ] + } + } + + automatic_os_upgrade_policy { + disable_automatic_rollback = true + enable_automatic_os_upgrade = true + } + + rolling_upgrade_policy { + max_batch_instance_percent = 21 + max_unhealthy_instance_percent = 22 + max_unhealthy_upgraded_instance_percent = 23 + pause_time_between_batches = "PT30S" + } + + depends_on = ["azurerm_lb_rule.test"] +} +`, template, rInt, rInt, version) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_imagesFromVirtualMachinePrerequisites(rInt int, location, rString string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_public_ip" "source" { + name = "source-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + allocation_method = "Dynamic" +} + +resource "azurerm_network_interface" "source" { + name = "sourcenic-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + ip_configuration { + name = "source" + subnet_id = azurerm_subnet.test.id + private_ip_address_allocation = "Dynamic" + public_ip_address_id = azurerm_public_ip.source.id + } +} + +resource "azurerm_storage_account" "test" { + name = "accsa%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "blob" +} +`, template, rInt, rInt, rString) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_imagesFromVirtualMachinePrerequisitesWithVM(rInt int, location, rString string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_imagesFromVirtualMachinePrerequisites(rInt, location, rString) + return fmt.Sprintf(` +%s + +resource "azurerm_virtual_machine" "source" { + name = "source" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + network_interface_ids = [ azurerm_network_interface.source.id ] + vm_size = "Standard_F2" + + storage_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2019-Datacenter" + version = "latest" + } + + storage_os_disk { + name = "osdisk1" + vhd_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/osdisk.vhd" + caching = "ReadWrite" + create_option = "FromImage" + disk_size_gb = 128 + } + + os_profile { + computer_name = "source" + admin_username = "mradministrator" + admin_password = "P@ssword1234!" + } + + os_profile_windows_config {} +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_imagesFromVirtualMachinePrerequisitesWithImage(rInt int, location, rString string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_imagesFromVirtualMachinePrerequisites(rInt, location, rString) + return fmt.Sprintf(` +%s + +resource "azurerm_image" "first" { + name = "first" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + os_disk { + os_type = "Windows" + os_state = "Generalized" + blob_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/osdisk.vhd" + size_gb = 128 + caching = "None" + } +} + +resource "azurerm_image" "second" { + name = "second" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + os_disk { + os_type = "Windows" + os_state = "Generalized" + blob_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/osdisk.vhd" + size_gb = 128 + caching = "None" + } + + depends_on = [ "azurerm_image.first" ] +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_imagesFromVirtualMachine(rInt int, location, rString, image string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_imagesFromVirtualMachinePrerequisitesWithImage(rInt, location, rString) + 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 = "mradministrator" + admin_password = "P@ssword1234!" + source_image_id = azurerm_image.%s.id + + 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 + } + } +} +`, template, image) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_imagesManualUpdate(rInt int, location, version string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = "%s" + 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 + } + } +} +`, template, version) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_imagesManualUpdateExternalRoll(rInt int, location, version string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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!" + terraform_should_roll_instances_when_required = false + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "%s" + 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 + } + } +} +`, template, version) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_imagesRollingUpdate(rInt int, location, version string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_public_ip" "test" { + name = "test-ip-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + allocation_method = "Static" +} + +resource "azurerm_lb" "test" { + name = "acctestlb-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + frontend_ip_configuration { + name = "internal" + public_ip_address_id = azurerm_public_ip.test.id + } +} + +resource "azurerm_lb_backend_address_pool" "test" { + name = "test" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + loadbalancer_id = azurerm_lb.test.id +} + +resource "azurerm_lb_nat_pool" "test" { + name = "test" + resource_group_name = azurerm_resource_group.test.name + loadbalancer_id = azurerm_lb.test.id + frontend_ip_configuration_name = "internal" + protocol = "Tcp" + frontend_port_start = 80 + frontend_port_end = 81 + backend_port = 8080 +} + +resource "azurerm_lb_probe" "test" { + resource_group_name = azurerm_resource_group.test.name + loadbalancer_id = azurerm_lb.test.id + name = "acctest-lb-probe" + port = 22 + protocol = "Tcp" +} + +resource "azurerm_lb_rule" "test" { + name = "AccTestLBRule" + resource_group_name = azurerm_resource_group.test.name + loadbalancer_id = azurerm_lb.test.id + probe_id = azurerm_lb_probe.test.id + backend_address_pool_id = azurerm_lb_backend_address_pool.test.id + frontend_ip_configuration_name = "internal" + protocol = "Tcp" + frontend_port = 22 + backend_port = 22 +} + +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!" + health_probe_id = azurerm_lb_probe.test.id + upgrade_mode = "Rolling" + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "%s" + 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 + load_balancer_backend_address_pool_ids = [ azurerm_lb_backend_address_pool.test.id ] + load_balancer_inbound_nat_rules_ids = [ azurerm_lb_nat_pool.test.id ] + } + } + + rolling_upgrade_policy { + max_batch_instance_percent = 21 + max_unhealthy_instance_percent = 22 + max_unhealthy_upgraded_instance_percent = 23 + pause_time_between_batches = "PT30S" + } + + depends_on = ["azurerm_lb_rule.test"] +} +`, template, rInt, rInt, version) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_imagesPlan(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_marketplace_agreement" "test" { + publisher = "plesk" + offer = "plesk-onyx-windows" + plan = "plsk-win-hst-azr-m" +} + +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 = "plesk" + offer = "plesk-onyx-windows" + sku = "plsk-win-hst-azr-m" + 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 + } + } + + plan { + name = "plsk-win-hst-azr-m" + product = "plesk-onyx-windows" + publisher = "plesk" + } + + depends_on = [ "azurerm_marketplace_agreement.test" ] +} +`, template) +} diff --git a/azurerm/resource_arm_windows_virtual_machine_scale_set_network_test.go b/azurerm/resource_arm_windows_virtual_machine_scale_set_network_test.go new file mode 100644 index 000000000000..e40f8c8d70e5 --- /dev/null +++ b/azurerm/resource_arm_windows_virtual_machine_scale_set_network_test.go @@ -0,0 +1,1932 @@ +package azurerm + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkAcceleratedNetworking(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkAcceleratedNetworking(ri, location, true), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkAcceleratedNetworkingUpdated(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkAcceleratedNetworking(ri, location, false), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkAcceleratedNetworking(ri, location, true), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkAcceleratedNetworking(ri, location, false), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkApplicationGateway(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkApplicationGateway(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkApplicationSecurityGroup(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkApplicationSecurityGroup(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkApplicationSecurityGroupUpdate(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + // none + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkPrivate(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // one + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkApplicationSecurityGroup(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // another + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkApplicationSecurityGroupUpdated(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // none + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkPrivate(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkDNSServers(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkDNSServers(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkDNSServersUpdated(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkIPForwarding(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + // enabled + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkIPForwarding(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // disabled + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkPrivate(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // enabled + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkIPForwarding(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkIPv6(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkIPv6(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + ExpectError: regexp.MustCompile("Error expanding `network_interface`: An IPv6 Primary IP Configuration is unsupported - instead add a IPv4 IP Configuration as the Primary and make the IPv6 IP Configuration the secondary"), + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkLoadBalancer(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkLoadBalancer(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleIPConfigurations(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleIPConfigurations(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleIPConfigurationsIPv6(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleIPConfigurationsIPv6(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleNICs(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleNICs(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleNICsMultipleIPConfigurations(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleNICsMultipleIPConfigurations(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleNICsMultiplePublicIPs(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleNICsMultiplePublicIPs(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleNICsWithDifferentDNSServers(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleNICsWithDifferentDNSServers(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkNetworkSecurityGroup(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkNetworkSecurityGroup(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkNetworkSecurityGroupUpdate(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + // without + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkPrivate(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // add one + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkNetworkSecurityGroup(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // change it + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkNetworkSecurityGroupUpdated(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // remove it + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkPrivate(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkPrivate(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkPrivate(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkPublicIP(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkPublicIP(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkPublicIPDomainNameLabel(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkPublicIPDomainNameLabel(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkPublicIPFromPrefix(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkPublicIPFromPrefix(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_networkPublicIPTags(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_networkPublicIPTags(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkAcceleratedNetworking(rInt int, location string, enabled bool) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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_F4" + 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 + enable_accelerated_networking = %t + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template, enabled) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkApplicationGateway(rInt int, location string) string { + template := testAccAzureRMApplicationGateway_basic(rInt, location) + name := testAccAzureRMWindowsVirtualMachineScaleSet_vmName(rInt) + return fmt.Sprintf(` +%s + +locals { + vm_name = "%s" +} + +resource "azurerm_subnet" "other" { + name = "other" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.1.0/24" +} + +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.other.id + application_gateway_backend_address_pool_ids = [ azurerm_application_gateway.test.backend_address_pool.0.id ] + } + } +} +`, template, name) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkApplicationSecurityGroup(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_application_security_group" "test" { + name = "acctestasg-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +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 + application_security_group_ids = [ azurerm_application_security_group.test.id ] + } + } +} +`, template, rInt) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkApplicationSecurityGroupUpdated(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_application_security_group" "test" { + name = "acctestasg-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_application_security_group" "other" { + name = "acctestasg2-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +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 + application_security_group_ids = [ + azurerm_application_security_group.test.id, + azurerm_application_security_group.other.id, + ] + } + } +} +`, template, rInt, rInt) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkDNSServers(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 + dns_servers = ["8.8.8.8"] + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkDNSServersUpdated(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 + dns_servers = ["1.1.1.1", "8.8.8.8"] + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkIPForwarding(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 + enable_ip_forwarding = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkIPv6(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 + version = "IPv6" + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkLoadBalancer(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_public_ip" "test" { + name = "test-ip-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + allocation_method = "Static" +} + +resource "azurerm_lb" "test" { + name = "acctestlb-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + frontend_ip_configuration { + name = "internal" + public_ip_address_id = azurerm_public_ip.test.id + } +} + +resource "azurerm_lb_backend_address_pool" "test" { + name = "test" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + loadbalancer_id = azurerm_lb.test.id +} + +resource "azurerm_lb_nat_pool" "test" { + name = "test" + resource_group_name = azurerm_resource_group.test.name + loadbalancer_id = azurerm_lb.test.id + frontend_ip_configuration_name = "internal" + protocol = "Tcp" + frontend_port_start = 80 + frontend_port_end = 81 + backend_port = 8080 +} + +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 + load_balancer_backend_address_pool_ids = [ azurerm_lb_backend_address_pool.test.id ] + load_balancer_inbound_nat_rules_ids = [ azurerm_lb_nat_pool.test.id ] + } + } +} +`, template, rInt, rInt) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleIPConfigurations(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = "internal" + primary = true + + ip_configuration { + name = "primary" + primary = true + subnet_id = azurerm_subnet.test.id + } + + ip_configuration { + name = "secondary" + subnet_id = azurerm_subnet.test.id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleIPConfigurationsIPv6(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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_D2s_v3" + 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 = "primary" + primary = true + + ip_configuration { + name = "first" + primary = true + subnet_id = azurerm_subnet.test.id + version = "IPv4" + } + + ip_configuration { + name = "second" + version = "IPv6" + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleNICs(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = "primary" + primary = true + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } + + network_interface { + name = "secondary" + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleNICsMultipleIPConfigurations(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = "primary" + primary = true + + ip_configuration { + name = "first" + primary = true + subnet_id = azurerm_subnet.test.id + } + + ip_configuration { + name = "second" + subnet_id = azurerm_subnet.test.id + } + } + + network_interface { + name = "secondary" + + ip_configuration { + name = "third" + primary = true + subnet_id = azurerm_subnet.test.id + } + + ip_configuration { + name = "fourth" + subnet_id = azurerm_subnet.test.id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleNICsWithDifferentDNSServers(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = "primary" + primary = true + dns_servers = ["8.8.8.8"] + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } + + network_interface { + name = "secondary" + dns_servers = ["1.1.1.1"] + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkMultipleNICsMultiplePublicIPs(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = "primary" + primary = true + + ip_configuration { + name = "first" + primary = true + subnet_id = azurerm_subnet.test.id + + public_ip_address { + name = "first" + domain_name_label = "acctest1-%d" + idle_timeout_in_minutes = 4 + } + } + } + + network_interface { + name = "secondary" + + ip_configuration { + name = "second" + primary = true + subnet_id = azurerm_subnet.test.id + + public_ip_address { + name = "second" + domain_name_label = "acctest2-%d" + idle_timeout_in_minutes = 4 + } + } + } +} +`, template, rInt, rInt) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkNetworkSecurityGroup(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_network_security_group" "test" { + name = "acctestnsg-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +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 + network_security_group_id = azurerm_network_security_group.test.id + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template, rInt) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkNetworkSecurityGroupUpdated(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_network_security_group" "test" { + name = "acctestnsg-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_network_security_group" "other" { + name = "acctestnsg2-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +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 + network_security_group_id = azurerm_network_security_group.other.id + + ip_configuration { + name = "internal" + primary = true + subnet_id = azurerm_subnet.test.id + } + } +} +`, template, rInt, rInt) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkPrivate(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkPublicIP(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = "primary" + primary = true + + ip_configuration { + name = "first" + primary = true + subnet_id = azurerm_subnet.test.id + + public_ip_address { + name = "first" + idle_timeout_in_minutes = 4 + } + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkPublicIPDomainNameLabel(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = "primary" + primary = true + + ip_configuration { + name = "first" + primary = true + subnet_id = azurerm_subnet.test.id + + public_ip_address { + name = "first" + domain_name_label = "acctestdnl-%d" + idle_timeout_in_minutes = 4 + } + } + } +} +`, template, rInt) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkPublicIPFromPrefix(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_public_ip_prefix" "test" { + name = "acctestpublicipprefix-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +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 = "primary" + primary = true + + ip_configuration { + name = "first" + primary = true + subnet_id = azurerm_subnet.test.id + + public_ip_address { + name = "first" + public_ip_prefix_id = azurerm_public_ip_prefix.test.id + } + } + } +} +`, template, rInt) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_networkPublicIPTags(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = "primary" + primary = true + + ip_configuration { + name = "first" + primary = true + subnet_id = azurerm_subnet.test.id + + public_ip_address { + name = "first" + + ip_tag { + tag = "/Sql" + type = "FirstPartyUsage" + } + } + } + } +} +`, template) +} diff --git a/azurerm/resource_arm_windows_virtual_machine_scale_set_other_test.go b/azurerm/resource_arm_windows_virtual_machine_scale_set_other_test.go new file mode 100644 index 000000000000..d820fe5fc674 --- /dev/null +++ b/azurerm/resource_arm_windows_virtual_machine_scale_set_other_test.go @@ -0,0 +1,1884 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" +) + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherAdditionalUnattendConfig(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherAdditionalUnattendConfig(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "additional_unattend_config.0.content", + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherBootDiagnostics(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(5) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + // Enabled + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherBootDiagnostics(ri, location, rs), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // Removed + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherBootDiagnosticsDisabled(ri, location, rs), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // Enabled + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherBootDiagnostics(ri, location, rs), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherComputerNamePrefix(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherComputerNamePrefix(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherCustomData(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherCustomData(ri, location, "/bin/bash"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "custom_data", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherCustomData(ri, location, "/bin/zsh"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "custom_data", + "terraform_should_roll_instances_when_required", + }, + }, + { + // removed + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "custom_data", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherDoNotRunExtensionsOnOverProvisionedMachines(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherDoNotRunExtensionsOnOverProvisionedMachines(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherEnableAutomaticUpdatesDisabled(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherEnableAutomaticUpdatesDisabled(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // enabled + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherEnableAutomaticUpdatesDisabled(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherPriorityLowDeallocate(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherPriorityLow(ri, location, "Deallocate"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherPriorityLowDelete(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherPriorityLow(ri, location, "Delete"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherPriorityLowMaxBidPrice(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + // expensive, but guarantees this test will pass + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherPriorityLowMaxBidPrice(ri, location, "0.5000"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherPriorityLowMaxBidPrice(ri, location, "-1"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherPriorityRegular(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherPriorityRegular(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherRequiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherRequiresImport(ri, location), + ExpectError: testRequiresImportError("azurerm_windows_virtual_machine_scale_set"), + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherSecret(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(4) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherSecret(ri, location, rs), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // update + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherSecretUpdated(ri, location, rs), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + + { + // removed + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherSecretRemoved(ri, location, rs), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherTags(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherTags(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // add one + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherTagsUpdated(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // remove all + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherTimeZone(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherTimeZone(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherVMAgent(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherVMAgent(ri, location, true), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherVMAgentDisabled(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherVMAgent(ri, location, false), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherWinRMHTTP(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherWinRMHTTP(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherWinRMHTTPS(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherWinRMHTTPS(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherAdditionalUnattendConfig(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 + } + } + + additional_unattend_config { + setting = "AutoLogon" + content = "myadminP@ssword1234!true1" + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherBootDiagnostics(rInt int, location string, rString string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_account" "test" { + name = "accsa%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +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 + } + } + + boot_diagnostics { + storage_account_uri = azurerm_storage_account.test.primary_blob_endpoint + } +} +`, template, rString) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherBootDiagnosticsDisabled(rInt int, location string, rString string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_storage_account" "test" { + name = "accsa%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +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 + } + } +} +`, template, rString) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherComputerNamePrefix(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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!" + computer_name_prefix = "morty" + + 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 + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherCustomData(rInt int, location, customData string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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!" + custom_data = base64encode(%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 + } + } +} +`, template, customData) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherDoNotRunExtensionsOnOverProvisionedMachines(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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!" + overprovision = true + do_not_run_extensions_on_overprovisioned_machines = true + + 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 + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherEnableAutomaticUpdatesDisabled(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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!" + enable_automatic_updates = false + + 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 + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherPriorityLow(rInt int, location, evictionPolicy string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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!" + eviction_policy = %q + priority = "Low" + + 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 + } + } +} +`, template, evictionPolicy) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherPriorityLowMaxBidPrice(rInt int, location, maxBid string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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!" + eviction_policy = "Delete" + priority = "Low" + max_bid_price = %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 + } + } +} +`, template, maxBid) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherPriorityRegular(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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!" + priority = "Regular" + + 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 + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherRequiresImport(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_windows_virtual_machine_scale_set" "import" { + name = azurerm_windows_virtual_machine_scale_set.test.name + resource_group_name = azurerm_windows_virtual_machine_scale_set.test.resource_group_name + location = azurerm_windows_virtual_machine_scale_set.test.location + sku = azurerm_windows_virtual_machine_scale_set.test.sku + instances = azurerm_windows_virtual_machine_scale_set.test.instances + admin_username = azurerm_windows_virtual_machine_scale_set.test.admin_username + admin_password = "azurerm_windows_virtual_machine_scale_set.test.admin_password + + 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 + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherSecret(rInt int, location string, rString string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_otherSecretTemplate(rInt, location, rString) + 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 + } + } + + secret { + key_vault_id = azurerm_key_vault.test.id + + certificate { + store = "My" + url = azurerm_key_vault_certificate.first.secret_id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherSecretRemoved(rInt int, location string, rString string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_otherSecretTemplate(rInt, location, rString) + 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 + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherSecretUpdated(rInt int, location string, rString string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_otherSecretTemplate(rInt, location, rString) + 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 + } + } + + secret { + key_vault_id = azurerm_key_vault.test.id + + certificate { + store = "My" + url = azurerm_key_vault_certificate.first.secret_id + } + + certificate { + store = "My" + url = azurerm_key_vault_certificate.second.secret_id + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherSecretTemplate(rInt int, location string, rString string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +data "azurerm_client_config" "current" {} + +resource "azurerm_key_vault" "test" { + name = "acctestkeyvault%s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + tenant_id = data.azurerm_client_config.current.tenant_id + + sku_name = "standard" + enabled_for_template_deployment = true + enabled_for_deployment = true + + access_policy { + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.service_principal_object_id + + certificate_permissions = [ + "create", + "delete", + "get", + "update", + ] + + key_permissions = [ + "create", + ] + + secret_permissions = [ + "set", + ] + + storage_permissions = [ + "set", + ] + } +} + +resource "azurerm_key_vault_certificate" "first" { + name = "first" + key_vault_id = azurerm_key_vault.test.id + + certificate_policy { + issuer_parameters { + name = "Self" + } + + key_properties { + exportable = true + key_size = 2048 + key_type = "RSA" + reuse_key = true + } + + lifetime_action { + action { + action_type = "AutoRenew" + } + + trigger { + days_before_expiry = 30 + } + } + + secret_properties { + content_type = "application/x-pkcs12" + } + + x509_certificate_properties { + key_usage = [ + "cRLSign", + "dataEncipherment", + "digitalSignature", + "keyAgreement", + "keyCertSign", + "keyEncipherment", + ] + + subject = "CN=hello-world-first" + validity_in_months = 12 + } + } +} + +resource "azurerm_key_vault_certificate" "second" { + name = "second" + key_vault_id = azurerm_key_vault.test.id + + certificate_policy { + issuer_parameters { + name = "Self" + } + + key_properties { + exportable = true + key_size = 2048 + key_type = "RSA" + reuse_key = true + } + + lifetime_action { + action { + action_type = "AutoRenew" + } + + trigger { + days_before_expiry = 30 + } + } + + secret_properties { + content_type = "application/x-pkcs12" + } + + x509_certificate_properties { + key_usage = [ + "cRLSign", + "dataEncipherment", + "digitalSignature", + "keyAgreement", + "keyCertSign", + "keyEncipherment", + ] + + subject = "CN=hello-world-second" + validity_in_months = 12 + } + } +} +`, template, rString) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherTags(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 + } + } + + tags = { + artist = "Billy" + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherTagsUpdated(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 + } + } + + tags = { + artist = "Billy" + when = "we all fall asleep" + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherTimeZone(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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!" + timezone = "Hawaiian Standard Time" + + 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 + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherVMAgent(rInt int, location string, enabled bool) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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!" + provision_vm_agent = %t + + 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 + } + } +} +`, template, enabled) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherWinRMHTTP(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 + } + } + + winrm_listener { + protocol = "Http" + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherWinRMHTTPS(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + // key vault name can only be up to 24 chars + trimmedName := fmt.Sprintf("%d", rInt)[0:5] + return fmt.Sprintf(` +%s + +data "azurerm_client_config" "current" {} + +resource "azurerm_key_vault" "test" { + name = "acctestkv%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + sku_name = "standard" + tenant_id = data.azurerm_client_config.current.tenant_id + + access_policy { + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.service_principal_object_id + + key_permissions = [ + "backup", + "create", + "decrypt", + "delete", + "encrypt", + "get", + "import", + "list", + "purge", + "recover", + "restore", + "sign", + "unwrapKey", + "update", + "verify", + "wrapKey", + ] + + secret_permissions = [ + "backup", + "delete", + "get", + "list", + "purge", + "recover", + "restore", + "set", + ] + + certificate_permissions = [ + "create", + "delete", + "deleteissuers", + "get", + "getissuers", + "import", + "list", + "listissuers", + "managecontacts", + "manageissuers", + "setissuers", + "update", + ] + } + + enabled_for_deployment = true + enabled_for_template_deployment = true +} + +resource "azurerm_key_vault_certificate" "test" { + name = "example" + vault_uri = azurerm_key_vault.test.vault_uri + + certificate_policy { + issuer_parameters { + name = "Self" + } + + key_properties { + exportable = true + key_size = 2048 + key_type = "RSA" + reuse_key = true + } + + lifetime_action { + action { + action_type = "AutoRenew" + } + + trigger { + days_before_expiry = 30 + } + } + + secret_properties { + content_type = "application/x-pkcs12" + } + + x509_certificate_properties { + key_usage = [ + "cRLSign", + "dataEncipherment", + "digitalSignature", + "keyAgreement", + "keyCertSign", + "keyEncipherment", + ] + + subject = "CN=${local.vm_name}" + validity_in_months = 12 + } + } +} + +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 + } + } + + secret { + key_vault_id = azurerm_key_vault.test.id + + certificate { + store = "My" + url = azurerm_key_vault_certificate.test.secret_id + } + } + + winrm_listener { + protocol = "Http" + } + + winrm_listener { + certificate_url = azurerm_key_vault_certificate.test.secret_id + protocol = "Https" + } +} +`, template, trimmedName) +} diff --git a/azurerm/resource_arm_windows_virtual_machine_scale_set_scaling_test.go b/azurerm/resource_arm_windows_virtual_machine_scale_set_scaling_test.go new file mode 100644 index 000000000000..25ec16dfd63b --- /dev/null +++ b/azurerm/resource_arm_windows_virtual_machine_scale_set_scaling_test.go @@ -0,0 +1,941 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMWindowsVirtualMachineScaleSet_scalingAutoScale(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingAutoScale(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_scalingInstanceCount(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingInstanceCount(ri, location, 1), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingInstanceCount(ri, location, 3), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingInstanceCount(ri, location, 5), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // update the count but the `sku` should be ignored + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingInstanceCountIgnoreUpdatedSku(ri, location, 3), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // confirm that the `sku` hasn't been changed + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingInstanceCount(ri, location, 3), + PlanOnly: true, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_scalingOverProvisionDisabled(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingOverProvisionDisabled(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_scalingProximityPlacementGroup(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingProximityPlacementGroup(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_scalingSinglePlacementGroupDisabled(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingSinglePlacementGroupDisabled(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_scalingSinglePlacementGroupDisabledUpdate(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_authPassword(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingSinglePlacementGroupDisabled(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_scalingUpdateSku(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingUpdateSku(ri, location, "Standard_F2"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingUpdateSku(ri, location, "Standard_F4"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingUpdateSku(ri, location, "Standard_F2"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + { + // confirms that the `instances` count comes from the API + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingUpdateSkuIgnoredUpdatedCount(ri, location, "Standard_F2"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingUpdateSku(ri, location, "Standard_F2"), + PlanOnly: true, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_scalingZonesSingle(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingZonesSingle(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_scalingZonesMultiple(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingZonesMultiple(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachineScaleSet_scalingZonesBalance(t *testing.T) { + resourceName := "azurerm_windows_virtual_machine_scale_set.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_scalingZonesBalance(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + // not returned from the API + "admin_password", + "terraform_should_roll_instances_when_required", + }, + }, + }, + }) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_scalingAutoScale(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = 2 + admin_username = "adminuser" + admin_password = "P@ssword1234!" + overprovision = true + + 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 + } + } +} + +resource "azurerm_monitor_autoscale_setting" "test" { + name = "autoscale-config" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + target_resource_id = azurerm_windows_virtual_machine_scale_set.test.id + + profile { + name = "AutoScale" + + capacity { + default = 2 + minimum = 1 + maximum = 5 + } + + rule { + metric_trigger { + metric_name = "Percentage CPU" + metric_resource_id = azurerm_windows_virtual_machine_scale_set.test.id + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 75 + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = "1" + cooldown = "PT1M" + } + } + + rule { + metric_trigger { + metric_name = "Percentage CPU" + metric_resource_id = azurerm_windows_virtual_machine_scale_set.test.id + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "LessThan" + threshold = 25 + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = "1" + cooldown = "PT1M" + } + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_scalingInstanceCount(rInt int, location string, instanceCount int) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = %d + 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 + } + } +} +`, template, instanceCount) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_scalingInstanceCountIgnoreUpdatedSku(rInt int, location string, instanceCount int) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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_F4" + instances = %d + 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 + } + } + + lifecycle { + ignore_changes = [ "sku" ] + } +} +`, template, instanceCount) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_scalingOverProvisionDisabled(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = 3 + admin_username = "adminuser" + admin_password = "P@ssword1234!" + overprovision = false + + 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 + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_scalingProximityPlacementGroup(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_proximity_placement_group" "test" { + name = "acctestPPG-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +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!" + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + + 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 + } + } +} +`, template, rInt) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_scalingSinglePlacementGroupDisabled(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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!" + single_placement_group = false + + 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 + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_scalingUpdateSku(rInt int, location, skuName string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = %q + 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 + } + } +} +`, template, skuName) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_scalingUpdateSkuIgnoredUpdatedCount(rInt int, location, skuName string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = %q + instances = 5 + 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 + } + } + + lifecycle { + ignore_changes = ["instances"] + } +} +`, template, skuName) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_scalingZonesSingle(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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!" + zones = [ "1" ] + + 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 + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_scalingZonesMultiple(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = 2 + admin_username = "adminuser" + admin_password = "P@ssword1234!" + zones = [ "1", "2" ] + + 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 + } + } +} +`, template) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_scalingZonesBalance(rInt int, location string) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt, location) + 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 = 4 + admin_username = "adminuser" + admin_password = "P@ssword1234!" + zones = [ "1", "2" ] + zone_balance = true + + 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 + } + } +} +`, template) +} diff --git a/azurerm/resource_arm_windows_virtual_machine_scale_set_test.go b/azurerm/resource_arm_windows_virtual_machine_scale_set_test.go new file mode 100644 index 000000000000..ff0be255794c --- /dev/null +++ b/azurerm/resource_arm_windows_virtual_machine_scale_set_test.go @@ -0,0 +1,93 @@ +package azurerm + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func testCheckAzureRMWindowsVirtualMachineScaleSetDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).Compute.VMScaleSetClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_windows_virtual_machine_scale_set" { + continue + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + name := rs.Primary.Attributes["name"] + + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return err + } + } + + return nil + } + + return nil +} + +func testCheckAzureRMWindowsVirtualMachineScaleSetExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + name := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).Compute.VMScaleSetClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Windows Virtual Machine Scale Set %q (Resource Group: %q) does not exist", name, resourceGroup) + } + + return fmt.Errorf("Bad: Get on VMScaleSetClient: %+v", err) + } + + return nil + } +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_vmName(rInt int) string { + // windows VM names can be up to 15 chars, however the prefix can only be 9 chars + return fmt.Sprintf("acctvm%s", fmt.Sprintf("%d", rInt)[0:2]) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_template(rInt int, location string) string { + name := testAccAzureRMWindowsVirtualMachineScaleSet_vmName(rInt) + return fmt.Sprintf(` +locals { + vm_name = "%s" +} + +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestnw-%d" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_subnet" "test" { + name = "internal" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefix = "10.0.2.0/24" +} +`, name, rInt, location, rInt) +} diff --git a/website/docs/r/linux_virtual_machine_scale_set.html.markdown b/website/docs/r/linux_virtual_machine_scale_set.html.markdown index 5543c3720ded..fae632316b4d 100644 --- a/website/docs/r/linux_virtual_machine_scale_set.html.markdown +++ b/website/docs/r/linux_virtual_machine_scale_set.html.markdown @@ -10,7 +10,7 @@ description: |- ~> **NOTE:** **This resource is in Beta** and as such the Schema can change in Minor versions of the Provider. -~> **NOTE**: All arguments including the administrator login and password will be stored in the raw state as plain-text. Read more about sensitive data in state. +~> **NOTE**: All arguments including the administrator login and password will be stored in the raw state as plain-text. [Read more about sensitive data in state](/docs/state/sensitive-data.html). Manages a Linux Virtual Machine Scale Set. @@ -238,7 +238,7 @@ A `data_disk` block supports the following: -> **NOTE:** `UltraSSD_LRS` is only supported when `ultra_ssd_enabled` within the `additional_capabilities` block is enabled. -* `write_accelerator_enabled` - (Optional) Should Write Accelerator be enabled for this Data Disk? Defaults to `false`. +* `write_accelerator_enabled` - (Optional) Should Write Accelerator be enabled for this Data Disk? Defaults to `false`. -> **NOTE:** This requires that the `storage_account_type` is set to `Premium_LRS` and that `caching` is set to `None`. @@ -270,8 +270,12 @@ A `ip_configuration` block supports the following: * `load_balancer_backend_address_pool_ids` - (Optional) A list of Backend Address Pools ID's from a Load Balancer which this Virtual Machine Scale Set should be connected to. +-> **NOTE:** When using this field you'll also need to configure a Rule for the Load Balancer, and use a `depends_on` between this resource and the Load Balancer Rule. + * `load_balancer_inbound_nat_rules_ids` - (Optional) A list of NAT Rule ID's from a Load Balancer which this Virtual Machine Scale Set should be connected to. +-> **NOTE:** When using this field you'll also need to configure a Rule for the Load Balancer, and use a `depends_on` between this resource and the Load Balancer Rule. + * `primary` - (Optional) Is this the Primary IP Configuration for this Network Interface? Defaults to `false`. -> **NOTE:** One `ip_configuration` block must be marked as Primary for each Network Interface. diff --git a/website/docs/r/virtual_machine_scale_set.html.markdown b/website/docs/r/virtual_machine_scale_set.html.markdown index 6221fbca82f6..6870657c4bfd 100644 --- a/website/docs/r/virtual_machine_scale_set.html.markdown +++ b/website/docs/r/virtual_machine_scale_set.html.markdown @@ -440,7 +440,13 @@ output "principal_id" { * `subnet_id` - (Required) Specifies the identifier of the subnet. * `application_gateway_backend_address_pool_ids` - (Optional) Specifies an array of references to backend address pools of application gateways. A scale set can reference backend address pools of multiple application gateways. Multiple scale sets cannot use the same application gateway. * `load_balancer_backend_address_pool_ids` - (Optional) Specifies an array of references to backend address pools of load balancers. A scale set can reference backend address pools of one public and one internal load balancer. Multiple scale sets cannot use the same load balancer. + +-> **NOTE:** When using this field you'll also need to configure a Rule for the Load Balancer, and use a `depends_on` between this resource and the Load Balancer Rule. + * `load_balancer_inbound_nat_rules_ids` - (Optional) Specifies an array of references to inbound NAT pools for load balancers. A scale set can reference inbound nat pools of one public and one internal load balancer. Multiple scale sets cannot use the same load balancer. + +-> **NOTE:** When using this field you'll also need to configure a Rule for the Load Balancer, and use a `depends_on` between this resource and the Load Balancer Rule. + * `primary` - (Required) Specifies if this ip_configuration is the primary one. * `application_security_group_ids` - (Optional) Specifies up to `20` application security group IDs. * `public_ip_address_configuration` - (Optional) Describes a virtual machines scale set IP Configuration's PublicIPAddress configuration. The public_ip_address_configuration is documented below. diff --git a/website/docs/r/windows_virtual_machine_scale_set.html.markdown b/website/docs/r/windows_virtual_machine_scale_set.html.markdown new file mode 100644 index 000000000000..0f11e53d21af --- /dev/null +++ b/website/docs/r/windows_virtual_machine_scale_set.html.markdown @@ -0,0 +1,405 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_windows_virtual_machine_scale_set" +sidebar_current: "docs-azurerm-resource-windows-virtual-machine-scale-set" +description: |- + Manages a Windows Virtual Machine Scale Set. +--- + +# azurerm_windows_virtual_machine_scale_set + +~> **NOTE:** **This resource is in Beta** and as such the Schema can change in Minor versions of the Provider. + +~> **NOTE**: All arguments including the administrator login and password will be stored in the raw state as plain-text. [Read more about sensitive data in state](/docs/state/sensitive-data.html). + +Manages a Windows Virtual Machine Scale Set. + +~> **NOTE:** This resource does not support Unmanaged Disks. If you need to use Unmanaged Disks you can continue to use [the `azurerm_virtual_machine_scale_set` resource](virtual_machine_scale_set.html) instead + +~> **NOTE:** Terraform will automatically update & reimage the nodes in the Scale Set if Required during an Update (for example, when changing Sku) - you can opt out of this by setting the `terraform_should_roll_instances_when_required` field to `false`. + +## Example Usage + +This example provisions a basic Windows Virtual Machine Scale Set on an internal network. Additional examples of how to use the `azurerm_windows_virtual_machine_scale_set` resource can be found [in the ./examples/vm-scale-set/windows` directory within the Github Repository](https://github.com/terraform-providers/terraform-provider-azurerm/tree/master/examples/vm-scale-set/windows). + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_virtual_network" "test" { + name = "example-network" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + address_space = ["10.0.0.0/16"] +} + +resource "azurerm_subnet" "internal" { + name = "internal" + resource_group_name = azurerm_resource_group.example.name + virtual_network_name = azurerm_virtual_network.example.name + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_windows_virtual_machine_scale_set" "example" { + name = "example-vmss" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + sku = "Standard_F2" + instances = 1 + admin_password = "P@55w0rd1234!" + admin_username = "adminuser" + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter-Server-Core" + 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.internal.id + } + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Windows Virtual Machine Scale Set. Changing this forces a new resource to be created. + +* `location` - (Required) The Azure location where the Windows Virtual Machine Scale Set should exist. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the Resource Group in which the Windows Virtual Machine Scale Set should be exist. Changing this forces a new resource to be created. + +* `admin_password` - (Required) The Password which should be used for the local-administrator on this Virtual Machine. Changing this forces a new resource to be created. + +* `admin_username` - (Required) The username of the local administrator on each Virtual Machine Scale Set instance. Changing this forces a new resource to be created. + +* `instances` - (Required) The number of Virtual Machines in the Scale Set. + +-> **NOTE:** If you're using AutoScaling, you may wish to use [Terraform's `ignore_changes` functionality](https://www.terraform.io/docs/configuration/resources.html#ignore_changes) to ignore changes to this field. + +* `sku` - (Required) The Virtual Machine SKU for the Scale Set, such as `Standard_F2`. + +* `network_interface` - (Required) One or more `network_interface` blocks as defined below. + +* `os_disk` - (Required) An `os_disk` block as defined below. + +--- + +* `additional_capabilities` - (Optional) A `additional_capabilities` block as defined below. + +* `additional_unattend_config` - (Optional) One or more `additional_unattend_config` blocks as defined below. + +* `automatic_os_upgrade_policy` - (Optional) A `automatic_os_upgrade_policy` block as defined below. This is Required and can only be specified when `upgrade_mode` is set to `Automatic`. + +* `boot_diagnostics` - (Optional) A `boot_diagnostics` block as defined below. + +* `computer_name_prefix` - (Optional) The prefix which should be used for the name of the Virtual Machines in this Scale Set. If unspecified this defaults to the value for the `name` field. + +* `custom_data` - (Optional) The Base64-Encoded Custom Data which should be used for this Virtual Machine Scale Set. + +-> **NOTE:** When Custom Data has been configured, it's not possible to remove it without tainting the Virtual Machine Scale Set, due to a limitation of the Azure API. + +* `data_disk` - (Optional) One or more `data_disk` blocks as defined below. + +* `do_not_run_extensions_on_overprovisioned_machines` - (Optional) Should Virtual Machine Extensions be run on Overprovisioned Virtual Machines in the Scale Set? Defaults to `false`. + +* `enable_automatic_updates` - (Optional) Are automatic updates enabled for this Virtual Machine? Defaults to `true`. + +* `eviction_policy` - (Optional) The Policy which should be used Virtual Machines are Evicted from the Scale Set. Changing this forces a new resource to be created. + +-> **NOTE:** This can only be configured when `priority` is set to `Low`. + +* `health_probe_id` - (Optional) The ID of a Load Balancer Probe which should be used to determine the health of an instance. Changing this forces a new resource to be created. This is Required and can only be specified when `upgrade_mode` is set to `Automatic` or `Rolling`. + +* `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 `Windows_Client` and `Windows_Server`. Changing this forces a new resource to be created. + +* `max_bid_price` - (Optional) The maximum price you're willing to pay for a low-priority VM Scale Set, in US Dollars; which must be greater than the current low-priority price. If this bid price falls below the current low-priority price the Virtual Machines in the Scale Set will be evicted using the `eviction_policy`. Defaults to `-1`, which means that this VM Scale Set should not be evicted for price reasons. + +-> **NOTE:** This can only be configured when `priority` is set to `Low`. + +* `overprovision` - (Optional) Should Azure over-provision Virtual Machines in this Scale Set? This means that multiple Virtual Machines will be provisioned and Azure will keep the instances which become available first - which improves provisioning success rates and improves deployment time. You're not billed for these over-provisioned VM's and they don't count towards the Subscription Quota. Defaults to `false`. + +* `priority` - (Optional) The Priority of this Virtual Machine Scale Set. Possible values are `Regular` and `Low`. Defaults to `Regular`. Changing this value forces a new resource. + +-> **NOTE:** When `priority` is set to `Low` an `eviction_policy` must be specified. + +* `provision_vm_agent` - (Optional) Should the Azure VM Agent be provisioned on each Virtual Machine in the Scale Set? Defaults to `true`. Changing this value forces a new resource to be created. + +* `proximity_placement_group_id` - (Optional) The ID of the Proximity Placement Group in which the Virtual Machine Scale Set should be assigned to. Changing this forces a new resource to be created. + +* `rolling_upgrade_policy` - (Optional) A `rolling_upgrade_policy` block as defined below. This is Required and can only be specified when `upgrade_mode` is set to `Automatic` or `Rolling`. + +* `secret` - (Optional) One or more `secret` blocks as defined below. + +* `single_placement_group` - (Optional) Should this Virtual Machine Scale Set be limited to a Single Placement Group, which means the number of instances will be capped at 100 Virtual Machines. Defaults to `true`. + +* `source_image_id` - (Optional) The ID of an Image which each Virtual Machine in this Scale Set should be based on. + +-> **NOTE:** One of either `source_image_id` or `source_image_reference` must be set. + +* `source_image_reference` - (Optional) A `source_image_reference` block as defined below. + +-> **NOTE:** One of either `source_image_id` or `source_image_reference` must be set. + +* `tags` - (Optional) A mapping of tags which should be assigned to this Virtual Machine Scale Set. + +* `terraform_should_roll_instances_when_required` - (Optional) Should Terraform automatically roll instances within the Virtual Machine Scale Set when required? This happens when the `data_disk`, `os_disk`, `sku`, `source_image_id`, or `source_image_reference` fields change. This field defaults to `true`. + +-> **NOTE:** This field is specific to Terraform, when required Terraform will automatically roll the instances in a Scale Set one at a time. + +* `timezone` - (Optional) Specifies the time zone of the virtual machine, [the possible values are defined here](https://jackstromberg.com/2017/01/list-of-time-zones-consumed-by-azure/). + +* `upgrade_mode` - (Optional) Specifies how Upgrades (e.g. changing the Image/SKU) should be performed to Virtual Machine Instances. Possible values are `Automatic`, `Manual` and `Rolling`. Defaults to `Manual`. + +* `winrm_listener` - (Optional) One or more `winrm_listener` blocks as defined below. + +* `zone_balance` - (Optional) Should the Virtual Machines in this Scale Set be strictly evenly distributed across Availability Zones? Defaults to `false`. Changing this forces a new resource to be created. + +-> **NOTE:** This can only be set to `true` when one or more `zones` are configured. + +* `zones` - (Optional) A list of Availability Zones in which the Virtual Machines in this Scale Set should be created in. Changing this forces a new resource to be created. + +--- + +A `additional_capabilities` block supports the following: + +* `ultra_ssd_enabled` - (Optional) Should the capacity to enable Data Disks of the `UltraSSD_LRS` storage account type be supported on this Virtual Machine Scale Set? Defaults to `false`. Changing this forces a new resource to be created. + +--- + +A `additional_unattend_config` block supports the following: + +* `content` - (Required) The XML formatted content that is added to the unattend.xml file for the specified path and component. + +* `setting` - (Required) The name of the setting to which the content applies. Possible values are `AutoLogon` and `FirstLogonCommands`. + +--- + +A `automatic_os_upgrade_policy` block supports the following: + +* `disable_automatic_rollback` - (Required) Should automatic rollbacks be disabled? Changing this forces a new resource to be created. + +* `enable_automatic_os_upgrade` - (Required) Should OS Upgrades automatically be applied to Scale Set instances in a rolling fashion when a newer version of the OS Image becomes available? Changing this forces a new resource to be created. + +--- + +A `boot_diagnostics` block supports the following: + +* `storage_account_uri` - (Required) The Primary/Secondary Endpoint for the Azure Storage Account which should be used to store Boot Diagnostics, including Console Output and Screenshots from the Hypervisor. + +--- + +A `certificate` block supports the following: + +* `store` - (Required) The certificate store on the Virtual Machine where the certificate should be added. + +* `url` - (Required) The Secret URL of a Key Vault Certificate. + +-> **NOTE:** This can be sourced from the `secret_id` field within the `azurerm_key_vault_certificate` Resource. + +--- + +A `data_disk` block supports the following: + +* `caching` - (Required) The type of Caching which should be used for this Data Disk. Possible values are `None`, `ReadOnly` and `ReadWrite`. + +* `disk_size_gb` - (Required) The size of the Data Disk which should be created. + +* `lun` - (Required) The Logical Unit Number of the Data Disk, which must be unique within the Virtual Machine. + +* `storage_account_type` - (Required) The Type of Storage Account which should back this Data Disk. Possible values include `Standard_LRS`, `StandardSSD_LRS`, `Premium_LRS` and `UltraSSD_LRS`. + +-> **NOTE:** `UltraSSD_LRS` is only supported when `ultra_ssd_enabled` within the `additional_capabilities` block is enabled. + +* `write_accelerator_enabled` - (Optional) Should Write Accelerator be enabled for this Data Disk? Defaults to `false`. + +-> **NOTE:** This requires that the `storage_account_type` is set to `Premium_LRS` and that `caching` is set to `None`. + +--- + +A `diff_disk_settings` block supports the following: + +`option` - (Required) Specifies the Ephemeral Disk Settings for the OS Disk. At this time the only possible value is `Local`. Changing this forces a new resource to be created. + +--- + +A `identity` block supports the following: + +* `type` - (Required) The type of Managed Identity which should be assigned to the Windows Virtual Machine Scale Set. Possible values are `SystemAssigned`, `UserAssigned` and `SystemAssigned, UserAssigned`. + +* `identity_ids` - (Optional) A list of User Managed Identity ID's which should be assigned to the Windows Virtual Machine Scale Set. + +~> **NOTE:** This is required when `type` is set to `UserAssigned`. + +--- + +A `ip_configuration` block supports the following: + +* `name` - (Required) The Name which should be used for this IP Configuration. + +* `application_gateway_backend_address_pool_ids` - (Optional) A list of Backend Address Pools ID's from a Application Gateway which this Virtual Machine Scale Set should be connected to. + +* `application_security_group_ids` - (Optional) A list of Application Security Group ID's which this Virtual Machine Scale Set should be connected to. + +* `load_balancer_backend_address_pool_ids` - (Optional) A list of Backend Address Pools ID's from a Load Balancer which this Virtual Machine Scale Set should be connected to. + +-> **NOTE:** When using this field you'll also need to configure a Rule for the Load Balancer, and use a `depends_on` between this resource and the Load Balancer Rule. + +* `load_balancer_inbound_nat_rules_ids` - (Optional) A list of NAT Rule ID's from a Load Balancer which this Virtual Machine Scale Set should be connected to. + +-> **NOTE:** When using this field you'll also need to configure a Rule for the Load Balancer, and use a `depends_on` between this resource and the Load Balancer Rule. + +* `primary` - (Optional) Is this the Primary IP Configuration for this Network Interface? Defaults to `false`. + +-> **NOTE:** One `ip_configuration` block must be marked as Primary for each Network Interface. + +* `public_ip_address` - (Optional) A `public_ip_address` block as defined below. + +* `subnet_id` - (Optional) The ID of the Subnet which this IP Configuration should be connected to. + +~> `subnet_id` is required if `version` is set to `IPv4`. + +* `version` - (Optional) The Internet Protocol Version which should be used for this IP Configuration. Possible values are `IPv4` and `IPv6`. Defaults to `IPv4`. + +--- + +A `ip_tag` block supports the following: + +* `tag` - The IP Tag associated with the Public IP, such as `SQL` or `Storage`. + +* `type` - The Type of IP Tag, such as `FirstPartyUsage`. + +--- + +A `network_interface` block supports the following: + +* `name` - (Required) The Name which should be used for this Network Interface. Changing this forces a new resource to be created. + +* `ip_configuration` - (Required) One or more `ip_configuration` blocks as defined above. + +* `dns_servers` - (Optional) A list of IP Addresses of DNS Servers which should be assigned to the Network Interface. + +* `enable_accelerated_networking` - (Optional) Does this Network Interface support Accelerated Networking? Defaults to `false`. + +* `enable_ip_forwarding` - (Optional) Does this Network Interface support IP Forwarding? Defaults to `false`. + +* `network_security_group_id` - (Optional) The ID of a Network Security Group which should be assigned to this Network Interface. + +* `primary` - (Optional) Is this the Primary IP Configuration? + +-> **NOTE:** If multiple `network_interface` blocks are specified, one must be set to `primary`. + +--- + +A `os_disk` block supports the following: + +* `caching` - (Required) The Type of Caching which should be used for the Internal OS Disk. Possible values are `None`, `ReadOnly` and `ReadWrite`. + +* `storage_account_type` - (Required) The Type of Storage Account which should back this the Internal OS Disk. Possible values include `Standard_LRS`, `StandardSSD_LRS` and `Premium_LRS`. + +* `diff_disk_settings` - (Optional) A `diff_disk_settings` block as defined above. Changing this forces a new resource to be created. + +* `disk_size_gb` - (Optional) The Size of the Internal OS Disk in GB, if you wish to vary from the size used in the image this Virtual Machine Scale Set is sourced from. + +-> **NOTE:** If specified this must be equal to or larger than the size of the Image the VM Scale Set is based on. When creating a larger disk than exists in the image you'll need to repartition the disk to use the remaining space. + +* `write_accelerator_enabled` - (Optional) Should Write Accelerator be Enabled for this OS Disk? Defaults to `false`. + +-> **NOTE:** This requires that the `storage_account_type` is set to `Premium_LRS` and that `caching` is set to `None`. + +--- + +A `public_ip_address` block supports the following: + +* `name` - (Required) The Name of the Public IP Address Configuration. + +* `domain_name_label` - (Optional) The Prefix which should be used for the Domain Name Label for each Virtual Machine Instance. Azure concatenates the Domain Name Label and Virtual Machine Index to create a unique Domain Name Label for each Virtual Machine. + +* `idle_timeout_in_minutes` - (Optional) The Idle Timeout in Minutes for the Public IP Address. Possible values are in the range `4` to `32`. + +* `ip_tag` - (Optional) One or more `ip_tag` blocks as defined above. + +* `public_ip_prefix_id` - (Optional) The ID of the Public IP Address Prefix from where Public IP Addresses should be allocated. Changing this forces a new resource to be created. + +~> **NOTE:** This functionality is in Preview and must be opted into via `az feature register --namespace Microsoft.Network --name AllowBringYourOwnPublicIpAddress` and then `az provider register -n Microsoft.Network`. + +--- + +A `rolling_upgrade_policy` block supports the following: + +* `max_batch_instance_percent` - (Required) The maximum percent of total virtual machine instances that will be upgraded simultaneously by the rolling upgrade in one batch. As this is a maximum, unhealthy instances in previous or future batches can cause the percentage of instances in a batch to decrease to ensure higher reliability. Changing this forces a new resource to be created. + +* `max_unhealthy_instance_percent` - (Required) The maximum percentage of the total virtual machine instances in the scale set that can be simultaneously unhealthy, either as a result of being upgraded, or by being found in an unhealthy state by the virtual machine health checks before the rolling upgrade aborts. This constraint will be checked prior to starting any batch. Changing this forces a new resource to be created. + +* `max_unhealthy_upgraded_instance_percent` - (Required) The maximum percentage of upgraded virtual machine instances that can be found to be in an unhealthy state. This check will happen after each batch is upgraded. If this percentage is ever exceeded, the rolling update aborts. Changing this forces a new resource to be created. + +* `pause_time_between_batches` - (Required) The wait time between completing the update for all virtual machines in one batch and starting the next batch. The time duration should be specified in ISO 8601 format. Changing this forces a new resource to be created. + +--- + +A `secret` block supports the following: + +* `certificate` - (Required) One or more `certificate` blocks as defined above. + +* `key_vault_id` - (Required) The ID of the Key Vault from which all Secrets should be sourced. + +--- + +A `winrm_listener` block supports the following: + +* `certificate_url` - (Optional) The Secret URL of a Key Vault Certificate, which must be specified when `protocol` is set to `Https`. + +-> **NOTE:** This can be sourced from the `secret_id` field within the `azurerm_key_vault_certificate` Resource. + +* `protocol` - (Required) The Protocol of the WinRM Listener. Possible values are `Http` and `Https`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The ID of the Windows Virtual Machine Scale Set. + +* `identity` - An `identity` block as defined below. + +* `unique_id` - The Unique ID for this Windows Virtual Machine Scale Set. + +--- + +An `identity` block exports the following: + +* `principal_id` - The ID of the System Managed Service Principal. + +### Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Windows Virtual Machine Scale Set. +* `update` - (Defaults to 60 minutes) Used when updating (and rolling the instances of) the Windows Virtual Machine Scale Set (e.g. when changing SKU). +* `delete` - (Defaults to 30 minutes) Used when deleting the Windows Virtual Machine Scale Set. + +## Import + +Windows Virtual Machine Scale Sets can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_windows_virtual_machine_scale_set.test /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/Microsoft.Compute/virtualMachineScaleSets/scaleset1 +```