provider/azurerm: VM Scale Sets - import support + fixes (#13464)

* Ensuring we base64 decode the custom data if it's base64 encoded

* Import support for VM Scale Sets

* Updating the docs to mention Import support

* Fixes #13009, where the SSH Keys would be set at the incorrect index

(leaving a null entry at the start, causing a crash on the second apply)

* Adding tests to cover the updating use-case

* Adding an import linux test

* Storing the base64 encoded value
Making custom_data a force new, since it an't be updated

* Updating the docs
This commit is contained in:
Tom Harvey 2017-04-16 23:37:28 +01:00 committed by Paul Stack
parent 1af649ed5a
commit f7f800bdfb
5 changed files with 747 additions and 313 deletions

View File

@ -0,0 +1,135 @@
package azurerm
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccAzureRMVirtualMachineScaleSet_importBasic(t *testing.T) {
resourceName := "azurerm_virtual_machine_scale_set.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccAzureRMVirtualMachineScaleSet_basic, ri, ri, ri, ri, ri, ri, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMVirtualMachineScaleSetDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccAzureRMVirtualMachineScaleSet_importLinux(t *testing.T) {
resourceName := "azurerm_virtual_machine_scale_set.test"
ri := acctest.RandInt()
config := testAccAzureRMVirtualMachineScaleSet_linux(ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMVirtualMachineScaleSetDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccAzureRMVirtualMachineScaleSet_importLoadBalancer(t *testing.T) {
resourceName := "azurerm_virtual_machine_scale_set.test"
ri := acctest.RandInt()
config := fmt.Sprintf(testAccAzureRMVirtualMachineScaleSetLoadbalancerTemplate, ri, ri, ri, ri, ri, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMVirtualMachineScaleSetDestroy,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccAzureRMVirtualMachineScaleSet_importOverProvision(t *testing.T) {
ri := acctest.RandInt()
config := fmt.Sprintf(testAccAzureRMVirtualMachineScaleSetOverprovisionTemplate, ri, ri, ri, ri, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMVirtualMachineScaleSetDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMVirtualMachineScaleSetExists("azurerm_virtual_machine_scale_set.test"),
testCheckAzureRMVirtualMachineScaleSetOverprovision("azurerm_virtual_machine_scale_set.test"),
),
},
},
})
}
func TestAccAzureRMVirtualMachineScaleSet_importExtension(t *testing.T) {
ri := acctest.RandInt()
config := fmt.Sprintf(testAccAzureRMVirtualMachineScaleSetExtensionTemplate, ri, ri, ri, ri, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMVirtualMachineScaleSetDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMVirtualMachineScaleSetExists("azurerm_virtual_machine_scale_set.test"),
testCheckAzureRMVirtualMachineScaleSetExtension("azurerm_virtual_machine_scale_set.test"),
),
},
},
})
}
func TestAccAzureRMVirtualMachineScaleSet_importMultipleExtensions(t *testing.T) {
ri := acctest.RandInt()
config := fmt.Sprintf(testAccAzureRMVirtualMachineScaleSetMultipleExtensionsTemplate, ri, ri, ri, ri, ri, ri)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMVirtualMachineScaleSetDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMVirtualMachineScaleSetExists("azurerm_virtual_machine_scale_set.test"),
testCheckAzureRMVirtualMachineScaleSetExtension("azurerm_virtual_machine_scale_set.test"),
),
},
},
})
}

View File

@ -346,7 +346,7 @@ func userDataStateFunc(v interface{}) string {
}
}
// Base64Encode encodes data if the input isn't already encoded using
// base64Encode encodes data if the input isn't already encoded using
// base64.StdEncoding.EncodeToString. If the input is already base64 encoded,
// return the original input unchanged.
func base64Encode(data string) string {

View File

@ -19,6 +19,9 @@ func resourceArmVirtualMachineScaleSet() *schema.Resource {
Read: resourceArmVirtualMachineScaleSetRead,
Update: resourceArmVirtualMachineScaleSetCreate,
Delete: resourceArmVirtualMachineScaleSetDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
@ -96,6 +99,7 @@ func resourceArmVirtualMachineScaleSet() *schema.Resource {
"custom_data": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
StateFunc: userDataStateFunc,
},
},
@ -497,8 +501,9 @@ func resourceArmVirtualMachineScaleSetRead(d *schema.ResourceData, meta interfac
return fmt.Errorf("Error making Read request on Azure Virtual Machine Scale Set %s: %s", name, err)
}
d.Set("location", resp.Location)
d.Set("name", resp.Name)
d.Set("resource_group_name", resGroup)
d.Set("location", azureRMNormalizeLocation(*resp.Location))
if err := d.Set("sku", flattenAzureRmVirtualMachineScaleSetSku(resp.Sku)); err != nil {
return fmt.Errorf("[DEBUG] Error setting Virtual Machine Scale Set Sku error: %#v", err)
@ -509,7 +514,12 @@ func resourceArmVirtualMachineScaleSetRead(d *schema.ResourceData, meta interfac
d.Set("upgrade_policy_mode", properties.UpgradePolicy.Mode)
d.Set("overprovision", properties.Overprovision)
if err := d.Set("os_profile", flattenAzureRMVirtualMachineScaleSetOsProfile(properties.VirtualMachineProfile.OsProfile)); err != nil {
osProfile, err := flattenAzureRMVirtualMachineScaleSetOsProfile(properties.VirtualMachineProfile.OsProfile)
if err != nil {
return fmt.Errorf("[DEBUG] Error flattening Virtual Machine Scale Set OS Profile. Error: %#v", err)
}
if err := d.Set("os_profile", osProfile); err != nil {
return fmt.Errorf("[DEBUG] Error setting Virtual Machine Scale Set OS Profile error: %#v", err)
}
@ -578,7 +588,7 @@ func flattenAzureRmVirtualMachineScaleSetOsProfileLinuxConfig(config *compute.Li
result["disable_password_authentication"] = *config.DisablePasswordAuthentication
if config.SSH != nil && len(*config.SSH.PublicKeys) > 0 {
ssh_keys := make([]map[string]interface{}, len(*config.SSH.PublicKeys))
ssh_keys := make([]map[string]interface{}, 0, len(*config.SSH.PublicKeys))
for _, i := range *config.SSH.PublicKeys {
key := make(map[string]interface{})
key["path"] = *i.Path
@ -710,7 +720,7 @@ func flattenAzureRmVirtualMachineScaleSetNetworkProfile(profile *compute.Virtual
return result
}
func flattenAzureRMVirtualMachineScaleSetOsProfile(profile *compute.VirtualMachineScaleSetOSProfile) []interface{} {
func flattenAzureRMVirtualMachineScaleSetOsProfile(profile *compute.VirtualMachineScaleSetOSProfile) ([]interface{}, error) {
result := make(map[string]interface{})
result["computer_name_prefix"] = *profile.ComputerNamePrefix
@ -720,7 +730,7 @@ func flattenAzureRMVirtualMachineScaleSetOsProfile(profile *compute.VirtualMachi
result["custom_data"] = *profile.CustomData
}
return []interface{}{result}
return []interface{}{result}, nil
}
func flattenAzureRmVirtualMachineScaleSetStorageProfileOSDisk(profile *compute.VirtualMachineScaleSetOSDisk) []interface{} {
@ -849,7 +859,11 @@ func resourceArmVirtualMachineScaleSetsOsProfileHash(v interface{}) int {
buf.WriteString(fmt.Sprintf("%s-", m["computer_name_prefix"].(string)))
buf.WriteString(fmt.Sprintf("%s-", m["admin_username"].(string)))
if m["custom_data"] != nil {
buf.WriteString(fmt.Sprintf("%s-", m["custom_data"].(string)))
customData := m["custom_data"].(string)
if !isBase64Encoded(customData) {
customData = base64Encode(customData)
}
buf.WriteString(fmt.Sprintf("%s-", customData))
}
return hashcode.String(buf.String())
}
@ -1076,12 +1090,12 @@ func expandAzureRmVirtualMachineScaleSetOsProfileLinuxConfig(d *schema.ResourceD
linuxConfig := osProfilesLinuxConfig[0].(map[string]interface{})
disablePasswordAuth := linuxConfig["disable_password_authentication"].(bool)
config := &compute.LinuxConfiguration{
DisablePasswordAuthentication: &disablePasswordAuth,
}
linuxKeys := linuxConfig["ssh_keys"].([]interface{})
sshPublicKeys := make([]compute.SSHPublicKey, 0, len(linuxKeys))
for _, key := range linuxKeys {
if key == nil {
continue
}
sshKey := key.(map[string]interface{})
path := sshKey["path"].(string)
keyData := sshKey["key_data"].(string)
@ -1094,8 +1108,11 @@ func expandAzureRmVirtualMachineScaleSetOsProfileLinuxConfig(d *schema.ResourceD
sshPublicKeys = append(sshPublicKeys, sshPublicKey)
}
config.SSH = &compute.SSHConfiguration{
PublicKeys: &sshPublicKeys,
config := &compute.LinuxConfiguration{
DisablePasswordAuthentication: &disablePasswordAuth,
SSH: &compute.SSHConfiguration{
PublicKeys: &sshPublicKeys,
},
}
return config, nil

View File

@ -140,7 +140,7 @@ The following arguments are supported:
* `computer_name_prefix` - (Required) Specifies the computer name prefix for all of the virtual machines in the scale set. Computer name prefixes must be 1 to 15 characters long.
* `admin_username` - (Required) Specifies the administrator account name to use for all the instances of virtual machines in the scale set.
* `admin_password` - (Required) Specifies the administrator password to use for all the instances of virtual machines in a scale set..
* `custom_data` - (Optional) Specifies custom data to supply to the machine. On linux-based systems, this can be used as a cloud-init script. On other systems, this will be copied as a file on disk. Internally, Terraform will base64 encode this value before sending it to the API. The maximum length of the binary array is 65535 bytes.
* `custom_data` - (Optional) Specifies custom data to supply to the machine. On linux-based systems, this can be used as a cloud-init script. On other systems, this will be copied as a file on disk. Internally, Terraform will base64 encode this value before sending it to the API. The maximum length of the binary array is 65535 bytes. Changing this forces a new resource to be created.
`os_profile_secrets` supports the following:
@ -177,7 +177,7 @@ The following arguments are supported:
* `disable_password_authentication` - (Required) Specifies whether password authentication should be disabled.
* `ssh_keys` - (Optional) Specifies a collection of `path` and `key_data` to be placed on the virtual machine.
~> **Note:** Please note that the only allowed `path` is `/home/<username>/.ssh/authorized_keys` due to a limitation of Azure_
~> _**Note:** Please note that the only allowed `path` is `/home/<username>/.ssh/authorized_keys` due to a limitation of Azure_
`network_profile` supports the following:
@ -225,3 +225,12 @@ The following arguments are supported:
The following attributes are exported:
* `id` - The virtual machine scale set ID.
## Import
Virtual Machine Scale Sets can be imported using the `resource id`, e.g.
```
terraform import azurerm_virtual_machine_scale_set.scaleset1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Compute/virtualMachineScaleSets/scaleset1
```