diff --git a/azurerm/internal/services/compute/linux_virtual_machine_resource.go b/azurerm/internal/services/compute/linux_virtual_machine_resource.go index deef305808a4..473d7a7fc96d 100644 --- a/azurerm/internal/services/compute/linux_virtual_machine_resource.go +++ b/azurerm/internal/services/compute/linux_virtual_machine_resource.go @@ -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", }, }, @@ -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" }, }, @@ -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), } @@ -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), @@ -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) diff --git a/azurerm/internal/services/compute/orchestrated_virtual_machine_scale_set_resource.go b/azurerm/internal/services/compute/orchestrated_virtual_machine_scale_set_resource.go new file mode 100644 index 000000000000..ff220dfba9e3 --- /dev/null +++ b/azurerm/internal/services/compute/orchestrated_virtual_machine_scale_set_resource.go @@ -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 +} diff --git a/azurerm/internal/services/compute/registration.go b/azurerm/internal/services/compute/registration.go index 74cf68caa570..b29001431de4 100644 --- a/azurerm/internal/services/compute/registration.go +++ b/azurerm/internal/services/compute/registration.go @@ -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 diff --git a/azurerm/internal/services/compute/tests/linux_virtual_machine_resource_orchestrated_test.go b/azurerm/internal/services/compute/tests/linux_virtual_machine_resource_orchestrated_test.go new file mode 100644 index 000000000000..01933332cbe1 --- /dev/null +++ b/azurerm/internal/services/compute/tests/linux_virtual_machine_resource_orchestrated_test.go @@ -0,0 +1,239 @@ +package tests + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" +) + +func TestAccAzureRMLinuxVirtualMachine_orchestrated(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: checkLinuxVirtualMachineIsDestroyed, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLinuxVirtualMachine_orchestrated(data), + Check: resource.ComposeTestCheckFunc( + checkLinuxVirtualMachineExists(data.ResourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMLinuxVirtualMachine_orchestratedMultiple(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: checkLinuxVirtualMachineIsDestroyed, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLinuxVirtualMachine_orchestratedMultiple(data), + Check: resource.ComposeTestCheckFunc( + checkLinuxVirtualMachineExists(data.ResourceName), + ), + }, + }, + }) +} + +func testAccAzureRMLinuxVirtualMachine_orchestrated(data acceptance.TestData) string { + template := testLinuxVirtualMachine_templateBaseForOchestratedVMSS(data) + return fmt.Sprintf(` +%s + +resource "azurerm_network_interface" "test" { + name = "acctestnic-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.test.id + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_orchestrated_virtual_machine_scale_set" "test" { + name = "acctestVMO-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + platform_fault_domain_count = 5 + single_placement_group = true + + zones = ["1"] + + tags = { + ENV = "Test" + } +} + +resource "azurerm_linux_virtual_machine" "test" { + name = "acctestVM-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@ssw0rd1234!" + disable_password_authentication = false + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + virtual_machine_scale_set_id = azurerm_orchestrated_virtual_machine_scale_set.test.id + zone = azurerm_orchestrated_virtual_machine_scale_set.test.zones.0 +} +`, template, data.RandomInteger, data.RandomInteger, data.RandomInteger) +} + +func testAccAzureRMLinuxVirtualMachine_orchestratedMultiple(data acceptance.TestData) string { + template := testLinuxVirtualMachine_templateBaseForOchestratedVMSS(data) + return fmt.Sprintf(` +%s + +resource "azurerm_orchestrated_virtual_machine_scale_set" "test" { + name = "acctestVMO-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + platform_fault_domain_count = 5 + single_placement_group = true + + zones = ["1"] + + tags = { + ENV = "Test" + } +} + +resource "azurerm_network_interface" "first" { + name = "acctestnic1-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.test.id + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_linux_virtual_machine" "test" { + name = "acctestVM1-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@ssw0rd1234!" + disable_password_authentication = false + network_interface_ids = [ + azurerm_network_interface.first.id, + ] + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + virtual_machine_scale_set_id = azurerm_orchestrated_virtual_machine_scale_set.test.id + zone = azurerm_orchestrated_virtual_machine_scale_set.test.zones.0 +} + +resource "azurerm_network_interface" "second" { + name = "acctestnic2-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.test.id + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_linux_virtual_machine" "another" { + name = "acctestVM2-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@ssw0rd1234!" + disable_password_authentication = false + network_interface_ids = [ + azurerm_network_interface.second.id, + ] + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "18.04-LTS" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + virtual_machine_scale_set_id = azurerm_orchestrated_virtual_machine_scale_set.test.id + zone = azurerm_orchestrated_virtual_machine_scale_set.test.zones.0 +} +`, template, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) +} + +func testLinuxVirtualMachine_templateBaseForOchestratedVMSS(data acceptance.TestData) string { + // in VMSS VMO mode, the `platform_fault_domain_count` has different acceptable values for different locations, + // therefore this location is fixed to EastUS2 to make sure the acceptance test has no issues about this value + location := "EastUS2" + return fmt.Sprintf(` +locals { + vm_name = "acctestvm%s" +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestnw-%d" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_subnet" "test" { + name = "internal" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefix = "10.0.2.0/24" +} +`, data.RandomString, data.RandomInteger, location, data.RandomInteger) +} diff --git a/azurerm/internal/services/compute/tests/orchestrated_virtual_machine_scale_set_resource_test.go b/azurerm/internal/services/compute/tests/orchestrated_virtual_machine_scale_set_resource_test.go new file mode 100644 index 000000000000..d22cf1df0da0 --- /dev/null +++ b/azurerm/internal/services/compute/tests/orchestrated_virtual_machine_scale_set_resource_test.go @@ -0,0 +1,217 @@ +package tests + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMOrchestratedVirtualMachineScaleSet_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_orchestrated_virtual_machine_scale_set", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMOrchestratedVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMOrchestratedVirtualMachineScaleSet_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMOrchestratedVirtualMachineScaleSetExists(data.ResourceName), + ), + }, + data.ImportStep(), + }, + }) +} + +func TestAccAzureRMOrchestratedVirtualMachineScaleSet_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_orchestrated_virtual_machine_scale_set", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMOrchestratedVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMOrchestratedVirtualMachineScaleSet_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMOrchestratedVirtualMachineScaleSetExists(data.ResourceName), + ), + }, + data.RequiresImportErrorStep(testAccAzureRMOrchestratedVirtualMachineScaleSet_requiresImport), + }, + }) +} + +func TestAccAzureRMOrchestratedVirtualMachineScaleSet_basicLinuxUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_orchestrated_virtual_machine_scale_set", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMOrchestratedVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMOrchestratedVirtualMachineScaleSet_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMOrchestratedVirtualMachineScaleSetExists(data.ResourceName), + ), + }, + data.ImportStep(), + { + Config: testAccAzureRMOrchestratedVirtualMachineScaleSet_update(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMOrchestratedVirtualMachineScaleSetExists(data.ResourceName), + ), + }, + data.ImportStep(), + { + Config: testAccAzureRMOrchestratedVirtualMachineScaleSet_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMOrchestratedVirtualMachineScaleSetExists(data.ResourceName), + ), + }, + data.ImportStep(), + }, + }) +} + +func testCheckAzureRMOrchestratedVirtualMachineScaleSetDestroy(s *terraform.State) error { + client := acceptance.AzureProvider.Meta().(*clients.Client).Compute.VMScaleSetClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_orchestrated_virtual_machine_scale_set" { + continue + } + + id, err := parse.VirtualMachineScaleSetID(rs.Primary.ID) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("bad: Get on Compute.VMScaleSetClient: %+v", err) + } + } + + return nil + } + + return nil +} + +func testCheckAzureRMOrchestratedVirtualMachineScaleSetExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := acceptance.AzureProvider.Meta().(*clients.Client).Compute.VMScaleSetClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("not found: %s", resourceName) + } + + id, err := parse.VirtualMachineScaleSetID(rs.Primary.ID) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("bad: Virtual Machine Scale Set VM Mode %q (Resource Group: %q) does not exist", id.Name, id.ResourceGroup) + } + + return fmt.Errorf("bad: Get on Compute.VMScaleSetClient: %+v", err) + } + + return nil + } +} + +func testAccAzureRMOrchestratedVirtualMachineScaleSet_basic(data acceptance.TestData) string { + template := testAccAzureRMOrchestratedVirtualMachineScaleSet_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_orchestrated_virtual_machine_scale_set" "test" { + name = "acctestVMO-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + platform_fault_domain_count = 5 + single_placement_group = true + + zones = ["1"] + + tags = { + ENV = "Test" + } +} +`, template, data.RandomInteger) +} + +func testAccAzureRMOrchestratedVirtualMachineScaleSet_update(data acceptance.TestData) string { + template := testAccAzureRMOrchestratedVirtualMachineScaleSet_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_orchestrated_virtual_machine_scale_set" "test" { + name = "acctestVMO-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + platform_fault_domain_count = 5 + single_placement_group = true + + zones = ["1"] + + tags = { + ENV = "Test", + FOO = "Bar" + } +} +`, template, data.RandomInteger) +} + +func testAccAzureRMOrchestratedVirtualMachineScaleSet_requiresImport(data acceptance.TestData) string { + template := testAccAzureRMOrchestratedVirtualMachineScaleSet_basic(data) + return fmt.Sprintf(` +%s + +resource "azurerm_orchestrated_virtual_machine_scale_set" "import" { + name = azurerm_orchestrated_virtual_machine_scale_set.test.name + location = azurerm_orchestrated_virtual_machine_scale_set.test.location + resource_group_name = azurerm_orchestrated_virtual_machine_scale_set.test.resource_group_name + + platform_fault_domain_count = azurerm_orchestrated_virtual_machine_scale_set.test.platform_fault_domain_count + single_placement_group = azurerm_orchestrated_virtual_machine_scale_set.test.single_placement_group +} +`, template) +} + +func testAccAzureRMOrchestratedVirtualMachineScaleSet_template(data acceptance.TestData) string { + // in VMSS VMO mode, the `platform_fault_domain_count` has different acceptable values for different locations, + // therefore this location is fixed to EastUS2 to make sure the acceptance test has no issues about this value + location := "EastUS2" + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-VMSS-%d" + location = "%s" +} +`, data.RandomInteger, location) +} diff --git a/azurerm/internal/services/compute/tests/windows_virtual_machine_resource_orchestrated_test.go b/azurerm/internal/services/compute/tests/windows_virtual_machine_resource_orchestrated_test.go new file mode 100644 index 000000000000..9d76a48fa68c --- /dev/null +++ b/azurerm/internal/services/compute/tests/windows_virtual_machine_resource_orchestrated_test.go @@ -0,0 +1,238 @@ +package tests + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" +) + +func TestAccAzureRMWindowsVirtualMachine_orchestrated(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_virtual_machine", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: checkWindowsVirtualMachineIsDestroyed, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachine_orchestrated(data), + Check: resource.ComposeTestCheckFunc( + checkWindowsVirtualMachineExists(data.ResourceName), + ), + }, + data.ImportStep("admin_password"), + }, + }) +} + +func TestAccAzureRMWindowsVirtualMachine_orchestratedMultiple(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_virtual_machine", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: checkWindowsVirtualMachineIsDestroyed, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMWindowsVirtualMachine_orchestratedMultiple(data), + Check: resource.ComposeTestCheckFunc( + checkWindowsVirtualMachineExists(data.ResourceName), + ), + }, + data.ImportStep("admin_password"), + }, + }) +} + +func testAccAzureRMWindowsVirtualMachine_orchestrated(data acceptance.TestData) string { + template := testWindowsVirtualMachine_templateBaseForOchestratedVMSS(data) + return fmt.Sprintf(` +%s + +resource "azurerm_network_interface" "test" { + name = "acctestnic-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.test.id + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_orchestrated_virtual_machine_scale_set" "test" { + name = "acctestVMO-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + platform_fault_domain_count = 5 + single_placement_group = true + + zones = ["1"] + + tags = { + ENV = "Test" + } +} + +resource "azurerm_windows_virtual_machine" "test" { + name = local.vm_name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@ssw0rd1234!" + network_interface_ids = [ + azurerm_network_interface.test.id, + ] + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + virtual_machine_scale_set_id = azurerm_orchestrated_virtual_machine_scale_set.test.id + zone = azurerm_orchestrated_virtual_machine_scale_set.test.zones.0 +} +`, template, data.RandomInteger, data.RandomInteger) +} + +func testAccAzureRMWindowsVirtualMachine_orchestratedMultiple(data acceptance.TestData) string { + template := testWindowsVirtualMachine_templateBaseForOchestratedVMSS(data) + return fmt.Sprintf(` +%s + +resource "azurerm_orchestrated_virtual_machine_scale_set" "test" { + name = "acctestVMO-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + platform_fault_domain_count = 5 + single_placement_group = true + + zones = ["1"] + + tags = { + ENV = "Test" + } +} + +resource "azurerm_network_interface" "first" { + name = "acctestnic1-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.test.id + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_network_interface" "second" { + name = "acctestnic2-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.test.id + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_windows_virtual_machine" "test" { + name = "accVM1%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@ssw0rd1234!" + network_interface_ids = [ + azurerm_network_interface.first.id, + ] + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2016-Datacenter" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + virtual_machine_scale_set_id = azurerm_orchestrated_virtual_machine_scale_set.test.id + zone = azurerm_orchestrated_virtual_machine_scale_set.test.zones.0 +} + +resource "azurerm_windows_virtual_machine" "another" { + name = "accVM2%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@ssw0rd1234!" + network_interface_ids = [ + azurerm_network_interface.second.id, + ] + + source_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2019-Datacenter" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + virtual_machine_scale_set_id = azurerm_orchestrated_virtual_machine_scale_set.test.id + zone = azurerm_orchestrated_virtual_machine_scale_set.test.zones.0 +} +`, template, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomIntOfLength(9), data.RandomIntOfLength(9)) +} + +func testWindowsVirtualMachine_templateBaseForOchestratedVMSS(data acceptance.TestData) string { + // in VMSS VMO mode, the `platform_fault_domain_count` has different acceptable values for different locations, + // therefore this location is fixed to EastUS2 to make sure the acceptance test has no issues about this value + location := "EastUS2" + return fmt.Sprintf(` +locals { + vm_name = "acctestvm%s" +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestnw-%d" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_subnet" "test" { + name = "internal" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefix = "10.0.2.0/24" +} +`, data.RandomString, data.RandomInteger, location, data.RandomInteger) +} diff --git a/azurerm/internal/services/compute/validation.go b/azurerm/internal/services/compute/validation.go index b10ac6771fb7..8215f102d881 100644 --- a/azurerm/internal/services/compute/validation.go +++ b/azurerm/internal/services/compute/validation.go @@ -57,6 +57,10 @@ func ValidateLinuxComputerNamePrefix(i interface{}, k string) (warnings []string return ValidateLinuxComputerName(i, k, 58) } +func ValidateOrchestratedVMSSName(i interface{}, k string) (warnings []string, errors []error) { + return ValidateVmName(i, k) +} + func ValidateLinuxComputerName(i interface{}, k string, maxLength int) (warnings []string, errors []error) { v, ok := i.(string) if !ok { diff --git a/azurerm/internal/services/compute/virtual_machine_scale_set_import.go b/azurerm/internal/services/compute/virtual_machine_scale_set_import.go index 277ca8f2fb12..3ebea2ed4cab 100644 --- a/azurerm/internal/services/compute/virtual_machine_scale_set_import.go +++ b/azurerm/internal/services/compute/virtual_machine_scale_set_import.go @@ -10,6 +10,40 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" ) +func importOrchestratedVirtualMachineScaleSet(d *schema.ResourceData, meta interface{}) (data []*schema.ResourceData, err error) { + id, err := parse.VirtualMachineScaleSetID(d.Id()) + if err != nil { + return []*schema.ResourceData{}, err + } + + client := meta.(*clients.Client).Compute.VMScaleSetClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + vm, err := client.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + return []*schema.ResourceData{}, fmt.Errorf("retrieving Virtual Machine Scale Set %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + } + + if err := assertOrchestratedVirtualMachineScaleSet(vm); err != nil { + return []*schema.ResourceData{}, fmt.Errorf("importing Virtual Machine Scale Set Orchestrator VM %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + } + + return []*schema.ResourceData{d}, nil +} + +func assertOrchestratedVirtualMachineScaleSet(resp compute.VirtualMachineScaleSet) error { + if resp.VirtualMachineScaleSetProperties == nil { + return fmt.Errorf("`properties` is nil") + } + + if resp.VirtualMachineScaleSetProperties.VirtualMachineProfile != nil { + return fmt.Errorf("the virtual machine scale set is an orchestration virtual machine scale set") + } + + return nil +} + func importVirtualMachineScaleSet(osType compute.OperatingSystemTypes, resourceType string) func(d *schema.ResourceData, meta interface{}) (data []*schema.ResourceData, err error) { return func(d *schema.ResourceData, meta interface{}) (data []*schema.ResourceData, err error) { id, err := parse.VirtualMachineScaleSetID(d.Id()) diff --git a/azurerm/internal/services/compute/windows_virtual_machine_resource.go b/azurerm/internal/services/compute/windows_virtual_machine_resource.go index 53b1348144f9..3ebc57e3246b 100644 --- a/azurerm/internal/services/compute/windows_virtual_machine_resource.go +++ b/azurerm/internal/services/compute/windows_virtual_machine_resource.go @@ -35,6 +35,7 @@ func resourceWindowsVirtualMachine() *schema.Resource { Read: resourceWindowsVirtualMachineRead, Update: resourceWindowsVirtualMachineUpdate, Delete: resourceWindowsVirtualMachineDelete, + Importer: azSchema.ValidateResourceIDPriorToImportThen(func(id string) error { _, err := parse.VirtualMachineID(id) return err @@ -115,7 +116,7 @@ func resourceWindowsVirtualMachine() *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", }, }, @@ -236,6 +237,16 @@ func resourceWindowsVirtualMachine() *schema.Resource { ValidateFunc: validate.VirtualMachineTimeZone(), }, + "virtual_machine_scale_set_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{ + "availability_set_id", + }, + ValidateFunc: computeValidate.VirtualMachineScaleSetID, + }, + "winrm_listener": winRmListenerSchema(), "zone": { @@ -244,7 +255,6 @@ func resourceWindowsVirtualMachine() *schema.Resource { ForceNew: true, ConflictsWith: []string{ "availability_set_id", - // TODO: "virtual_machine_scale_set_id" }, }, @@ -394,11 +404,6 @@ func resourceWindowsVirtualMachineCreate(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), } @@ -457,6 +462,16 @@ func resourceWindowsVirtualMachineCreate(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("timezone"); ok { params.VirtualMachineProperties.OsProfile.WindowsConfiguration.TimeZone = utils.String(v.(string)) } @@ -571,6 +586,12 @@ func resourceWindowsVirtualMachineRead(d *schema.ResourceData, meta interface{}) } 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) diff --git a/website/azurerm.erb b/website/azurerm.erb index 0ce801bf38ad..153784d1e536 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -1102,6 +1102,10 @@ azurerm_virtual_machine_scale_set_extension +
  • + azurerm_orchestrated_virtual_machine_scale_set +
  • +
  • azurerm_windows_virtual_machine
  • diff --git a/website/docs/r/linux_virtual_machine.html.markdown b/website/docs/r/linux_virtual_machine.html.markdown index ccbfd86e027e..5e099d2b32e8 100644 --- a/website/docs/r/linux_virtual_machine.html.markdown +++ b/website/docs/r/linux_virtual_machine.html.markdown @@ -170,6 +170,10 @@ The following arguments are supported: * `tags` - (Optional) A mapping of tags which should be assigned to this Virtual Machine. +* `virtual_machine_scale_set_id` - (Optional) Specifies the Orchestrated Virtual Machine Scale Set that this Virtual Machine should be created within. Changing this forces a new resource to be created. + +~> **NOTE:** Orchestrated Virtual Machine Scale Sets can be provisioned using [the `azurerm_orchestrated_virtual_machine_scale_set` resource](/docs/providers/azurerm/r/orchestrated_virtual_machine_scale_set.html). + * `zone` - (Optional) The Zone in which this Virtual Machine should be created. Changing this forces a new resource to be created. --- diff --git a/website/docs/r/orchestrated_virtual_machine_scale_set.html.markdown b/website/docs/r/orchestrated_virtual_machine_scale_set.html.markdown new file mode 100644 index 000000000000..aab15eae06a0 --- /dev/null +++ b/website/docs/r/orchestrated_virtual_machine_scale_set.html.markdown @@ -0,0 +1,84 @@ +--- +subcategory: "Compute" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_orchestrated_virtual_machine_scale_set" +description: |- + Manages an Orchestrated Virtual Machine Scale Set. +--- + +# azurerm_orchestrated_virtual_machine_scale_set + +Manages an Orchestrated Virtual Machine Scale Set. + +-> **Note:** Orchestrated Virtual Machine Scale Sets are in Public Preview - [more details can be found in the Azure Documentation](https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/orchestration-modes). + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_orchestrated_virtual_machine_scale_set" "example" { + name = "example-VMSS" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + + platform_fault_domain_count = 5 + single_placement_group = true + + zones = ["1"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Orchestrated Virtual Machine Scale Set. Changing this forces a new resource to be created. + +* `location` - (Required) The Azure location where the Orchestrated Virtual Machine Scale Set should exist. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the Resource Group in which the Orchestrated Virtual Machine Scale Set should exist. Changing this forces a new resource to be created. + +* `platform_fault_domain_count` - (Required) Specifies the number of fault domains that are used by this Orchestrated Virtual Machine Scale Set. Changing this forces a new resource to be created. + +~> **NOTE:** The number of Fault Domains varies depending on which Azure Region you're using - a list can be found [here](https://github.com/MicrosoftDocs/azure-docs/blob/master/includes/managed-disks-common-fault-domain-region-list.md). + +* `single_placement_group` - (Required) Should the Orchestrated Virtual Machine Scale Set use single placement group? Changing this forces a new resource to be created. + +~> **NOTE:** Due to a limitation of the Azure API at this time, you can only assign `single_placement_group` to `true`. + +You cannot assign `single_placement_group` to `false` unless you have opted-in the private preview program of the orchestration mode of virtual machine scale sets. + +* `zones` - (Optional) A list of Availability Zones in which the Virtual Machines in this Scale Set should be created in. Changing this forces a new resource to be created. + +~> **Note:** Due to a limitation of the Azure API at this time only one Availability Zone can be defined. + +* `tags` - (Optional) A mapping of tags which should be assigned to this Orchestrated Virtual Machine Scale Set. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The ID of the Orchestrated Virtual Machine Scale Set. + +* `unique_id` - The Unique ID for the Orchestrated Virtual Machine Scale Set. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Orchestrated Virtual Machine Scale Set. +* `update` - (Defaults to 30 minutes) Used when updating the Orchestrated Virtual Machine Scale Set. +* `read` - (Defaults to 5 minutes) Used when retrieving the Orchestrated Virtual Machine Scale Set. +* `delete` - (Defaults to 30 minutes) Used when deleting the Orchestrated Virtual Machine Scale Set. + +## Import + +An Orchestrated Virtual Machine Scale Set can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_orchestrated_virtual_machine_scale_set.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/Microsoft.Compute/virtualMachineScaleSets/scaleset1 +``` diff --git a/website/docs/r/windows_virtual_machine.html.markdown b/website/docs/r/windows_virtual_machine.html.markdown index 1692577037b0..e4f43f543c88 100644 --- a/website/docs/r/windows_virtual_machine.html.markdown +++ b/website/docs/r/windows_virtual_machine.html.markdown @@ -161,6 +161,10 @@ The following arguments are supported: * `timezone` - (Optional) Specifies the Time Zone which should be used by the Virtual Machine, [the possible values are defined here](https://jackstromberg.com/2017/01/list-of-time-zones-consumed-by-azure/). +* `virtual_machine_scale_set_id` - (Optional) Specifies the Orchestrated Virtual Machine Scale Set that this Virtual Machine should be created within. Changing this forces a new resource to be created. + +~> **NOTE:** Orchestrated Virtual Machine Scale Sets can be provisioned using [the `azurerm_orchestrated_virtual_machine_scale_set` resource](/docs/providers/azurerm/r/orchestrated_virtual_machine_scale_set.html). + * `winrm_listener` - (Optional) One or more `winrm_listener` blocks as defined below. * `zone` - (Optional) The Zone in which this Virtual Machine should be created. Changing this forces a new resource to be created.