Skip to content

Commit

Permalink
New resource: azurerm_orchestrated_virtual_machine_scale_set (#6626)
Browse files Browse the repository at this point in the history
This PR implements the new orchestration mode VM of VMSS by introducing the new resource azurerm_orchestrated_virtual_machine_scale_set.

For more information about the orchestration mode of VMSS, please refer this doc

Fixes #6085
  • Loading branch information
ArcturusZhang committed May 21, 2020
1 parent 57c513c commit 9982911
Show file tree
Hide file tree
Showing 13 changed files with 1,099 additions and 35 deletions.
Expand Up @@ -114,7 +114,7 @@ func resourceLinuxVirtualMachine() *schema.Resource {
// TODO: raise a GH issue for the broken API
// availability_set_id: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/acctestRG-200122113424880096/providers/Microsoft.Compute/availabilitySets/ACCTESTAVSET-200122113424880096" => "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/acctestRG-200122113424880096/providers/Microsoft.Compute/availabilitySets/acctestavset-200122113424880096" (forces new resource)
ConflictsWith: []string{
// TODO: "virtual_machine_scale_set_id"
"virtual_machine_scale_set_id",
"zone",
},
},
Expand Down Expand Up @@ -216,15 +216,27 @@ func resourceLinuxVirtualMachine() *schema.Resource {

"source_image_reference": sourceImageReferenceSchema(true),

"virtual_machine_scale_set_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{
"availability_set_id",
},
ValidateFunc: computeValidate.VirtualMachineScaleSetID,
},

"tags": tags.Schema(),

"zone": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
// this has to be computed because when you are trying to assign this VM to a VMSS in VMO mode,
// the VMO mode VMSS will assign a zone for each of its instance
Computed: true,
ConflictsWith: []string{
"availability_set_id",
// TODO: "virtual_machine_scale_set_id"
},
},

Expand Down Expand Up @@ -371,11 +383,6 @@ func resourceLinuxVirtualMachineCreate(d *schema.ResourceData, meta interface{})
// Optional
AdditionalCapabilities: additionalCapabilities,
DiagnosticsProfile: bootDiagnostics,

// @tombuildsstuff: passing in a VMSS ID returns:
// > Code="InvalidParameter" Message="The value of parameter virtualMachineScaleSet is invalid." Target="virtualMachineScaleSet"
// presuming this isn't finished yet; note: this'll conflict with availability set id
VirtualMachineScaleSet: nil,
},
Tags: tags.Expand(t),
}
Expand Down Expand Up @@ -426,6 +433,16 @@ func resourceLinuxVirtualMachineCreate(d *schema.ResourceData, meta interface{})
}
}

if v, ok := d.GetOk("virtual_machine_scale_set_id"); ok {
// you must also specify a zone in order to assign this vm to a orchestrated vmss
if _, ok := d.GetOk("zone"); !ok {
return fmt.Errorf("`zone` must be specified when `virtual_machine_scale_set_id` is set")
}
params.VirtualMachineScaleSet = &compute.SubResource{
ID: utils.String(v.(string)),
}
}

if v, ok := d.GetOk("zone"); ok {
params.Zones = &[]string{
v.(string),
Expand Down Expand Up @@ -547,6 +564,12 @@ func resourceLinuxVirtualMachineRead(d *schema.ResourceData, meta interface{}) e
}
d.Set("dedicated_host_id", dedicatedHostId)

virtualMachineScaleSetId := ""
if props.VirtualMachineScaleSet != nil && props.VirtualMachineScaleSet.ID != nil {
virtualMachineScaleSetId = *props.VirtualMachineScaleSet.ID
}
d.Set("virtual_machine_scale_set_id", virtualMachineScaleSetId)

if profile := props.OsProfile; profile != nil {
d.Set("admin_username", profile.AdminUsername)
d.Set("allow_extension_operations", profile.AllowExtensionOperations)
Expand Down
@@ -0,0 +1,191 @@
package compute

import (
"fmt"
"log"
"time"

"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/location"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute/parse"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags"
azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmOrchestratedVirtualMachineScaleSet() *schema.Resource {
return &schema.Resource{
Create: resourceArmOrchestratedVirtualMachineScaleSetCreateUpdate,
Read: resourceArmOrchestratedVirtualMachineScaleSetRead,
Update: resourceArmOrchestratedVirtualMachineScaleSetCreateUpdate,
Delete: resourceArmOrchestratedVirtualMachineScaleSetDelete,

Importer: azSchema.ValidateResourceIDPriorToImportThen(func(id string) error {
_, err := parse.VirtualMachineScaleSetID(id)
return err
}, importOrchestratedVirtualMachineScaleSet),

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(30 * time.Minute),
Read: schema.DefaultTimeout(5 * time.Minute),
Update: schema.DefaultTimeout(30 * time.Minute),
Delete: schema.DefaultTimeout(30 * time.Minute),
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: ValidateOrchestratedVMSSName,
},

"resource_group_name": azure.SchemaResourceGroupName(),

"location": azure.SchemaLocation(),

"platform_fault_domain_count": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
// The range of this value varies in different locations
ValidateFunc: validation.IntBetween(0, 5),
},

"single_placement_group": {
Type: schema.TypeBool,
Required: true,
ForceNew: true,
},

// the VMO mode can only be deployed into one zone for now, and its zone will also be assigned to all its VM instances
"zones": azure.SchemaSingleZone(),

"unique_id": {
Type: schema.TypeString,
Computed: true,
},

"tags": tags.Schema(),
},
}
}

func resourceArmOrchestratedVirtualMachineScaleSetCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Compute.VMScaleSetClient
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()

resourceGroup := d.Get("resource_group_name").(string)
name := d.Get("name").(string)

if d.IsNewResource() {
existing, err := client.Get(ctx, resourceGroup, name)
if err != nil {
if !utils.ResponseWasNotFound(existing.Response) {
return fmt.Errorf("checking for existing Orchestrated Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err)
}
}

if existing.ID != nil && *existing.ID != "" {
return tf.ImportAsExistsError("azurerm_orchestrated_virtual_machine_scale_set", *existing.ID)
}
}

props := compute.VirtualMachineScaleSet{
Location: utils.String(location.Normalize(d.Get("location").(string))),
Tags: tags.Expand(d.Get("tags").(map[string]interface{})),
VirtualMachineScaleSetProperties: &compute.VirtualMachineScaleSetProperties{
PlatformFaultDomainCount: utils.Int32(int32(d.Get("platform_fault_domain_count").(int))),
SinglePlacementGroup: utils.Bool(d.Get("single_placement_group").(bool)),
},
Zones: azure.ExpandZones(d.Get("zones").([]interface{})),
}

future, err := client.CreateOrUpdate(ctx, resourceGroup, name, props)
if err != nil {
return fmt.Errorf("creating Orchestrated Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err)
}

if err := future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("waiting for creation of Orchestrated Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err)
}

resp, err := client.Get(ctx, resourceGroup, name)
if err != nil {
return fmt.Errorf("retrieving Orchestrated Virtual Machine Scale Set %q (Resource Group %q): %+v", name, resourceGroup, err)
}

if resp.ID == nil || *resp.ID == "" {
return fmt.Errorf("retrieving Orchestrated Virtual Machine Scale Set %q (Resource Group %q): ID was empty", name, resourceGroup)
}
d.SetId(*resp.ID)

return resourceArmOrchestratedVirtualMachineScaleSetRead(d, meta)
}

func resourceArmOrchestratedVirtualMachineScaleSetRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Compute.VMScaleSetClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.VirtualMachineScaleSetID(d.Id())
if err != nil {
return err
}

resp, err := client.Get(ctx, id.ResourceGroup, id.Name)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[DEBUG] Orchestrated Virtual Machine Scale Set %q was not found in Resource Group %q - removing from state!", id.Name, id.ResourceGroup)
d.SetId("")
return nil
}

return fmt.Errorf("retrieving Orchestrated Virtual Machine Scale Set %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err)
}

d.Set("name", id.Name)
d.Set("resource_group_name", id.ResourceGroup)
d.Set("location", location.NormalizeNilable(resp.Location))

if props := resp.VirtualMachineScaleSetProperties; props != nil {
d.Set("platform_fault_domain_count", props.PlatformFaultDomainCount)
d.Set("single_placement_group", props.SinglePlacementGroup)
d.Set("unique_id", props.UniqueID)
}

if err := d.Set("zones", resp.Zones); err != nil {
return fmt.Errorf("setting `zones`: %+v", err)
}

return tags.FlattenAndSet(d, resp.Tags)
}

func resourceArmOrchestratedVirtualMachineScaleSetDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Compute.VMScaleSetClient
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.VirtualMachineScaleSetID(d.Id())
if err != nil {
return err
}

future, err := client.Delete(ctx, id.ResourceGroup, id.Name)
if err != nil {
return fmt.Errorf("deleting Orchestrated Virtual Machine Scale Set %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err)
}

if err := future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("waiting for deletion of Orchestrated Virtual Machine Scale Set %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err)
}

return nil
}
43 changes: 22 additions & 21 deletions azurerm/internal/services/compute/registration.go
Expand Up @@ -41,27 +41,28 @@ func (r Registration) SupportedDataSources() map[string]*schema.Resource {
// SupportedResources returns the supported Resources supported by this Service
func (r Registration) SupportedResources() map[string]*schema.Resource {
resources := map[string]*schema.Resource{
"azurerm_availability_set": resourceArmAvailabilitySet(),
"azurerm_dedicated_host": resourceArmDedicatedHost(),
"azurerm_dedicated_host_group": resourceArmDedicatedHostGroup(),
"azurerm_disk_encryption_set": resourceArmDiskEncryptionSet(),
"azurerm_image": resourceArmImage(),
"azurerm_managed_disk": resourceArmManagedDisk(),
"azurerm_marketplace_agreement": resourceArmMarketplaceAgreement(),
"azurerm_proximity_placement_group": resourceArmProximityPlacementGroup(),
"azurerm_shared_image_gallery": resourceArmSharedImageGallery(),
"azurerm_shared_image_version": resourceArmSharedImageVersion(),
"azurerm_shared_image": resourceArmSharedImage(),
"azurerm_snapshot": resourceArmSnapshot(),
"azurerm_virtual_machine_data_disk_attachment": resourceArmVirtualMachineDataDiskAttachment(),
"azurerm_virtual_machine_extension": resourceArmVirtualMachineExtension(),
"azurerm_virtual_machine_scale_set": resourceArmVirtualMachineScaleSet(),
"azurerm_virtual_machine": resourceArmVirtualMachine(),
"azurerm_linux_virtual_machine": resourceLinuxVirtualMachine(),
"azurerm_linux_virtual_machine_scale_set": resourceArmLinuxVirtualMachineScaleSet(),
"azurerm_virtual_machine_scale_set_extension": resourceArmVirtualMachineScaleSetExtension(),
"azurerm_windows_virtual_machine": resourceWindowsVirtualMachine(),
"azurerm_windows_virtual_machine_scale_set": resourceArmWindowsVirtualMachineScaleSet(),
"azurerm_availability_set": resourceArmAvailabilitySet(),
"azurerm_dedicated_host": resourceArmDedicatedHost(),
"azurerm_dedicated_host_group": resourceArmDedicatedHostGroup(),
"azurerm_disk_encryption_set": resourceArmDiskEncryptionSet(),
"azurerm_image": resourceArmImage(),
"azurerm_managed_disk": resourceArmManagedDisk(),
"azurerm_marketplace_agreement": resourceArmMarketplaceAgreement(),
"azurerm_proximity_placement_group": resourceArmProximityPlacementGroup(),
"azurerm_shared_image_gallery": resourceArmSharedImageGallery(),
"azurerm_shared_image_version": resourceArmSharedImageVersion(),
"azurerm_shared_image": resourceArmSharedImage(),
"azurerm_snapshot": resourceArmSnapshot(),
"azurerm_virtual_machine_data_disk_attachment": resourceArmVirtualMachineDataDiskAttachment(),
"azurerm_virtual_machine_extension": resourceArmVirtualMachineExtension(),
"azurerm_virtual_machine_scale_set": resourceArmVirtualMachineScaleSet(),
"azurerm_orchestrated_virtual_machine_scale_set": resourceArmOrchestratedVirtualMachineScaleSet(),
"azurerm_virtual_machine": resourceArmVirtualMachine(),
"azurerm_linux_virtual_machine": resourceLinuxVirtualMachine(),
"azurerm_linux_virtual_machine_scale_set": resourceArmLinuxVirtualMachineScaleSet(),
"azurerm_virtual_machine_scale_set_extension": resourceArmVirtualMachineScaleSetExtension(),
"azurerm_windows_virtual_machine": resourceWindowsVirtualMachine(),
"azurerm_windows_virtual_machine_scale_set": resourceArmWindowsVirtualMachineScaleSet(),
}

return resources
Expand Down

0 comments on commit 9982911

Please sign in to comment.