diff --git a/azurerm/internal/services/compute/linux_virtual_machine_resource.go b/azurerm/internal/services/compute/linux_virtual_machine_resource.go index ae46a485f764..5c155ca2c4f2 100644 --- a/azurerm/internal/services/compute/linux_virtual_machine_resource.go +++ b/azurerm/internal/services/compute/linux_virtual_machine_resource.go @@ -51,7 +51,7 @@ func resourceLinuxVirtualMachine() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: ValidateLinuxName, + ValidateFunc: ValidateVmName, }, "resource_group_name": azure.SchemaResourceGroupName(), @@ -128,9 +128,8 @@ func resourceLinuxVirtualMachine() *schema.Resource { // 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 Linux) - ValidateFunc: ValidateLinuxName, + + ValidateFunc: ValidateLinuxComputerNameFull, }, "custom_data": base64.OptionalSchema(true), @@ -295,6 +294,10 @@ func resourceLinuxVirtualMachineCreate(d *schema.ResourceData, meta interface{}) if v, ok := d.GetOk("computer_name"); ok && len(v.(string)) > 0 { computerName = v.(string) } else { + _, errs := ValidateLinuxComputerNameFull(d.Get("name"), "computer_name") + if len(errs) > 0 { + return fmt.Errorf("unable to assume default computer name %s Please adjust the %q, or specify an explicit %q", errs[0], "name", "computer_name") + } computerName = name } disablePasswordAuthentication := d.Get("disable_password_authentication").(bool) diff --git a/azurerm/internal/services/compute/resource_arm_linux_virtual_machine_scale_set.go b/azurerm/internal/services/compute/resource_arm_linux_virtual_machine_scale_set.go index fd87400734b0..7b938fff16ed 100644 --- a/azurerm/internal/services/compute/resource_arm_linux_virtual_machine_scale_set.go +++ b/azurerm/internal/services/compute/resource_arm_linux_virtual_machine_scale_set.go @@ -49,7 +49,7 @@ func resourceArmLinuxVirtualMachineScaleSet() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: ValidateLinuxName, + ValidateFunc: ValidateVmName, }, "resource_group_name": azure.SchemaResourceGroupName(), @@ -106,9 +106,8 @@ func resourceArmLinuxVirtualMachineScaleSet() *schema.Resource { // 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 Linux) - ValidateFunc: ValidateLinuxName, + + ValidateFunc: ValidateLinuxComputerNamePrefix, }, "custom_data": base64.OptionalSchema(false), @@ -352,6 +351,10 @@ func resourceArmLinuxVirtualMachineScaleSetCreate(d *schema.ResourceData, meta i if v, ok := d.GetOk("computer_name_prefix"); ok && len(v.(string)) > 0 { computerNamePrefix = v.(string) } else { + _, errs := ValidateLinuxComputerNamePrefix(d.Get("name"), "computer_name_prefix") + if len(errs) > 0 { + return fmt.Errorf("unable to assume default computer name prefix %s. Please adjust the %q, or specify an explicit %q", errs[0], "name", "computer_name_prefix") + } computerNamePrefix = name } diff --git a/azurerm/internal/services/compute/resource_arm_windows_virtual_machine_scale_set.go b/azurerm/internal/services/compute/resource_arm_windows_virtual_machine_scale_set.go index 1d834071c844..a35c91173a5e 100644 --- a/azurerm/internal/services/compute/resource_arm_windows_virtual_machine_scale_set.go +++ b/azurerm/internal/services/compute/resource_arm_windows_virtual_machine_scale_set.go @@ -49,7 +49,7 @@ func resourceArmWindowsVirtualMachineScaleSet() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: ValidateWindowsName, + ValidateFunc: ValidateVmName, }, "resource_group_name": azure.SchemaResourceGroupName(), @@ -107,9 +107,8 @@ func resourceArmWindowsVirtualMachineScaleSet() *schema.Resource { // 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: ValidateWindowsName, + + ValidateFunc: ValidateWindowsComputerNamePrefix, }, "custom_data": base64.OptionalSchema(false), @@ -371,6 +370,10 @@ func resourceArmWindowsVirtualMachineScaleSetCreate(d *schema.ResourceData, meta if v, ok := d.GetOk("computer_name_prefix"); ok && len(v.(string)) > 0 { computerNamePrefix = v.(string) } else { + _, errs := ValidateWindowsComputerNamePrefix(d.Get("name"), "computer_name_prefix") + if len(errs) > 0 { + return fmt.Errorf("unable to assume default computer name prefix %s. Please adjust the %q, or specify an explicit %q", errs[0], "name", "computer_name_prefix") + } computerNamePrefix = name } diff --git a/azurerm/internal/services/compute/tests/linux_virtual_machine_resource_other_test.go b/azurerm/internal/services/compute/tests/linux_virtual_machine_resource_other_test.go index 9d1e5f1d4b35..2bb383f7b952 100644 --- a/azurerm/internal/services/compute/tests/linux_virtual_machine_resource_other_test.go +++ b/azurerm/internal/services/compute/tests/linux_virtual_machine_resource_other_test.go @@ -2,6 +2,7 @@ package tests import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -104,6 +105,22 @@ func TestAccLinuxVirtualMachine_otherComputerNameDefault(t *testing.T) { }) } +func TestAccLinuxVirtualMachine_otherComputerNameDefaultInvalid(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: checkLinuxVirtualMachineIsDestroyed, + Steps: []resource.TestStep{ + { + Config: testLinuxVirtualMachine_otherComputerNameDefaultInvalid(data), + ExpectError: regexp.MustCompile("unable to assume default computer name"), + }, + }, + }) +} + func TestAccLinuxVirtualMachine_otherComputerNameCustom(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine", "test") @@ -116,7 +133,7 @@ func TestAccLinuxVirtualMachine_otherComputerNameCustom(t *testing.T) { Config: testLinuxVirtualMachine_otherComputerNameCustom(data), Check: resource.ComposeTestCheckFunc( checkLinuxVirtualMachineExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "computer_name", "custom123"), + resource.TestCheckResourceAttr(data.ResourceName, "computer_name", "custom-linux-hostname-123"), ), }, data.ImportStep(), @@ -504,6 +521,41 @@ resource "azurerm_linux_virtual_machine" "test" { `, template, data.RandomInteger) } +func testLinuxVirtualMachine_otherComputerNameDefaultInvalid(data acceptance.TestData) string { + template := testLinuxVirtualMachine_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_linux_virtual_machine" "test" { + name = "acctestVM-this-name-too-long-to-be-a-linux-vm-computer-name-1234567890" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + admin_ssh_key { + username = "adminuser" + public_key = local.first_public_key + } + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } +} +`, template) +} + func testLinuxVirtualMachine_otherComputerNameCustom(data acceptance.TestData) string { template := testLinuxVirtualMachine_template(data) return fmt.Sprintf(` @@ -515,7 +567,7 @@ resource "azurerm_linux_virtual_machine" "test" { location = azurerm_resource_group.test.location size = "Standard_F2" admin_username = "adminuser" - computer_name = "custom123" + computer_name = "custom-linux-hostname-123" network_interface_ids = [ azurerm_network_interface.test.id, ] diff --git a/azurerm/internal/services/compute/tests/resource_arm_linux_virtual_machine_scale_set_other_test.go b/azurerm/internal/services/compute/tests/resource_arm_linux_virtual_machine_scale_set_other_test.go index a4ea0f662e90..9481d9b3b6c3 100644 --- a/azurerm/internal/services/compute/tests/resource_arm_linux_virtual_machine_scale_set_other_test.go +++ b/azurerm/internal/services/compute/tests/resource_arm_linux_virtual_machine_scale_set_other_test.go @@ -2,6 +2,7 @@ package tests import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -71,6 +72,22 @@ func TestAccAzureRMLinuxVirtualMachineScaleSet_otherComputerNamePrefix(t *testin }) } +func TestAccAzureRMLinuxVirtualMachineScaleSet_otherComputerNamePrefixInvalid(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine_scale_set", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMLinuxVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLinuxVirtualMachineScaleSet_otherComputerNamePrefixInvalid(data), + ExpectError: regexp.MustCompile("unable to assume default computer name prefix"), + }, + }, + }) +} + func TestAccAzureRMLinuxVirtualMachineScaleSet_otherCustomData(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine_scale_set", "test") @@ -629,7 +646,49 @@ resource "azurerm_linux_virtual_machine_scale_set" "test" { instances = 1 admin_username = "adminuser" admin_password = "P@ssword1234!" - computer_name_prefix = "morty" + computer_name_prefix = "my-linux-computer-name-prefix" + + disable_password_authentication = false + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + 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, data.RandomInteger) +} + +func testAccAzureRMLinuxVirtualMachineScaleSet_otherComputerNamePrefixInvalid(data acceptance.TestData) string { + template := testAccAzureRMLinuxVirtualMachineScaleSet_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_linux_virtual_machine_scale_set" "test" { + name = "acctestvmss-%d-too-long-to-be-a-computer-name-but-not-vmss-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!" disable_password_authentication = false diff --git a/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_disk_data_test.go b/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_disk_data_test.go index bed5fc86872e..4546be69df9d 100644 --- a/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_disk_data_test.go +++ b/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_disk_data_test.go @@ -479,10 +479,9 @@ func testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDisk_diskEncryptionSet // TODO: switch back to default location location := "westus2" - name := testAccAzureRMWindowsVirtualMachineScaleSet_vmName(data) return fmt.Sprintf(` locals { - vm_name = "%s" + vm_name = "acctestVM-%d" } data "azurerm_client_config" "current" {} @@ -550,7 +549,7 @@ resource "azurerm_subnet" "test" { virtual_network_name = azurerm_virtual_network.test.name address_prefix = "10.0.2.0/24" } -`, name, data.RandomInteger, location, data.RandomString, data.RandomInteger) +`, data.RandomInteger, data.RandomInteger, location, data.RandomString, data.RandomInteger) } func testAccAzureRMWindowsVirtualMachineScaleSet_disksDataDisk_diskEncryptionSetResource(data acceptance.TestData) string { diff --git a/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_disk_os_test.go b/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_disk_os_test.go index a45eedda0b0e..005f5c1cb66c 100644 --- a/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_disk_os_test.go +++ b/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_disk_os_test.go @@ -302,10 +302,9 @@ func testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDisk_diskEncryptionSetDe // TODO: switch back to default location location := "westus2" - name := testAccAzureRMWindowsVirtualMachineScaleSet_vmName(data) return fmt.Sprintf(` locals { - vm_name = "%s" + vm_name = "acctestVM-%d" } data "azurerm_client_config" "current" {} @@ -373,7 +372,7 @@ resource "azurerm_subnet" "test" { virtual_network_name = azurerm_virtual_network.test.name address_prefix = "10.0.2.0/24" } -`, name, data.RandomInteger, location, data.RandomString, data.RandomInteger) +`, data.RandomInteger, data.RandomInteger, location, data.RandomString, data.RandomInteger) } func testAccAzureRMWindowsVirtualMachineScaleSet_disksOSDisk_diskEncryptionSetResource(data acceptance.TestData) string { diff --git a/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_network_test.go b/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_network_test.go index d816c889b0b0..7dc0e598bdfe 100644 --- a/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_network_test.go +++ b/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_network_test.go @@ -621,7 +621,6 @@ resource "azurerm_windows_virtual_machine_scale_set" "test" { } func testAccAzureRMWindowsVirtualMachineScaleSet_networkApplicationGateway(data acceptance.TestData) string { - name := testAccAzureRMWindowsVirtualMachineScaleSet_vmName(data) return fmt.Sprintf(` provider "azurerm" { features {} @@ -718,7 +717,7 @@ resource "azurerm_application_gateway" "test" { } locals { - vm_name = "%s" + vm_name = "acctestVM-%d" } resource "azurerm_subnet" "other" { @@ -761,7 +760,7 @@ resource "azurerm_windows_virtual_machine_scale_set" "test" { } } } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, name) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) } func testAccAzureRMWindowsVirtualMachineScaleSet_networkApplicationSecurityGroup(data acceptance.TestData) string { diff --git a/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_other_test.go b/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_other_test.go index 78c1bab89ef2..725a0b6c08c9 100644 --- a/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_other_test.go +++ b/azurerm/internal/services/compute/tests/resource_arm_windows_virtual_machine_scale_set_other_test.go @@ -2,6 +2,7 @@ package tests import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -93,6 +94,22 @@ func TestAccAzureRMWindowsVirtualMachineScaleSet_otherComputerNamePrefix(t *test }) } +func TestAccAzureRMWindowsVirtualMachineScaleSet_otherComputerNamePrefixInvalid(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_virtual_machine_scale_set", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMWindowsVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachineScaleSet_otherComputerNamePrefixInvalid(data), + ExpectError: regexp.MustCompile("unable to assume default computer name prefix"), + }, + }, + }) +} + func TestAccAzureRMWindowsVirtualMachineScaleSet_otherCustomData(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_virtual_machine_scale_set", "test") @@ -788,7 +805,7 @@ func testAccAzureRMWindowsVirtualMachineScaleSet_otherComputerNamePrefix(data ac %s resource "azurerm_windows_virtual_machine_scale_set" "test" { - name = local.vm_name + name = "acctestVM-%d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location sku = "Standard_F2" @@ -820,7 +837,47 @@ resource "azurerm_windows_virtual_machine_scale_set" "test" { } } } -`, template) +`, template, data.RandomInteger) +} + +func testAccAzureRMWindowsVirtualMachineScaleSet_otherComputerNamePrefixInvalid(data acceptance.TestData) string { + template := testAccAzureRMWindowsVirtualMachineScaleSet_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_windows_virtual_machine_scale_set" "test" { + name = "acctestVM-%d" + 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, data.RandomInteger) } func testAccAzureRMWindowsVirtualMachineScaleSet_otherCustomData(data acceptance.TestData, customData string) string { diff --git a/azurerm/internal/services/compute/tests/windows_virtual_machine_resource_other_test.go b/azurerm/internal/services/compute/tests/windows_virtual_machine_resource_other_test.go index ad25e0639c9e..7ee83975f76a 100644 --- a/azurerm/internal/services/compute/tests/windows_virtual_machine_resource_other_test.go +++ b/azurerm/internal/services/compute/tests/windows_virtual_machine_resource_other_test.go @@ -2,6 +2,7 @@ package tests import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -138,6 +139,22 @@ func TestAccWindowsVirtualMachine_otherComputerNameDefault(t *testing.T) { }) } +func TestAccWindowsVirtualMachine_otherComputerNameDefaultInvalid(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_virtual_machine", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: checkWindowsVirtualMachineIsDestroyed, + Steps: []resource.TestStep{ + { + Config: testWindowsVirtualMachine_otherComputerNameDefaultInvalid(data), + ExpectError: regexp.MustCompile("unable to assume default computer name"), + }, + }, + }) +} + func TestAccWindowsVirtualMachine_otherComputerNameCustom(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_virtual_machine", "test") @@ -746,6 +763,37 @@ resource "azurerm_windows_virtual_machine" "test" { `, template) } +func testWindowsVirtualMachine_otherComputerNameDefaultInvalid(data acceptance.TestData) string { + template := testWindowsVirtualMachine_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_windows_virtual_machine" "test" { + name = "${local.vm_name}-this-too-long-to-be-a-computer-name" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@$$w0rd1234!" + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter" + version = "latest" + } +} +`, template) +} + func testWindowsVirtualMachine_otherComputerNameCustom(data acceptance.TestData) string { template := testWindowsVirtualMachine_template(data) return fmt.Sprintf(` diff --git a/azurerm/internal/services/compute/validation.go b/azurerm/internal/services/compute/validation.go index a3cdc9f3b334..b10ac6771fb7 100644 --- a/azurerm/internal/services/compute/validation.go +++ b/azurerm/internal/services/compute/validation.go @@ -8,7 +8,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) -func ValidateLinuxName(i interface{}, k string) (warnings []string, errors []error) { +func ValidateVmName(i interface{}, k string) (warnings []string, errors []error) { v, ok := i.(string) if !ok { errors = append(errors, fmt.Errorf("Expected %q to be a string but it wasn't!", k)) @@ -21,8 +21,55 @@ func ValidateLinuxName(i interface{}, k string) (warnings []string, errors []err return } - const maxLength = 64 - // The value must be between 1 and 64 (Linux) characters long. + const maxLength = 80 + // VM name can be 1-80 characters in length + if len(v) > maxLength { + errors = append(errors, fmt.Errorf("%q can be at most %d characters, got %d", k, maxLength, len(v))) + } + + if matched := regexp.MustCompile(`^[a-zA-Z0-9._-]+$`).Match([]byte(v)); !matched { + errors = append(errors, fmt.Errorf("%q may only contain alphanumeric characters, dots, dashes and underscores", k)) + } + + if matched := regexp.MustCompile(`^[a-zA-Z0-9]`).Match([]byte(v)); !matched { + errors = append(errors, fmt.Errorf("%q must begin with an alphanumeric character", k)) + } + + if matched := regexp.MustCompile(`[a-z0-9_]$`).Match([]byte(v)); !matched { + errors = append(errors, fmt.Errorf("%q must end with an alphanumeric character or underscore", k)) + } + + // Portal: Virtual machine name cannot contain only numbers. + if matched := regexp.MustCompile(`^\d+$`).Match([]byte(v)); matched { + errors = append(errors, fmt.Errorf("%q cannot contain only numbers", k)) + } + + return warnings, errors +} + +func ValidateLinuxComputerNameFull(i interface{}, k string) (warnings []string, errors []error) { + // Linux host name cannot exceed 64 characters in length + return ValidateLinuxComputerName(i, k, 64) +} + +func ValidateLinuxComputerNamePrefix(i interface{}, k string) (warnings []string, errors []error) { + // Linux host name prefix cannot exceed 58 characters in length + return ValidateLinuxComputerName(i, k, 58) +} + +func ValidateLinuxComputerName(i interface{}, k string, maxLength int) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string but it wasn't!", k)) + return + } + + // The value must not be empty. + if strings.TrimSpace(v) == "" { + errors = append(errors, fmt.Errorf("%q must not be empty", k)) + return + } + if len(v) > maxLength { errors = append(errors, fmt.Errorf("%q can be at most %d characters, got %d", k, maxLength, len(v))) } @@ -35,29 +82,29 @@ func ValidateLinuxName(i interface{}, k string) (warnings []string, errors []err errors = append(errors, fmt.Errorf("%q cannot end with an period or dash", k)) } - // Azure resource names cannot contain special characters \/""[]:|<>+=;,?*@& or begin with '_' or end with '.' or '-' - specialCharacters := `\/""[]:|<>+=;,?*@&` + // Linux host name cannot contain the following characters + specialCharacters := `\/"[]:|<>+=;,?*@&~!#$%^()_{}'` if strings.ContainsAny(v, specialCharacters) { errors = append(errors, fmt.Errorf("%q cannot contain the special characters: `%s`", k, specialCharacters)) } - // The value can only contain alphanumeric characters and can start with a number. - if matched := regexp.MustCompile(`^[a-zA-Z0-9-_.]+$`).Match([]byte(v)); !matched { - errors = append(errors, fmt.Errorf("%q may only contain alphanumeric characters, dashes and underscores", k)) - } + return warnings, errors +} - // Portal: Virtual machine name cannot contain only numbers. - if matched := regexp.MustCompile(`^\d+$`).Match([]byte(v)); matched { - errors = append(errors, fmt.Errorf("%q cannot contain only numbers", k)) - } +func ValidateWindowsComputerNameFull(i interface{}, k string) (warnings []string, errors []error) { + // Windows computer name cannot be more than 15 characters long + return ValidateWindowsComputerName(i, k, 15) +} - return warnings, errors +func ValidateWindowsComputerNamePrefix(i interface{}, k string) (warnings []string, errors []error) { + // Windows computer name prefix cannot be more than 9 characters long + return ValidateWindowsComputerName(i, k, 9) } -func ValidateWindowsName(i interface{}, k string) (warnings []string, errors []error) { +func ValidateWindowsComputerName(i interface{}, k string, maxLength int) (warnings []string, errors []error) { v, ok := i.(string) if !ok { - errors = append(errors, fmt.Errorf("Expected %q to be a string but it wasn't!", k)) + errors = append(errors, fmt.Errorf("expected %q to be a string but it wasn't!", k)) return } @@ -67,8 +114,6 @@ func ValidateWindowsName(i interface{}, k string) (warnings []string, errors []e return } - const maxLength = 15 - // The value must be between 1 and 15 (Windows) characters long. if len(v) > maxLength { errors = append(errors, fmt.Errorf("%q can be at most %d characters, got %d", k, maxLength, len(v))) } @@ -82,7 +127,7 @@ func ValidateWindowsName(i interface{}, k string) (warnings []string, errors []e errors = append(errors, fmt.Errorf("%q may only contain alphanumeric characters and dashes", k)) } - // Portal: Virtual machine name cannot contain only numbers. + // Windows computer name cannot contain only numbers if matched := regexp.MustCompile(`^\d+$`).Match([]byte(v)); matched { errors = append(errors, fmt.Errorf("%q cannot contain only numbers", k)) } diff --git a/azurerm/internal/services/compute/validation_test.go b/azurerm/internal/services/compute/validation_test.go index d60ff61d7c38..e30c65d49ff4 100644 --- a/azurerm/internal/services/compute/validation_test.go +++ b/azurerm/internal/services/compute/validation_test.go @@ -2,7 +2,7 @@ package compute import "testing" -func TestValidateLinuxName(t *testing.T) { +func TestValidateVmName(t *testing.T) { testData := []struct { input string expected bool @@ -18,30 +18,128 @@ func TestValidateLinuxName(t *testing.T) { expected: true, }, { - // can't start with an underscore + // 79 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyza", + expected: true, + }, + { + // 80 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab", + expected: true, + }, + { + // 81 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc", + expected: false, + }, + { + // may contain alphanumerics, dots, dashes and underscores + input: "hello_world7.goodbye-world4", + expected: true, + }, + { + // must begin with an alphanumeric input: "_hello", expected: false, }, + { + // can't end with a period + input: "hello.", + expected: false, + }, { // can't end with a dash input: "hello-", expected: false, }, + { + // can end with an underscore + input: "hello_", + expected: true, + }, { // can't contain an exclamation mark input: "hello!", expected: false, }, { - // dash in the middle - input: "malcolm-in-the-middle", + // start with a number + input: "0abc", + expected: true, + }, + { + // cannot contain only numbers + input: "12345", + expected: false, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + + _, errors := ValidateVmName(v.input, "name") + actual := len(errors) == 0 + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} + +func TestValidateLinuxComputerName(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 period input: "hello.", expected: false, }, + { + // can't end with a dash + input: "hello-", + expected: false, + }, + { + // can't contain an exclamation mark + input: "hello!", + expected: false, + }, + { + // or brackets + input: "hello[]", + expected: false, + }, + { + // or pipe + input: "hel|lo", + expected: false, + }, + { + // nor dollar + input: "dollar$bill", + expected: false, + }, + { + // dash in the middle + input: "malcolm-in-the-middle", + expected: true, + }, { // can have a dot in the middle input: "hello.world", @@ -52,11 +150,24 @@ func TestValidateLinuxName(t *testing.T) { input: "0abc", expected: true, }, - { - // cannot contain only numbers - input: "12345", - expected: false, - }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + + _, errors := ValidateLinuxComputerName(v.input, "computer_name", 100) + actual := len(errors) == 0 + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} + +func TestValidateLinuxComputerNameFull(t *testing.T) { + testData := []struct { + input string + expected bool + }{ { // 63 chars input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk", @@ -77,7 +188,40 @@ func TestValidateLinuxName(t *testing.T) { for _, v := range testData { t.Logf("[DEBUG] Testing %q..", v.input) - _, errors := ValidateLinuxName(v.input, "name") + _, errors := ValidateLinuxComputerNameFull(v.input, "computer_name") + actual := len(errors) == 0 + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} + +func TestValidateLinuxComputerNamePrefix(t *testing.T) { + testData := []struct { + input string + expected bool + }{ + { + // 57 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde", + expected: true, + }, + { + // 58 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef", + expected: true, + }, + { + // 59 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg", + expected: false, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + + _, errors := ValidateLinuxComputerNamePrefix(v.input, "computer_name") actual := len(errors) == 0 if v.expected != actual { t.Fatalf("Expected %t but got %t", v.expected, actual) @@ -85,7 +229,7 @@ func TestValidateLinuxName(t *testing.T) { } } -func TestValidateWindowsName(t *testing.T) { +func TestValidateWindowsComputerName(t *testing.T) { testData := []struct { input string expected bool @@ -145,6 +289,24 @@ func TestValidateWindowsName(t *testing.T) { input: "12345", expected: false, }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + + _, errors := ValidateWindowsComputerName(v.input, "computer_name", 100) + actual := len(errors) == 0 + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} + +func TestValidateWindowsComputerNameFull(t *testing.T) { + testData := []struct { + input string + expected bool + }{ { // 14 chars input: "abcdefghijklmn", @@ -165,7 +327,40 @@ func TestValidateWindowsName(t *testing.T) { for _, v := range testData { t.Logf("[DEBUG] Testing %q..", v.input) - _, errors := ValidateWindowsName(v.input, "name") + _, errors := ValidateWindowsComputerNameFull(v.input, "computer_name") + actual := len(errors) == 0 + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} + +func TestValidateWindowsComputerNamePrefix(t *testing.T) { + testData := []struct { + input string + expected bool + }{ + { + // 8 chars + input: "abcdefgh", + expected: true, + }, + { + // 9 chars + input: "abcdefghi", + expected: true, + }, + { + // 10 chars + input: "abcdefghij", + expected: false, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + + _, errors := ValidateWindowsComputerNamePrefix(v.input, "computer_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/windows_virtual_machine_resource.go b/azurerm/internal/services/compute/windows_virtual_machine_resource.go index 109d195fc63e..58012d741865 100644 --- a/azurerm/internal/services/compute/windows_virtual_machine_resource.go +++ b/azurerm/internal/services/compute/windows_virtual_machine_resource.go @@ -52,7 +52,7 @@ func resourceWindowsVirtualMachine() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: ValidateWindowsName, + ValidateFunc: ValidateVmName, }, "resource_group_name": azure.SchemaResourceGroupName(), @@ -129,9 +129,8 @@ func resourceWindowsVirtualMachine() *schema.Resource { // 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: ValidateWindowsName, + + ValidateFunc: ValidateWindowsComputerNameFull, }, "custom_data": base64.OptionalSchema(true), @@ -295,7 +294,7 @@ func resourceWindowsVirtualMachineCreate(d *schema.ResourceData, meta interface{ 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 %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("checking for existing Windows Virtual Machine %q (Resource Group %q): %+v", name, resourceGroup, err) } } @@ -319,6 +318,10 @@ func resourceWindowsVirtualMachineCreate(d *schema.ResourceData, meta interface{ if v, ok := d.GetOk("computer_name"); ok && len(v.(string)) > 0 { computerName = v.(string) } else { + _, errs := ValidateWindowsComputerNameFull(d.Get("name"), "computer_name") + if len(errs) > 0 { + return fmt.Errorf("unable to assume default computer name %s. Please adjust the %q, or specify an explicit %q", errs[0], "name", "computer_name") + } computerName = name } enableAutomaticUpdates := d.Get("enable_automatic_updates").(bool) @@ -326,7 +329,7 @@ func resourceWindowsVirtualMachineCreate(d *schema.ResourceData, meta interface{ identityRaw := d.Get("identity").([]interface{}) identity, err := expandVirtualMachineIdentity(identityRaw) if err != nil { - return fmt.Errorf("Error expanding `identity`: %+v", err) + return fmt.Errorf("expanding `identity`: %+v", err) } planRaw := d.Get("plan").([]interface{}) plan := expandPlan(planRaw) @@ -466,20 +469,20 @@ func resourceWindowsVirtualMachineCreate(d *schema.ResourceData, meta interface{ future, err := client.CreateOrUpdate(ctx, resourceGroup, name, params) if err != nil { - return fmt.Errorf("Error creating Windows Virtual Machine %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("creating Windows Virtual Machine %q (Resource Group %q): %+v", name, resourceGroup, err) } if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for creation of Windows Virtual Machine %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("waiting for creation of Windows Virtual Machine %q (Resource Group %q): %+v", name, resourceGroup, err) } read, err := client.Get(ctx, resourceGroup, name, "") if err != nil { - return fmt.Errorf("Error retrieving Windows Virtual Machine %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("retrieving Windows Virtual Machine %q (Resource Group %q): %+v", name, resourceGroup, err) } if read.ID == nil { - return fmt.Errorf("Error retrieving Windows Virtual Machine %q (Resource Group %q): `id` was nil", name, resourceGroup) + return fmt.Errorf("retrieving Windows Virtual Machine %q (Resource Group %q): `id` was nil", name, resourceGroup) } d.SetId(*read.ID) @@ -507,7 +510,7 @@ func resourceWindowsVirtualMachineRead(d *schema.ResourceData, meta interface{}) return nil } - return fmt.Errorf("Error retrieving Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } d.Set("name", id.Name) @@ -517,20 +520,20 @@ func resourceWindowsVirtualMachineRead(d *schema.ResourceData, meta interface{}) } if err := d.Set("identity", flattenVirtualMachineIdentity(resp.Identity)); err != nil { - return fmt.Errorf("Error setting `identity`: %+v", err) + return fmt.Errorf("setting `identity`: %+v", err) } if err := d.Set("plan", flattenPlan(resp.Plan)); err != nil { - return fmt.Errorf("Error setting `plan`: %+v", err) + return fmt.Errorf("setting `plan`: %+v", err) } if resp.VirtualMachineProperties == nil { - return fmt.Errorf("Error retrieving Windows Virtual Machine %q (Resource Group %q): `properties` was nil", id.Name, id.ResourceGroup) + return fmt.Errorf("retrieving Windows Virtual Machine %q (Resource Group %q): `properties` was nil", id.Name, id.ResourceGroup) } props := *resp.VirtualMachineProperties if err := d.Set("additional_capabilities", flattenVirtualMachineAdditionalCapabilities(props.AdditionalCapabilities)); err != nil { - return fmt.Errorf("Error setting `additional_capabilities`: %+v", err) + return fmt.Errorf("setting `additional_capabilities`: %+v", err) } availabilitySetId := "" @@ -540,7 +543,7 @@ func resourceWindowsVirtualMachineRead(d *schema.ResourceData, meta interface{}) d.Set("availability_set_id", availabilitySetId) if err := d.Set("boot_diagnostics", flattenBootDiagnostics(props.DiagnosticsProfile)); err != nil { - return fmt.Errorf("Error setting `boot_diagnostics`: %+v", err) + return fmt.Errorf("setting `boot_diagnostics`: %+v", err) } d.Set("eviction_policy", string(props.EvictionPolicy)) @@ -558,7 +561,7 @@ func resourceWindowsVirtualMachineRead(d *schema.ResourceData, meta interface{}) if profile := props.NetworkProfile; profile != nil { if err := d.Set("network_interface_ids", flattenVirtualMachineNetworkInterfaceIDs(props.NetworkProfile.NetworkInterfaces)); err != nil { - return fmt.Errorf("Error setting `network_interface_ids`: %+v", err) + return fmt.Errorf("setting `network_interface_ids`: %+v", err) } } @@ -575,7 +578,7 @@ func resourceWindowsVirtualMachineRead(d *schema.ResourceData, meta interface{}) if config := profile.WindowsConfiguration; config != nil { if err := d.Set("additional_unattend_content", flattenAdditionalUnattendContent(config.AdditionalUnattendContent, d)); err != nil { - return fmt.Errorf("Error setting `additional_unattend_content`: %+v", err) + return fmt.Errorf("setting `additional_unattend_content`: %+v", err) } d.Set("enable_automatic_updates", config.EnableAutomaticUpdates) @@ -584,12 +587,12 @@ func resourceWindowsVirtualMachineRead(d *schema.ResourceData, meta interface{}) d.Set("timezone", config.TimeZone) if err := d.Set("winrm_listener", flattenWinRMListener(config.WinRM)); err != nil { - return fmt.Errorf("Error setting `winrm_listener`: %+v", err) + return fmt.Errorf("setting `winrm_listener`: %+v", err) } } if err := d.Set("secret", flattenWindowsSecrets(profile.Secrets)); err != nil { - return fmt.Errorf("Error setting `secret`: %+v", err) + return fmt.Errorf("setting `secret`: %+v", err) } } // Resources created with azurerm_virtual_machine have priority set to "" @@ -609,10 +612,10 @@ func resourceWindowsVirtualMachineRead(d *schema.ResourceData, meta interface{}) // the storage_account_type isn't returned so we need to look it up flattenedOSDisk, err := flattenVirtualMachineOSDisk(ctx, disksClient, profile.OsDisk) if err != nil { - return fmt.Errorf("Error flattening `os_disk`: %+v", err) + return fmt.Errorf("flattening `os_disk`: %+v", err) } if err := d.Set("os_disk", flattenedOSDisk); err != nil { - return fmt.Errorf("Error settings `os_disk`: %+v", err) + return fmt.Errorf("settings `os_disk`: %+v", err) } var storageImageId string @@ -622,7 +625,7 @@ func resourceWindowsVirtualMachineRead(d *schema.ResourceData, meta interface{}) d.Set("source_image_id", storageImageId) if err := d.Set("source_image_reference", flattenSourceImageReference(profile.ImageReference)); err != nil { - return fmt.Errorf("Error setting `source_image_reference`: %+v", err) + return fmt.Errorf("setting `source_image_reference`: %+v", err) } } @@ -667,13 +670,13 @@ func resourceWindowsVirtualMachineUpdate(d *schema.ResourceData, meta interface{ return nil } - return fmt.Errorf("Error retrieving Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } log.Printf("[DEBUG] Retrieving InstanceView for Windows Virtual Machine %q (Resource Group %q)..", id.Name, id.ResourceGroup) instanceView, err := client.InstanceView(ctx, id.ResourceGroup, id.Name) if err != nil { - return fmt.Errorf("Error retrieving InstanceView for Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving InstanceView for Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } shouldTurnBackOn := virtualMachineShouldBeStarted(instanceView) @@ -722,7 +725,7 @@ func resourceWindowsVirtualMachineUpdate(d *schema.ResourceData, meta interface{ identityRaw := d.Get("identity").([]interface{}) identity, err := expandVirtualMachineIdentity(identityRaw) if err != nil { - return fmt.Errorf("Error expanding `identity`: %+v", err) + return fmt.Errorf("expanding `identity`: %+v", err) } update.Identity = identity } @@ -789,7 +792,7 @@ func resourceWindowsVirtualMachineUpdate(d *schema.ResourceData, meta interface{ availableOnThisHost := false sizes, err := client.ListAvailableSizes(ctx, id.ResourceGroup, id.Name) if err != nil { - return fmt.Errorf("Error retrieving available sizes for Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving available sizes for Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if sizes.Value != nil { @@ -831,11 +834,11 @@ func resourceWindowsVirtualMachineUpdate(d *schema.ResourceData, meta interface{ forceShutdown := false future, err := client.PowerOff(ctx, id.ResourceGroup, id.Name, utils.Bool(forceShutdown)) if err != nil { - return fmt.Errorf("Error sending Power Off to Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("sending Power Off to Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for Power Off of Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for Power Off of Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } log.Printf("[DEBUG] Shut Down Windows Virtual Machine %q (Resource Group %q)..", id.Name, id.ResourceGroup) @@ -846,11 +849,11 @@ func resourceWindowsVirtualMachineUpdate(d *schema.ResourceData, meta interface{ log.Printf("[DEBUG] Deallocating Windows Virtual Machine %q (Resource Group %q)..", id.Name, id.ResourceGroup) future, err := client.Deallocate(ctx, id.ResourceGroup, id.Name) if err != nil { - return fmt.Errorf("Error Deallocating Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("Deallocating Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for Deallocation of Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for Deallocation of Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } log.Printf("[DEBUG] Deallocated Windows Virtual Machine %q (Resource Group %q)..", id.Name, id.ResourceGroup) @@ -878,11 +881,11 @@ func resourceWindowsVirtualMachineUpdate(d *schema.ResourceData, meta interface{ future, err := disksClient.Update(ctx, id.ResourceGroup, diskName, update) if err != nil { - return fmt.Errorf("Error resizing OS Disk %q for Windows Virtual Machine %q (Resource Group %q): %+v", diskName, id.Name, id.ResourceGroup, err) + return fmt.Errorf("resizing OS Disk %q for Windows Virtual Machine %q (Resource Group %q): %+v", diskName, id.Name, id.ResourceGroup, err) } if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for resize of OS Disk %q for Windows Virtual Machine %q (Resource Group %q): %+v", diskName, id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for resize of OS Disk %q for Windows Virtual Machine %q (Resource Group %q): %+v", diskName, id.Name, id.ResourceGroup, err) } log.Printf("[DEBUG] Resized OS Disk %q for Windows Virtual Machine %q (Resource Group %q) to %dGB.", diskName, id.Name, id.ResourceGroup, newSize) @@ -892,11 +895,11 @@ func resourceWindowsVirtualMachineUpdate(d *schema.ResourceData, meta interface{ log.Printf("[DEBUG] Updating Windows Virtual Machine %q (Resource Group %q)..", id.Name, id.ResourceGroup) future, err := client.Update(ctx, id.ResourceGroup, id.Name, update) if err != nil { - return fmt.Errorf("Error updating Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("updating Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for update of Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for update of Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } log.Printf("[DEBUG] Updated Windows Virtual Machine %q (Resource Group %q).", id.Name, id.ResourceGroup) @@ -907,11 +910,11 @@ func resourceWindowsVirtualMachineUpdate(d *schema.ResourceData, meta interface{ log.Printf("[DEBUG] Starting Windows Virtual Machine %q (Resource Group %q)..", id.Name, id.ResourceGroup) future, err := client.Start(ctx, id.ResourceGroup, id.Name) if err != nil { - return fmt.Errorf("Error starting Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("starting Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for start of Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for start of Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } log.Printf("[DEBUG] Started Windows Virtual Machine %q (Resource Group %q)..", id.Name, id.ResourceGroup) @@ -940,7 +943,7 @@ func resourceWindowsVirtualMachineDelete(d *schema.ResourceData, meta interface{ return nil } - return fmt.Errorf("Error retrieving Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("retrieving Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } // ISSUE: XXX @@ -952,20 +955,20 @@ func resourceWindowsVirtualMachineDelete(d *schema.ResourceData, meta interface{ skipShutdown := true powerOffFuture, err := client.PowerOff(ctx, id.ResourceGroup, id.Name, utils.Bool(skipShutdown)) if err != nil { - return fmt.Errorf("Error powering off Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("powering off Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if err := powerOffFuture.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for power off of Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for power off of Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } log.Printf("[DEBUG] Powered Off Windows Virtual Machine %q (Resource Group %q).", id.Name, id.ResourceGroup) log.Printf("[DEBUG] Deleting Windows Virtual Machine %q (Resource Group %q)..", id.Name, id.ResourceGroup) deleteFuture, err := client.Delete(ctx, id.ResourceGroup, id.Name) if err != nil { - return fmt.Errorf("Error deleting Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("deleting Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if err := deleteFuture.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for deletion of Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + return fmt.Errorf("waiting for deletion of Windows Virtual Machine %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } log.Printf("[DEBUG] Deleted Windows Virtual Machine %q (Resource Group %q).", id.Name, id.ResourceGroup) @@ -989,12 +992,12 @@ func resourceWindowsVirtualMachineDelete(d *schema.ResourceData, meta interface{ diskDeleteFuture, err := disksClient.Delete(ctx, diskId.ResourceGroup, diskId.Name) if err != nil { if !response.WasNotFound(diskDeleteFuture.Response()) { - return fmt.Errorf("Error deleting OS Disk %q (Resource Group %q) for Windows Virtual Machine %q (Resource Group %q): %+v", diskId.Name, diskId.ResourceGroup, id.Name, id.ResourceGroup, err) + return fmt.Errorf("deleting OS Disk %q (Resource Group %q) for Windows Virtual Machine %q (Resource Group %q): %+v", diskId.Name, diskId.ResourceGroup, id.Name, id.ResourceGroup, err) } } if !response.WasNotFound(diskDeleteFuture.Response()) { if err := diskDeleteFuture.WaitForCompletionRef(ctx, disksClient.Client); err != nil { - return fmt.Errorf("Error OS Disk %q (Resource Group %q) for Windows Virtual Machine %q (Resource Group %q): %+v", diskId.Name, diskId.ResourceGroup, id.Name, id.ResourceGroup, err) + return fmt.Errorf("OS Disk %q (Resource Group %q) for Windows Virtual Machine %q (Resource Group %q): %+v", diskId.Name, diskId.ResourceGroup, id.Name, id.ResourceGroup, err) } } diff --git a/website/docs/r/linux_virtual_machine.html.markdown b/website/docs/r/linux_virtual_machine.html.markdown index 7e3b0b310126..bd7fa6074832 100644 --- a/website/docs/r/linux_virtual_machine.html.markdown +++ b/website/docs/r/linux_virtual_machine.html.markdown @@ -124,7 +124,7 @@ The following arguments are supported: * `boot_diagnostics` - (Optional) A `boot_diagnostics` block as defined below. -* `computer_name` - (Optional) Specifies the Hostname which should be used for this Virtual Machine. If unspecified this defaults to the value for the `name` field. Changing this forces a new resource to be created. +* `computer_name` - (Optional) Specifies the Hostname which should be used for this Virtual Machine. If unspecified this defaults to the value for the `name` field. If the value of the `name` field is not a valid `computer_name`, then you must specify `computer_name`. Changing this forces a new resource to be created. * `custom_data` - (Optional) The Base64-Encoded Custom Data which should be used for this Virtual Machine. Changing this forces a new resource to be created. 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 b8b52404b7a6..7ec83988addd 100644 --- a/website/docs/r/linux_virtual_machine_scale_set.html.markdown +++ b/website/docs/r/linux_virtual_machine_scale_set.html.markdown @@ -128,7 +128,7 @@ The following arguments are supported: * `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. +* `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. If the value of the `name` field is not a valid `computer_name_prefix`, then you must specify `computer_name_prefix`. * `custom_data` - (Optional) The Base64-Encoded Custom Data which should be used for this Virtual Machine Scale Set. diff --git a/website/docs/r/windows_virtual_machine.html.markdown b/website/docs/r/windows_virtual_machine.html.markdown index 54907eeb9499..c061e023baec 100644 --- a/website/docs/r/windows_virtual_machine.html.markdown +++ b/website/docs/r/windows_virtual_machine.html.markdown @@ -115,7 +115,7 @@ The following arguments are supported: * `boot_diagnostics` - (Optional) A `boot_diagnostics` block as defined below. -* `computer_name` - (Optional) Specifies the Hostname which should be used for this Virtual Machine. If unspecified this defaults to the value for the `name` field. Changing this forces a new resource to be created. +* `computer_name` - (Optional) Specifies the Hostname which should be used for this Virtual Machine. If unspecified this defaults to the value for the `name` field. If the value of the `name` field is not a valid `computer_name`, then you must specify `computer_name`. Changing this forces a new resource to be created. * `custom_data` - (Optional) The Base64-Encoded Custom Data which should be used for this Virtual Machine. Changing this forces a new resource to be created. diff --git a/website/docs/r/windows_virtual_machine_scale_set.html.markdown b/website/docs/r/windows_virtual_machine_scale_set.html.markdown index 165776ab75ef..4911dd804da5 100644 --- a/website/docs/r/windows_virtual_machine_scale_set.html.markdown +++ b/website/docs/r/windows_virtual_machine_scale_set.html.markdown @@ -118,7 +118,7 @@ The following arguments are supported: * `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. +* `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. If the value of the `name` field is not a valid `computer_name_prefix`, then you must specify `computer_name_prefix`. * `custom_data` - (Optional) The Base64-Encoded Custom Data which should be used for this Virtual Machine Scale Set.