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 authored 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.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit 9982911

Please sign in to comment.