From 03ad41a2642079e7787f5e208575fcaab6a609a7 Mon Sep 17 00:00:00 2001 From: stack72 Date: Tue, 17 May 2016 14:21:20 +0100 Subject: [PATCH] provider/azurerm: Add `azurerm_virtual_machine_scale_set` resource --- builtin/providers/azurerm/config.go | 7 + builtin/providers/azurerm/provider.go | 40 +- .../resource_arm_virtual_machine_scale_set.go | 1088 +++++++++++++++++ ...urce_arm_virtual_machine_scale_set_test.go | 288 +++++ .../virtual_machine_scale_sets.html.markdown | 221 ++++ website/source/layouts/azurerm.erb | 4 + 6 files changed, 1629 insertions(+), 19 deletions(-) create mode 100644 builtin/providers/azurerm/resource_arm_virtual_machine_scale_set.go create mode 100644 builtin/providers/azurerm/resource_arm_virtual_machine_scale_set_test.go create mode 100644 website/source/docs/providers/azurerm/r/virtual_machine_scale_sets.html.markdown diff --git a/builtin/providers/azurerm/config.go b/builtin/providers/azurerm/config.go index 535f03392b4a..4246f07d8ba2 100644 --- a/builtin/providers/azurerm/config.go +++ b/builtin/providers/azurerm/config.go @@ -27,6 +27,7 @@ type ArmClient struct { usageOpsClient compute.UsageOperationsClient vmExtensionImageClient compute.VirtualMachineExtensionImagesClient vmExtensionClient compute.VirtualMachineExtensionsClient + vmScaleSetClient compute.VirtualMachineScaleSetsClient vmImageClient compute.VirtualMachineImagesClient vmClient compute.VirtualMachinesClient @@ -166,6 +167,12 @@ func (c *Config) getArmClient() (*ArmClient, error) { vmic.Sender = autorest.CreateSender(withRequestLogging()) client.vmImageClient = vmic + vmssc := compute.NewVirtualMachineScaleSetsClient(c.SubscriptionID) + setUserAgent(&vmssc.Client) + vmssc.Authorizer = spt + vmssc.Sender = autorest.CreateSender(withRequestLogging()) + client.vmScaleSetClient = vmssc + vmc := compute.NewVirtualMachinesClient(c.SubscriptionID) setUserAgent(&vmc.Client) vmc.Authorizer = spt diff --git a/builtin/providers/azurerm/provider.go b/builtin/providers/azurerm/provider.go index 58ff3d2565a1..0dfdc07f7261 100644 --- a/builtin/providers/azurerm/provider.go +++ b/builtin/providers/azurerm/provider.go @@ -5,13 +5,14 @@ import ( "reflect" "strings" + "sync" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform/helper/mutexkv" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" riviera "github.com/jen20/riviera/azure" - "sync" ) // Provider returns a terraform.ResourceProvider. @@ -45,24 +46,25 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ // These resources use the Azure ARM SDK - "azurerm_availability_set": resourceArmAvailabilitySet(), - "azurerm_cdn_endpoint": resourceArmCdnEndpoint(), - "azurerm_cdn_profile": resourceArmCdnProfile(), - "azurerm_local_network_gateway": resourceArmLocalNetworkGateway(), - "azurerm_network_interface": resourceArmNetworkInterface(), - "azurerm_network_security_group": resourceArmNetworkSecurityGroup(), - "azurerm_network_security_rule": resourceArmNetworkSecurityRule(), - "azurerm_public_ip": resourceArmPublicIp(), - "azurerm_route": resourceArmRoute(), - "azurerm_route_table": resourceArmRouteTable(), - "azurerm_storage_account": resourceArmStorageAccount(), - "azurerm_storage_blob": resourceArmStorageBlob(), - "azurerm_storage_container": resourceArmStorageContainer(), - "azurerm_storage_queue": resourceArmStorageQueue(), - "azurerm_subnet": resourceArmSubnet(), - "azurerm_template_deployment": resourceArmTemplateDeployment(), - "azurerm_virtual_machine": resourceArmVirtualMachine(), - "azurerm_virtual_network": resourceArmVirtualNetwork(), + "azurerm_availability_set": resourceArmAvailabilitySet(), + "azurerm_cdn_endpoint": resourceArmCdnEndpoint(), + "azurerm_cdn_profile": resourceArmCdnProfile(), + "azurerm_local_network_gateway": resourceArmLocalNetworkGateway(), + "azurerm_network_interface": resourceArmNetworkInterface(), + "azurerm_network_security_group": resourceArmNetworkSecurityGroup(), + "azurerm_network_security_rule": resourceArmNetworkSecurityRule(), + "azurerm_public_ip": resourceArmPublicIp(), + "azurerm_route": resourceArmRoute(), + "azurerm_route_table": resourceArmRouteTable(), + "azurerm_storage_account": resourceArmStorageAccount(), + "azurerm_storage_blob": resourceArmStorageBlob(), + "azurerm_storage_container": resourceArmStorageContainer(), + "azurerm_storage_queue": resourceArmStorageQueue(), + "azurerm_subnet": resourceArmSubnet(), + "azurerm_template_deployment": resourceArmTemplateDeployment(), + "azurerm_virtual_machine": resourceArmVirtualMachine(), + "azurerm_virtual_machine_scale_set": resourceArmVirtualMachineScaleSet(), + "azurerm_virtual_network": resourceArmVirtualNetwork(), // These resources use the Riviera SDK "azurerm_dns_a_record": resourceArmDnsARecord(), diff --git a/builtin/providers/azurerm/resource_arm_virtual_machine_scale_set.go b/builtin/providers/azurerm/resource_arm_virtual_machine_scale_set.go new file mode 100644 index 000000000000..3df0603e3dc8 --- /dev/null +++ b/builtin/providers/azurerm/resource_arm_virtual_machine_scale_set.go @@ -0,0 +1,1088 @@ +package azurerm + +import ( + "bytes" + "fmt" + "log" + "net/http" + + "github.com/Azure/azure-sdk-for-go/arm/compute" + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceArmVirtualMachineScaleSet() *schema.Resource { + return &schema.Resource{ + Create: resourceArmVirtualMachineScaleSetCreate, + Read: resourceArmVirtualMachineScaleSetRead, + Update: resourceArmVirtualMachineScaleSetCreate, + Delete: resourceArmVirtualMachineScaleSetDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "location": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + StateFunc: azureRMNormalizeLocation, + }, + + "resource_group_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "sku": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "tier": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "capacity": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + }, + }, + }, + Set: resourceArmVirtualMachineScaleSetSkuHash, + }, + + "upgrade_policy_mode": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "virtual_machine_os_profile": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "computer_name_prefix": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "admin_username": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "admin_password": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "custom_data": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Set: resourceArmVirtualMachineScaleSetsOsProfileHash, + }, + + "virtual_machine_os_profile_secrets": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "source_vault_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "vault_certificates": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "certificate_url": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "certificate_store": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + + "virtual_machine_os_profile_windows_config": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "provision_vm_agent": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + "enable_automatic_upgrades": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + "winrm": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "protocol": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "certificate_url": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "additional_unattend_config": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "pass": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "component": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "setting_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "content": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + }, + Set: resourceArmVirtualMachineScaleSetOsProfileLWindowsConfigHash, + }, + + "virtual_machine_os_profile_linux_config": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "disable_password_authentication": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, + "ssh_keys": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "path": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "key_data": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + Set: resourceArmVirtualMachineScaleSetOsProfileLinuxConfigHash, + }, + + "virtual_machine_network_profile": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "primary": &schema.Schema{ + Type: schema.TypeBool, + Required: true, + }, + + "ip_configuration": &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "subnet_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "load_balancer_backend_address_pool_ids": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + }, + }, + }, + }, + }, + Set: resourceArmVirtualMachineScaleSetNetworkConfigurationHash, + }, + + "virtual_machine_storage_profile_os_disk": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "image": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "vhd_containers": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + + "caching": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "os_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "create_option": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + Set: resourceArmVirtualMachineScaleSetStorageProfileOsDiskHash, + }, + + "virtual_machine_storage_profile_image_reference": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "publisher": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "offer": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "sku": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "version": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + Set: resourceArmVirtualMachineScaleSetStorageProfileImageReferenceHash, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceArmVirtualMachineScaleSetCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient) + vmScaleSetClient := client.vmScaleSetClient + + log.Printf("[INFO] preparing arguments for Azure ARM Virtual Machine Scale Set creation.") + + name := d.Get("name").(string) + location := d.Get("location").(string) + resGroup := d.Get("resource_group_name").(string) + tags := d.Get("tags").(map[string]interface{}) + + sku, err := expandVirtualMachineScaleSetSku(d) + if err != nil { + return err + } + + storageProfile := compute.VirtualMachineScaleSetStorageProfile{} + osDisk, err := expandAzureRMVirtualMachineScaleSetsStorageProfileOsDisk(d) + if err != nil { + return err + } + storageProfile.OsDisk = osDisk + if _, ok := d.GetOk("virtual_machine_storage_profile_image_reference"); ok { + imageRef, err := expandAzureRmVirtualMachineScaleSetStorageProfileImageReference(d) + if err != nil { + return err + } + storageProfile.ImageReference = imageRef + } + + osProfile, err := expandAzureRMVirtualMachineScaleSetsOsProfile(d) + if err != nil { + return err + } + + updatePolicy := d.Get("upgrade_policy_mode").(string) + scaleSetProps := compute.VirtualMachineScaleSetProperties{ + UpgradePolicy: &compute.UpgradePolicy{ + Mode: compute.UpgradeMode(updatePolicy), + }, + VirtualMachineProfile: &compute.VirtualMachineScaleSetVMProfile{ + NetworkProfile: expandAzureRmVirtualMachineScaleSetNetworkProfile(d), + StorageProfile: &storageProfile, + OsProfile: osProfile, + }, + } + + scaleSetParams := compute.VirtualMachineScaleSet{ + Name: &name, + Location: &location, + Tags: expandTags(tags), + Sku: sku, + Properties: &scaleSetProps, + } + _, vmErr := vmScaleSetClient.CreateOrUpdate(resGroup, name, scaleSetParams, make(chan struct{})) + if vmErr != nil { + return vmErr + } + + read, err := vmScaleSetClient.Get(resGroup, name) + if err != nil { + return err + } + if read.ID == nil { + return fmt.Errorf("Cannot read Virtual Machine Scale Set %s (resource group %s) ID", name, resGroup) + } + + d.SetId(*read.ID) + + return resourceArmVirtualMachineScaleSetRead(d, meta) +} + +func resourceArmVirtualMachineScaleSetRead(d *schema.ResourceData, meta interface{}) error { + vmScaleSetClient := meta.(*ArmClient).vmScaleSetClient + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + name := id.Path["virtualMachineScaleSets"] + + resp, err := vmScaleSetClient.Get(resGroup, name) + if resp.StatusCode == http.StatusNotFound { + log.Printf("[INFO] AzureRM Virtual Machine Scale Set (%s) Not Found. Removing from State", name) + d.SetId("") + return nil + } + if err != nil { + 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) + + if err := d.Set("sku", flattenAzureRmVirtualMachineScaleSetSku(resp.Sku)); err != nil { + return fmt.Errorf("[DEBUG] Error setting Virtual Machine Scale Set Sku error: %#v", err) + } + + d.Set("upgrade_policy_mode", resp.Properties.UpgradePolicy.Mode) + + if err := d.Set("virtual_machine_os_profile", flattenAzureRMVirtualMachineScaleSetOsProfile(resp.Properties.VirtualMachineProfile.OsProfile)); err != nil { + return fmt.Errorf("[DEBUG] Error setting Virtual Machine Scale Set OS Profile error: %#v", err) + } + + if resp.Properties.VirtualMachineProfile.OsProfile.Secrets != nil { + if err := d.Set("virtual_machine_os_profile_secrets", flattenAzureRmVirtualMachineScaleSetOsProfileSecrets(resp.Properties.VirtualMachineProfile.OsProfile.Secrets)); err != nil { + return fmt.Errorf("[DEBUG] Error setting Virtual Machine Scale Set OS Profile Secrets error: %#v", err) + } + } + + if resp.Properties.VirtualMachineProfile.OsProfile.WindowsConfiguration != nil { + if err := d.Set("virtual_machine_os_profile_windows_config", flattenAzureRmVirtualMachineScaleSetOsProfileWindowsConfig(resp.Properties.VirtualMachineProfile.OsProfile.WindowsConfiguration)); err != nil { + return fmt.Errorf("[DEBUG] Error setting Virtual Machine Scale Set OS Profile Windows config error: %#v", err) + } + } + + if resp.Properties.VirtualMachineProfile.OsProfile.LinuxConfiguration != nil { + if err := d.Set("virtual_machine_os_profile_linux_config", flattenAzureRmVirtualMachineScaleSetOsProfileLinuxConfig(resp.Properties.VirtualMachineProfile.OsProfile.LinuxConfiguration)); err != nil { + return fmt.Errorf("[DEBUG] Error setting Virtual Machine Scale Set OS Profile Windows config error: %#v", err) + } + } + + if err := d.Set("virtual_machine_network_profile", flattenAzureRmVirtualMachineScaleSetNetworkProfile(resp.Properties.VirtualMachineProfile.NetworkProfile)); err != nil { + return fmt.Errorf("[DEBUG] Error setting Virtual Machine Scale Set Network Profile error: %#v", err) + } + + if resp.Properties.VirtualMachineProfile.StorageProfile.ImageReference != nil { + if err := d.Set("virtual_machine_storage_profile_image_reference", flattenAzureRmVirtualMachineScaleSetStorageProfileImageReference(resp.Properties.VirtualMachineProfile.StorageProfile.ImageReference)); err != nil { + return fmt.Errorf("[DEBUG] Error setting Virtual Machine Scale Set Storage Profile Image Reference error: %#v", err) + } + } + + if err := d.Set("virtual_machine_storage_profile_os_disk", flattenAzureRmVirtualMachineScaleSetStorageProfileOSDisk(resp.Properties.VirtualMachineProfile.StorageProfile.OsDisk)); err != nil { + return fmt.Errorf("[DEBUG] Error setting Virtual Machine Scale Set Storage Profile OS Disk error: %#v", err) + } + + flattenAndSetTags(d, resp.Tags) + + return nil +} + +func resourceArmVirtualMachineScaleSetDelete(d *schema.ResourceData, meta interface{}) error { + vmScaleSetClient := meta.(*ArmClient).vmScaleSetClient + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + name := id.Path["virtualMachineScaleSets"] + + _, err = vmScaleSetClient.Delete(resGroup, name, make(chan struct{})) + + return err +} + +func flattenAzureRmVirtualMachineScaleSetOsProfileLinuxConfig(config *compute.LinuxConfiguration) []interface{} { + result := make(map[string]interface{}) + 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)) + for _, i := range *config.SSH.PublicKeys { + key := make(map[string]interface{}) + key["path"] = *i.Path + + if i.KeyData != nil { + key["key_data"] = *i.KeyData + } + + ssh_keys = append(ssh_keys, key) + } + + result["ssh_keys"] = ssh_keys + } + + return []interface{}{result} +} + +func flattenAzureRmVirtualMachineScaleSetOsProfileWindowsConfig(config *compute.WindowsConfiguration) []interface{} { + result := make(map[string]interface{}) + + if config.ProvisionVMAgent != nil { + result["provision_vm_agent"] = *config.ProvisionVMAgent + } + + if config.EnableAutomaticUpdates != nil { + result["enable_automatic_upgrades"] = *config.EnableAutomaticUpdates + } + + if config.WinRM != nil { + listeners := make([]map[string]interface{}, 0, len(*config.WinRM.Listeners)) + for _, i := range *config.WinRM.Listeners { + listener := make(map[string]interface{}) + listener["protocol"] = i.Protocol + + if i.CertificateURL != nil { + listener["certificate_url"] = *i.CertificateURL + } + + listeners = append(listeners, listener) + } + + result["winrm"] = listeners + } + + if config.AdditionalUnattendContent != nil { + content := make([]map[string]interface{}, 0, len(*config.AdditionalUnattendContent)) + for _, i := range *config.AdditionalUnattendContent { + c := make(map[string]interface{}) + c["pass"] = i.PassName + c["component"] = i.ComponentName + c["setting_name"] = i.SettingName + c["content"] = *i.Content + + content = append(content, c) + } + + result["additional_unattend_config"] = content + } + + return []interface{}{result} +} + +func flattenAzureRmVirtualMachineScaleSetOsProfileSecrets(secrets *[]compute.VaultSecretGroup) []map[string]interface{} { + result := make([]map[string]interface{}, 0, len(*secrets)) + for _, secret := range *secrets { + s := map[string]interface{}{ + "source_vault_id": *secret.SourceVault.ID, + } + + if secret.VaultCertificates != nil { + certs := make([]map[string]interface{}, 0, len(*secret.VaultCertificates)) + for _, cert := range *secret.VaultCertificates { + vaultCert := make(map[string]interface{}) + vaultCert["certificate_url"] = *cert.CertificateURL + + if cert.CertificateStore != nil { + vaultCert["certificate_store"] = *cert.CertificateStore + } + + certs = append(certs, vaultCert) + } + + s["vault_certificates"] = certs + } + + result = append(result, s) + } + return result +} + +func flattenAzureRmVirtualMachineScaleSetNetworkProfile(profile *compute.VirtualMachineScaleSetNetworkProfile) []map[string]interface{} { + networkConfigurations := profile.NetworkInterfaceConfigurations + result := make([]map[string]interface{}, 0, len(*networkConfigurations)) + for _, netConfig := range *networkConfigurations { + s := map[string]interface{}{ + "name": *netConfig.Name, + "primary": *netConfig.Properties.Primary, + } + + if netConfig.Properties.IPConfigurations != nil { + ipConfigs := make([]map[string]interface{}, 0, len(*netConfig.Properties.IPConfigurations)) + for _, ipConfig := range *netConfig.Properties.IPConfigurations { + config := make(map[string]interface{}) + config["name"] = *ipConfig.Name + + if ipConfig.Properties.Subnet != nil { + config["subnet_id"] = *ipConfig.Properties.Subnet.ID + } + + if ipConfig.Properties.LoadBalancerBackendAddressPools != nil { + addressPools := make([]string, 0, len(*ipConfig.Properties.LoadBalancerBackendAddressPools)) + for _, pool := range *ipConfig.Properties.LoadBalancerBackendAddressPools { + addressPools = append(addressPools, *pool.ID) + } + config["load_balancer_backend_address_pool_ids"] = addressPools + } + } + + s["ip_configuration"] = ipConfigs + } + + result = append(result, s) + } + + return result +} + +func flattenAzureRMVirtualMachineScaleSetOsProfile(profile *compute.VirtualMachineScaleSetOSProfile) []interface{} { + result := make(map[string]interface{}) + + result["computer_name_prefix"] = *profile.ComputerNamePrefix + result["admin_username"] = *profile.AdminUsername + + if profile.CustomData != nil { + result["custom_data"] = *profile.CustomData + } + + return []interface{}{result} +} + +func flattenAzureRmVirtualMachineScaleSetStorageProfileOSDisk(profile *compute.VirtualMachineScaleSetOSDisk) []interface{} { + result := make(map[string]interface{}) + result["name"] = *profile.Name + if profile.Image != nil { + result["image"] = *profile.Image.URI + } + + containers := make([]interface{}, 0, len(*profile.VhdContainers)) + for _, container := range *profile.VhdContainers { + containers = append(containers, container) + } + result["vhd_containers"] = schema.NewSet(schema.HashString, containers) + + result["caching"] = profile.Caching + result["create_option"] = profile.CreateOption + + return []interface{}{result} +} + +func flattenAzureRmVirtualMachineScaleSetStorageProfileImageReference(profile *compute.ImageReference) []interface{} { + result := make(map[string]interface{}) + result["publisher"] = *profile.Publisher + result["offer"] = *profile.Offer + result["sku"] = *profile.Sku + result["version"] = *profile.Version + + return []interface{}{result} +} + +func flattenAzureRmVirtualMachineScaleSetSku(sku *compute.Sku) []interface{} { + result := make(map[string]interface{}) + result["name"] = *sku.Name + result["capacity"] = *sku.Capacity + + if *sku.Tier != "" { + result["tier"] = *sku.Tier + } + + return []interface{}{result} +} + +func virtualMachineScaleSetStateRefreshFunc(client *ArmClient, resourceGroupName string, scaleSetName string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + res, err := client.vmScaleSetClient.Get(resourceGroupName, scaleSetName) + if err != nil { + return nil, "", fmt.Errorf("Error issuing read request in virtualMachineScaleSetStateRefreshFunc to Azure ARM for Virtual Machine Scale Set '%s' (RG: '%s'): %s", scaleSetName, resourceGroupName, err) + } + + return res, *res.Properties.ProvisioningState, nil + } +} + +func resourceArmVirtualMachineScaleSetStorageProfileImageReferenceHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["publisher"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["offer"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["sku"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["version"].(string))) + + return hashcode.String(buf.String()) +} + +func resourceArmVirtualMachineScaleSetSkuHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) + if m["tier"] != nil { + buf.WriteString(fmt.Sprintf("%s-", m["tier"].(string))) + } + buf.WriteString(fmt.Sprintf("%d-", m["capacity"].(int))) + + return hashcode.String(buf.String()) +} + +func resourceArmVirtualMachineScaleSetStorageProfileOsDiskHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) + + if m["image"] != nil { + buf.WriteString(fmt.Sprintf("%s-", m["image"].(string))) + } + if m["os_type"] != nil { + buf.WriteString(fmt.Sprintf("%s-", m["os_type"].(string))) + } + + return hashcode.String(buf.String()) +} + +func resourceArmVirtualMachineScaleSetNetworkConfigurationHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) + buf.WriteString(fmt.Sprintf("%t-", m["primary"].(bool))) + return hashcode.String(buf.String()) +} + +func resourceArmVirtualMachineScaleSetsOsProfileHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + 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))) + } + return hashcode.String(buf.String()) +} + +func resourceArmVirtualMachineScaleSetOsProfileLinuxConfigHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%t-", m["disable_password_authentication"].(bool))) + + return hashcode.String(buf.String()) +} + +func resourceArmVirtualMachineScaleSetOsProfileLWindowsConfigHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + if m["provision_vm_agent"] != nil { + buf.WriteString(fmt.Sprintf("%t-", m["provision_vm_agent"].(bool))) + } + if m["enable_automatic_upgrades"] != nil { + buf.WriteString(fmt.Sprintf("%t-", m["enable_automatic_upgrades"].(bool))) + } + return hashcode.String(buf.String()) +} + +func expandVirtualMachineScaleSetSku(d *schema.ResourceData) (*compute.Sku, error) { + skuConfig := d.Get("sku").(*schema.Set).List() + + config := skuConfig[0].(map[string]interface{}) + + name := config["name"].(string) + tier := config["tier"].(string) + capacity := int64(config["capacity"].(int)) + + sku := &compute.Sku{ + Name: &name, + Capacity: &capacity, + } + + if tier != "" { + sku.Tier = &tier + } + + return sku, nil +} + +func expandAzureRmVirtualMachineScaleSetNetworkProfile(d *schema.ResourceData) *compute.VirtualMachineScaleSetNetworkProfile { + scaleSetNetworkProfileConfigs := d.Get("virtual_machine_network_profile").(*schema.Set).List() + networkProfileConfig := make([]compute.VirtualMachineScaleSetNetworkConfiguration, 0, len(scaleSetNetworkProfileConfigs)) + + for _, npProfileConfig := range scaleSetNetworkProfileConfigs { + config := npProfileConfig.(map[string]interface{}) + + name := config["name"].(string) + primary := config["primary"].(bool) + + ipConfigurationConfigs := config["ip_configuration"].([]interface{}) + ipConfigurations := make([]compute.VirtualMachineScaleSetIPConfiguration, 0, len(ipConfigurationConfigs)) + for _, ipConfigConfig := range ipConfigurationConfigs { + ipconfig := ipConfigConfig.(map[string]interface{}) + name := ipconfig["name"].(string) + subnetId := ipconfig["subnet_id"].(string) + + ipConfiguration := compute.VirtualMachineScaleSetIPConfiguration{ + Name: &name, + Properties: &compute.VirtualMachineScaleSetIPConfigurationProperties{ + Subnet: &compute.APIEntityReference{ + ID: &subnetId, + }, + }, + } + //TODO: Add the support for the load balancers when it drops + //if v := ipconfig["load_balancer_backend_address_pool_ids"]; v != nil { + // + //} + + ipConfigurations = append(ipConfigurations, ipConfiguration) + } + + nProfile := compute.VirtualMachineScaleSetNetworkConfiguration{ + Name: &name, + Properties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{ + Primary: &primary, + IPConfigurations: &ipConfigurations, + }, + } + + networkProfileConfig = append(networkProfileConfig, nProfile) + } + + return &compute.VirtualMachineScaleSetNetworkProfile{ + NetworkInterfaceConfigurations: &networkProfileConfig, + } +} + +func expandAzureRMVirtualMachineScaleSetsOsProfile(d *schema.ResourceData) (*compute.VirtualMachineScaleSetOSProfile, error) { + osProfileConfigs := d.Get("virtual_machine_os_profile").(*schema.Set).List() + + osProfileConfig := osProfileConfigs[0].(map[string]interface{}) + namePrefix := osProfileConfig["computer_name_prefix"].(string) + username := osProfileConfig["admin_username"].(string) + password := osProfileConfig["admin_password"].(string) + customData := osProfileConfig["custom_data"].(string) + + osProfile := &compute.VirtualMachineScaleSetOSProfile{ + ComputerNamePrefix: &namePrefix, + AdminUsername: &username, + } + + if password != "" { + osProfile.AdminPassword = &password + } + + if customData != "" { + osProfile.CustomData = &customData + } + + if _, ok := d.GetOk("virtual_machine_os_profile_secrets"); ok { + secrets := expandAzureRmVirtualMachineScaleSetOsProfileSecrets(d) + if secrets != nil { + osProfile.Secrets = secrets + } + } + + if _, ok := d.GetOk("virtual_machine_os_profile_linux_config"); ok { + linuxConfig, err := expandAzureRmVirtualMachineScaleSetOsProfileLinuxConfig(d) + if err != nil { + return nil, err + } + osProfile.LinuxConfiguration = linuxConfig + } + + if _, ok := d.GetOk("virtual_machine_os_profile_windows_config"); ok { + winConfig, err := expandAzureRmVirtualMachineScaleSetOsProfileWindowsConfig(d) + if err != nil { + return nil, err + } + if winConfig != nil { + osProfile.WindowsConfiguration = winConfig + } + } + + return osProfile, nil +} + +func expandAzureRMVirtualMachineScaleSetsStorageProfileOsDisk(d *schema.ResourceData) (*compute.VirtualMachineScaleSetOSDisk, error) { + osDiskConfigs := d.Get("virtual_machine_storage_profile_os_disk").(*schema.Set).List() + + osDiskConfig := osDiskConfigs[0].(map[string]interface{}) + name := osDiskConfig["name"].(string) + image := osDiskConfig["image"].(string) + caching := osDiskConfig["caching"].(string) + osType := osDiskConfig["os_type"].(string) + createOption := osDiskConfig["create_option"].(string) + + var vhdContainers []string + containers := osDiskConfig["vhd_containers"].(*schema.Set).List() + for _, v := range containers { + str := v.(string) + vhdContainers = append(vhdContainers, str) + } + + osDisk := &compute.VirtualMachineScaleSetOSDisk{ + Name: &name, + Caching: compute.CachingTypes(caching), + OsType: compute.OperatingSystemTypes(osType), + CreateOption: compute.DiskCreateOptionTypes(createOption), + VhdContainers: &vhdContainers, + } + + if image != "" { + osDisk.Image = &compute.VirtualHardDisk{ + URI: &image, + } + } + + return osDisk, nil + +} + +func expandAzureRmVirtualMachineScaleSetStorageProfileImageReference(d *schema.ResourceData) (*compute.ImageReference, error) { + storageImageRefs := d.Get("virtual_machine_storage_profile_image_reference").(*schema.Set).List() + + storageImageRef := storageImageRefs[0].(map[string]interface{}) + + publisher := storageImageRef["publisher"].(string) + offer := storageImageRef["offer"].(string) + sku := storageImageRef["sku"].(string) + version := storageImageRef["version"].(string) + + return &compute.ImageReference{ + Publisher: &publisher, + Offer: &offer, + Sku: &sku, + Version: &version, + }, nil +} + +func expandAzureRmVirtualMachineScaleSetOsProfileLinuxConfig(d *schema.ResourceData) (*compute.LinuxConfiguration, error) { + osProfilesLinuxConfig := d.Get("virtual_machine_os_profile_linux_config").(*schema.Set).List() + + 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 { + sshKey := key.(map[string]interface{}) + path := sshKey["path"].(string) + keyData := sshKey["key_data"].(string) + + sshPublicKey := compute.SSHPublicKey{ + Path: &path, + KeyData: &keyData, + } + + sshPublicKeys = append(sshPublicKeys, sshPublicKey) + } + + config.SSH = &compute.SSHConfiguration{ + PublicKeys: &sshPublicKeys, + } + + return config, nil +} + +func expandAzureRmVirtualMachineScaleSetOsProfileWindowsConfig(d *schema.ResourceData) (*compute.WindowsConfiguration, error) { + osProfilesWindowsConfig := d.Get("virtual_machine_os_profile_windows_config").(*schema.Set).List() + + osProfileConfig := osProfilesWindowsConfig[0].(map[string]interface{}) + config := &compute.WindowsConfiguration{} + + if v := osProfileConfig["provision_vm_agent"]; v != nil { + provision := v.(bool) + config.ProvisionVMAgent = &provision + } + + if v := osProfileConfig["enable_automatic_upgrades"]; v != nil { + update := v.(bool) + config.EnableAutomaticUpdates = &update + } + + if v := osProfileConfig["winrm"]; v != nil { + winRm := v.(*schema.Set).List() + if len(winRm) > 0 { + winRmListners := make([]compute.WinRMListener, 0, len(winRm)) + for _, winRmConfig := range winRm { + config := winRmConfig.(map[string]interface{}) + + protocol := config["protocol"].(string) + winRmListner := compute.WinRMListener{ + Protocol: compute.ProtocolTypes(protocol), + } + if v := config["certificate_url"].(string); v != "" { + winRmListner.CertificateURL = &v + } + + winRmListners = append(winRmListners, winRmListner) + } + config.WinRM = &compute.WinRMConfiguration{ + Listeners: &winRmListners, + } + } + } + if v := osProfileConfig["additional_unattend_config"]; v != nil { + additionalConfig := v.(*schema.Set).List() + if len(additionalConfig) > 0 { + additionalConfigContent := make([]compute.AdditionalUnattendContent, 0, len(additionalConfig)) + for _, addConfig := range additionalConfig { + config := addConfig.(map[string]interface{}) + pass := config["pass"].(string) + component := config["component"].(string) + settingName := config["setting_name"].(string) + content := config["content"].(string) + + addContent := compute.AdditionalUnattendContent{ + PassName: compute.PassNames(pass), + ComponentName: compute.ComponentNames(component), + SettingName: compute.SettingNames(settingName), + Content: &content, + } + + additionalConfigContent = append(additionalConfigContent, addContent) + } + config.AdditionalUnattendContent = &additionalConfigContent + } + } + return config, nil +} + +func expandAzureRmVirtualMachineScaleSetOsProfileSecrets(d *schema.ResourceData) *[]compute.VaultSecretGroup { + secretsConfig := d.Get("virtual_machine_os_profile_secrets").(*schema.Set).List() + secrets := make([]compute.VaultSecretGroup, 0, len(secretsConfig)) + + for _, secretConfig := range secretsConfig { + config := secretConfig.(map[string]interface{}) + sourceVaultId := config["source_vault_id"].(string) + + vaultSecretGroup := compute.VaultSecretGroup{ + SourceVault: &compute.SubResource{ + ID: &sourceVaultId, + }, + } + + if v := config["vault_certificates"]; v != nil { + certsConfig := v.(*schema.Set).List() + certs := make([]compute.VaultCertificate, 0, len(certsConfig)) + for _, certConfig := range certsConfig { + config := certConfig.(map[string]interface{}) + + certUrl := config["certificate_url"].(string) + cert := compute.VaultCertificate{ + CertificateURL: &certUrl, + } + if v := config["certificate_store"].(string); v != "" { + cert.CertificateStore = &v + } + + certs = append(certs, cert) + } + vaultSecretGroup.VaultCertificates = &certs + } + + secrets = append(secrets, vaultSecretGroup) + } + + return &secrets +} diff --git a/builtin/providers/azurerm/resource_arm_virtual_machine_scale_set_test.go b/builtin/providers/azurerm/resource_arm_virtual_machine_scale_set_test.go new file mode 100644 index 000000000000..cd44d95989d2 --- /dev/null +++ b/builtin/providers/azurerm/resource_arm_virtual_machine_scale_set_test.go @@ -0,0 +1,288 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAzureRMVirtualMachineScaleSet_basicLinux(t *testing.T) { + ri := acctest.RandInt() + config := fmt.Sprintf(testAccAzureRMVirtualMachineScaleSet_basicLinux, 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, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMVirtualMachineScaleSetExists("azurerm_virtual_machine_scale_set.test"), + ), + }, + }, + }) +} + +func TestAccAzureRMVirtualMachineScaleSet_basicWindowsMachine(t *testing.T) { + ri := acctest.RandInt() + rs := acctest.RandString(6) + config := fmt.Sprintf(testAccAzureRMVirtualMachineScaleSet_basicWindows, ri, ri, ri, ri, ri, ri, rs, 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"), + ), + }, + }, + }) +} + +func testCheckAzureRMVirtualMachineScaleSetExists(name 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[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + name := rs.Primary.Attributes["name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for virtual machine: scale set %s", name) + } + + conn := testAccProvider.Meta().(*ArmClient).vmScaleSetClient + + resp, err := conn.Get(resourceGroup, name) + if err != nil { + return fmt.Errorf("Bad: Get on vmScaleSetClient: %s", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: VirtualMachineScaleSet %q (resource group: %q) does not exist", name, resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMVirtualMachineScaleSetDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).vmScaleSetClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_virtual_machine_scale_set" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.Get(resourceGroup, name) + + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Virtual Machine Scale Set still exists:\n%#v", resp.Properties) + } + } + + return nil +} + +var testAccAzureRMVirtualMachineScaleSet_basicLinux = ` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "West US" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%d" + address_space = ["10.0.0.0/16"] + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "acctni-%d" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_storage_account" "test" { + name = "accsa%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "westus" + account_type = "Standard_LRS" + + tags { + environment = "staging" + } +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_virtual_machine_scale_set" "test" { + name = "acctvmss-%d" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + upgrade_policy_mode = "Manual" + + sku { + name = "Standard_A0" + tier = "Standard" + capacity = 2 + } + + virtual_machine_os_profile { + computer_name_prefix = "testvm-%d" + admin_username = "myadmin" + admin_password = "Passwword1234" + } + + virtual_machine_network_profile { + name = "TestNetworkProfile-%d" + primary = true + ip_configuration { + name = "TestIPConfiguration" + subnet_id = "${azurerm_subnet.test.id}" + } + } + + virtual_machine_storage_profile_os_disk { + name = "osDiskProfile" + caching = "ReadWrite" + create_option = "FromImage" + vhd_containers = ["${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}"] + } + + virtual_machine_storage_profile_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "14.04.2-LTS" + version = "latest" + } +} +` + +var testAccAzureRMVirtualMachineScaleSet_basicWindows = ` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "West US" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%d" + address_space = ["10.0.0.0/16"] + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "acctni-%d" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_storage_account" "test" { + name = "accsa%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "westus" + account_type = "Standard_LRS" + + tags { + environment = "staging" + } +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_virtual_machine_scale_set" "test" { + name = "acctvmss-%d" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + upgrade_policy_mode = "Manual" + + sku { + name = "Standard_A0" + tier = "Standard" + capacity = 2 + } + + virtual_machine_os_profile { + computer_name_prefix = "vm-%s" + admin_username = "myadmin" + admin_password = "Passwword1234" + } + + virtual_machine_network_profile { + name = "TestNetworkProfile-%d" + primary = true + ip_configuration { + name = "TestIPConfiguration" + subnet_id = "${azurerm_subnet.test.id}" + } + } + + virtual_machine_storage_profile_os_disk { + name = "osDiskProfile" + caching = "ReadWrite" + create_option = "FromImage" + vhd_containers = ["${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}"] + } + + virtual_machine_storage_profile_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2012-R2-Datacenter" + version = "4.0.20160518" + } +} +` diff --git a/website/source/docs/providers/azurerm/r/virtual_machine_scale_sets.html.markdown b/website/source/docs/providers/azurerm/r/virtual_machine_scale_sets.html.markdown new file mode 100644 index 000000000000..ea266ef1e3f0 --- /dev/null +++ b/website/source/docs/providers/azurerm/r/virtual_machine_scale_sets.html.markdown @@ -0,0 +1,221 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_virtual_machine_scale_sets" +sidebar_current: "docs-azurerm-resource-virtualmachine-scale-sets" +description: |- + Create a Virtual Machine scale set. +--- + +# azurerm\_virtual\_machine\_scale\_sets + +Create a virtual machine scale set. + +## Example Usage + +``` +resource "azurerm_resource_group" "test" { + name = "acctestrg" + location = "West US" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn" + address_space = ["10.0.0.0/16"] + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "acctni" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_storage_account" "test" { + name = "accsa" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "westus" + account_type = "Standard_LRS" + + tags { + environment = "staging" + } +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_virtual_machine_scale_set" "test" { + name = "mytestscaleset-1" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + upgrade_policy_mode = "Manual" + + sku { + name = "Standard_A0" + tier = "Standard" + capacity = 2 + } + + virtual_machine_os_profile { + computer_name_prefix = "testvm" + admin_username = "myadmin" + admin_password = "Passwword1234" + } + + virtual_machine_os_profile_linux_config { + disable_password_authentication = true + ssh_keys { + path = "/home/myadmin/.ssh/authorized_keys" + key_data = "${file("~/.ssh/demo_key.pub")}" + } + } + + virtual_machine_network_profile { + name = "TestNetworkProfile" + primary = true + ip_configuration { + name = "TestIPConfiguration" + subnet_id = "${azurerm_subnet.test.id}" + } + } + + virtual_machine_storage_profile_os_disk { + name = "osDiskProfile" + caching = "ReadWrite" + create_option = "FromImage" + vhd_containers = ["${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}"] + } + + virtual_machine_storage_profile_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "14.04.2-LTS" + version = "latest" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the virtual machine scale set resource. Changing this forces a + new resource to be created. +* `resource_group_name` - (Required) The name of the resource group in which to + create the virtual machine scale set. +* `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. +* `sku` - (Required) A sku block as documented below. +* `upgrade_policy_mode` - (Required) Specifies the mode of an upgrade to virtual machines in the scale set. Possible values, `Manual` or `Automatic`. +* `virtual_machine_os_profile` - (Required) A Virtual Machine OS Profile block as documented below. +* `virtual_machine_os_profile_secrets` - (Optional) A collection of Secret blocks as documented below. +* `virtual_machine_os_profile_windows_config` - (Required, when a windows machine) A Windows config block as documented below. +* `virtual_machine_os_profile_linux_config` - (Required, when a linux machine) A Linux config block as documented below. +* `virtual_machine_network_profile` - (Required) A collection of network profile block as documented below. +* `virtual_machine_storage_profile_os_disk` - (Required) A storage profile os disk block as documented below +* `virtual_machine_storage_profile_image_reference` - (Optional) A storage profile image reference block as documented below. +* `tags` - (Optional) A mapping of tags to assign to the resource. + + +`sku` supports the following: + +* `name` - (Required) Specifies the size of virtual machines in a scale set. +* `tier` - (Optional) Specifies the tier of virtual machines in a scale set. Possible values, `standard` or `basic`. +* `capacity` - (Required) Specifies the number of virtual machines in the scale set. + +`virtual_machine_os_profile` supports the following: + +* `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 a base-64 encoded string of custom data. The base-64 encoded string is decoded to a binary array that is saved as a file on all the Virtual Machines in the scale set. The maximum length of the binary array is 65535 bytes. + +`virtual_machine_os_profile_secrets` supports the following: + +* `source_vault_id` - (Required) Specifies the key vault to use. +* `vault_certificates` - (Required, on windows machines) A collection of Vault Certificates as documented below + +`vault_certificates` support the following: + +* `certificate_url` - (Required) It is the Base64 encoding of a JSON Object that which is encoded in UTF-8 of which the contents need to be `data`, `dataType` and `password`. +* `certificate_store` - (Required, on windows machines) Specifies the certificate store on the Virtual Machine where the certificate should be added to. + + +`virtual_machine_os_profile_windows_config` supports the following: + +* `provision_vm_agent` - (Optional) Indicates whether virtual machine agent should be provisioned on the virtual machines in the scale set. +* `enable_automatic_upgrades` - (Optional) Indicates whether virtual machines in the scale set are enabled for automatic updates. +* `winrm` - (Optional) A collection of WinRM configuration blocks as documented below. +* `additional_unattend_config` - (Optional) An Additional Unattended Config block as documented below. + +`winrm` supports the following: + +* `protocol` - (Required) Specifies the protocol of listener +* `certificate_url` - (Optional) Specifies URL of the certificate with which new Virtual Machines is provisioned. + +`additional_unattend_config` supports the following: + +* `pass` - (Required) Specifies the name of the pass that the content applies to. The only allowable value is `oobeSystem`. +* `component` - (Required) Specifies the name of the component to configure with the added content. The only allowable value is `Microsoft-Windows-Shell-Setup`. +* `setting_name` - (Required) Specifies the name of the setting to which the content applies. Possible values are: `FirstLogonCommands` and `AutoLogon`. +* `content` - (Optional) Specifies the base-64 encoded XML formatted content that is added to the unattend.xml file for the specified path and component. + +`virtual_machine_os_profile_linux_config` supports the following: + +* `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//.ssh/authorized_keys` due to a limitation of Azure_ + + +`virtual_machine_network_profile` supports the following: + +* `name` - (Required) Specifies the name of the network interface configuration. +* `primary` - (Required) Indicates whether network interfaces created from the network interface configuration will be the primary NIC of the VM. +* `ip_configuration` - (Required) An ip_configuration block as documented below + +`ip_configuration` supports the following: + +* `name` - (Required) Specifies name of the IP configuration. +* `subnet_id` - (Required) Specifies the identifier of the subnet. +* `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. + +`virtual_machine_storage_profile_os_disk` supports the following: + +* `name` - (Required) Specifies the disk name. +* `vhd_containers` - (Required) Specifies the vhd uri. +* `create_option` - (Required) Specifies how the virtual machine should be created. The only possible option is `FromImage`. +* `caching` - (Required) Specifies the caching requirements. +* `image` - (Optional) Specifies the blob uri for user image. A virtual machine scale set creates an os disk in the same container as the user image. + Updating the osDisk image causes the existing disk to be deleted and a new one created with the new image. If the VM scale set is in Manual upgrade mode then the virtual machines are not updated until they have manualUpgrade applied to them. +* `os_type` - (Optional) Specifies the operating system Type, valid values are windows, linux. + +`virtual_machine_storage_profile_image_reference` supports the following: + +* `publisher` - (Required) Specifies the publisher of the image used to create the virtual machines +* `offer` - (Required) Specifies the offer of the image used to create the virtual machines. +* `sku` - (Required) Specifies the SKU of the image used to create the virtual machines. +* `version` - (Optional) Specifies the version of the image used to create the virtual machines. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The virtual machine scale set ID. diff --git a/website/source/layouts/azurerm.erb b/website/source/layouts/azurerm.erb index b364e5aaa078..bb0d6137bc51 100644 --- a/website/source/layouts/azurerm.erb +++ b/website/source/layouts/azurerm.erb @@ -191,6 +191,10 @@ azurerm_virtual_machine + > + azurerm_virtual_machine_scale_set + +