From 83e3ab1fc711c70dcbb5731d941ae9d2ea70083a Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Fri, 22 May 2015 15:31:14 +0200 Subject: [PATCH] Seems to be almost ready... --- builtin/providers/azure/provider.go | 2 +- .../providers/azure/resource_azure_disk.go | 291 +++++++++++++++++- .../azure/resource_azure_instance.go | 146 +++++---- .../azure/resource_azure_security_group.go | 8 +- .../azure/resource_azure_virtual_network.go | 24 +- 5 files changed, 372 insertions(+), 99 deletions(-) diff --git a/builtin/providers/azure/provider.go b/builtin/providers/azure/provider.go index c4f117391..8fec6f012 100644 --- a/builtin/providers/azure/provider.go +++ b/builtin/providers/azure/provider.go @@ -23,7 +23,7 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "azure_disk": resourceAzureDisk(), + "azure_data_disk": resourceAzureDataDisk(), "azure_instance": resourceAzureInstance(), "azure_security_group": resourceAzureSecurityGroup(), "azure_virtual_network": resourceAzureVirtualNetwork(), diff --git a/builtin/providers/azure/resource_azure_disk.go b/builtin/providers/azure/resource_azure_disk.go index 3b67a39c7..bbb4a68cc 100644 --- a/builtin/providers/azure/resource_azure_disk.go +++ b/builtin/providers/azure/resource_azure_disk.go @@ -1,44 +1,301 @@ package azure -import "github.com/hashicorp/terraform/helper/schema" +import ( + "fmt" + "log" -func resourceAzureDisk() *schema.Resource { + "github.com/hashicorp/terraform/helper/schema" + "github.com/svanharmelen/azure-sdk-for-go/management" + "github.com/svanharmelen/azure-sdk-for-go/management/virtualmachinedisk" +) + +const dataDiskBlobStorageURL = "http://%s.blob.core.windows.net/disks/%s.vhd" + +func resourceAzureDataDisk() *schema.Resource { return &schema.Resource{ - Create: resourceAzureDiskCreate, - Read: resourceAzureDiskRead, - Update: resourceAzureDiskUpdate, - Delete: resourceAzureDiskDelete, + Create: resourceAzureDataDiskCreate, + Read: resourceAzureDataDiskRead, + Update: resourceAzureDataDiskUpdate, + Delete: resourceAzureDataDiskDelete, Schema: map[string]*schema.Schema{ "name": &schema.Schema{ Type: schema.TypeString, - Required: true, + Optional: true, + Computed: true, ForceNew: true, }, + + "lun": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + }, + + "size": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + }, + + "caching": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "None", + }, + + "storage": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "media_link": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "source_media_link": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "virtual_machine": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, }, } } -func resourceAzureDiskCreate(d *schema.ResourceData, meta interface{}) error { - //mc := meta.(*management.Client) +func resourceAzureDataDiskCreate(d *schema.ResourceData, meta interface{}) error { + mc := meta.(*management.Client) - return resourceAzureDiskRead(d, meta) + if err := verifyDataDiskParameters(d); err != nil { + return err + } + + lun := d.Get("lun").(int) + vm := d.Get("virtual_machine").(string) + + p := virtualmachinedisk.CreateDataDiskParameters{ + Lun: lun, + LogicalDiskSizeInGB: d.Get("size").(int), + HostCaching: hostCaching(d), + MediaLink: mediaLink(d), + SourceMediaLink: d.Get("source_media_link").(string), + } + + if name, ok := d.GetOk("name"); ok { + p.DiskName = name.(string) + } + + log.Printf("[DEBUG] Adding data disk %d to instance: %s", lun, vm) + req, err := virtualmachinedisk.NewClient(*mc).AddDataDisk(vm, vm, vm, p) + if err != nil { + return fmt.Errorf("Error adding data disk %d to instance %s: %s", lun, vm, err) + } + + // Wait until the data disk is added + if err := mc.WaitForOperation(req, nil); err != nil { + return fmt.Errorf( + "Error waiting for data disk %d to be added to instance %s: %s", lun, vm, err) + } + + log.Printf("[DEBUG] Retrieving data disk %d from instance %s", lun, vm) + disk, err := virtualmachinedisk.NewClient(*mc).GetDataDisk(vm, vm, vm, lun) + if err != nil { + return fmt.Errorf("Error retrieving data disk %d from instance %s: %s", lun, vm, err) + } + + d.SetId(disk.DiskName) + + return resourceAzureDataDiskRead(d, meta) } -func resourceAzureDiskRead(d *schema.ResourceData, meta interface{}) error { - //mc := meta.(*management.Client) +func resourceAzureDataDiskRead(d *schema.ResourceData, meta interface{}) error { + mc := meta.(*management.Client) + + lun := d.Get("lun").(int) + vm := d.Get("virtual_machine").(string) + + log.Printf("[DEBUG] Retrieving data disk: %s", d.Id()) + datadisk, err := virtualmachinedisk.NewClient(*mc).GetDataDisk(vm, vm, vm, lun) + if err != nil { + return fmt.Errorf("Error retrieving data disk %s: %s", d.Id(), err) + } + + d.Set("name", datadisk.DiskName) + d.Set("lun", datadisk.Lun) + d.Set("size", datadisk.LogicalDiskSizeInGB) + d.Set("caching", datadisk.HostCaching) + d.Set("media_link", datadisk.MediaLink) + + log.Printf("[DEBUG] Retrieving disk: %s", d.Id()) + disk, err := virtualmachinedisk.NewClient(*mc).GetDisk(d.Id()) + if err != nil { + return fmt.Errorf("Error retrieving disk %s: %s", d.Id(), err) + } + + d.Set("virtual_machine", disk.AttachedTo.RoleName) return nil } -func resourceAzureDiskUpdate(d *schema.ResourceData, meta interface{}) error { - //mc := meta.(*management.Client) +func resourceAzureDataDiskUpdate(d *schema.ResourceData, meta interface{}) error { + mc := meta.(*management.Client) + lun := d.Get("lun").(int) + vm := d.Get("virtual_machine").(string) - return resourceAzureDiskRead(d, meta) + if d.HasChange("size") { + p := virtualmachinedisk.UpdateDiskParameters{ + Name: d.Id(), + } + + if d.HasChange("size") { + p.ResizedSizeInGB = d.Get("size").(int) + } + + log.Printf("[DEBUG] Updating disk: %s", d.Id()) + req, err := virtualmachinedisk.NewClient(*mc).UpdateDisk(d.Id(), p) + if err != nil { + return fmt.Errorf("Error updating disk %s: %s", d.Id(), err) + } + + // Wait until the disk is updated + if err := mc.WaitForOperation(req, nil); err != nil { + return fmt.Errorf( + "Error waiting for disk %s to be updated: %s", d.Id(), err) + } + } + + if d.HasChange("virtual_machine") { + log.Printf("[DEBUG] Detaching data disk: %s", d.Id()) + req, err := virtualmachinedisk.NewClient(*mc).DeleteDataDisk(vm, vm, vm, lun, false) + if err != nil { + return fmt.Errorf("Error detaching data disk %s: %s", d.Id(), err) + } + + // Wait until the data disk is detached + if err := mc.WaitForOperation(req, nil); err != nil { + return fmt.Errorf( + "Error waiting for data disk %s to be detached: %s", d.Id(), err) + } + + p := virtualmachinedisk.CreateDataDiskParameters{ + DiskName: d.Id(), + Lun: lun, + HostCaching: hostCaching(d), + MediaLink: mediaLink(d), + } + + log.Printf("[DEBUG] Attaching data disk: %s", d.Id()) + req, err = virtualmachinedisk.NewClient(*mc).AddDataDisk(vm, vm, vm, p) + if err != nil { + return fmt.Errorf("Error attaching data disk %s to instance %s: %s", d.Id(), vm, err) + } + + // Wait until the data disk is attached + if err := mc.WaitForOperation(req, nil); err != nil { + return fmt.Errorf( + "Error waiting for data disk %s to be attached to instance %s: %s", d.Id(), vm, err) + } + + // Make sure we return here since all possible changes are + // already updated if we reach this point + return nil + } + + if d.HasChange("caching") || d.HasChange("lun") { + p := virtualmachinedisk.UpdateDataDiskParameters{ + DiskName: d.Id(), + Lun: lun, + HostCaching: hostCaching(d), + MediaLink: mediaLink(d), + } + + log.Printf("[DEBUG] Updating data disk: %s", d.Id()) + req, err := virtualmachinedisk.NewClient(*mc).UpdateDataDisk(vm, vm, vm, lun, p) + if err != nil { + return fmt.Errorf("Error updating data disk %s: %s", d.Id(), err) + } + + // Wait until the data disk is updated + if err := mc.WaitForOperation(req, nil); err != nil { + return fmt.Errorf( + "Error waiting for data disk %s to be updated: %s", d.Id(), err) + } + } + + return resourceAzureDataDiskRead(d, meta) } -func resourceAzureDiskDelete(d *schema.ResourceData, meta interface{}) error { - //mc := meta.(*management.Client) +func resourceAzureDataDiskDelete(d *schema.ResourceData, meta interface{}) error { + mc := meta.(*management.Client) + lun := d.Get("lun").(int) + vm := d.Get("virtual_machine").(string) + + // If a name was not supplied, it means we created a new emtpy disk and we now want to + // delete that disk again. Otherwise we only want to detach the disk and keep the blob. + _, removeBlob := d.GetOk("name") + + log.Printf("[DEBUG] Detaching data disk %s with removeBlob = %t", d.Id(), removeBlob) + req, err := virtualmachinedisk.NewClient(*mc).DeleteDataDisk(vm, vm, vm, lun, removeBlob) + if err != nil { + return fmt.Errorf( + "Error detaching data disk %s with removeBlob = %t: %s", d.Id(), removeBlob, err) + } + + // Wait until the data disk is detached and optionally deleted + if err := mc.WaitForOperation(req, nil); err != nil { + return fmt.Errorf( + "Error waiting for data disk %s to be detached with removeBlob = %t: %s", + d.Id(), removeBlob, err) + } + + d.SetId("") + + return nil +} + +func hostCaching(d *schema.ResourceData) virtualmachinedisk.HostCachingType { + switch d.Get("caching").(string) { + case "ReadOnly": + return virtualmachinedisk.HostCachingTypeReadOnly + case "ReadWrite": + return virtualmachinedisk.HostCachingTypeReadWrite + default: + return virtualmachinedisk.HostCachingTypeNone + } +} + +func mediaLink(d *schema.ResourceData) string { + mediaLink, ok := d.GetOk("media_link") + if ok { + return mediaLink.(string) + } + + name, ok := d.GetOk("name") + if !ok { + name = fmt.Sprintf("%s-%d", d.Get("virtual_machine").(string), d.Get("lun").(int)) + } + + return fmt.Sprintf(dataDiskBlobStorageURL, d.Get("storage").(string), name.(string)) +} + +func verifyDataDiskParameters(d *schema.ResourceData) error { + caching := d.Get("caching").(string) + if caching != "None" && caching != "ReadOnly" && caching != "ReadWrite" { + return fmt.Errorf( + "Invalid caching type %s! Valid options are 'None', 'ReadOnly' and 'ReadWrite'.", caching) + } + + if _, ok := d.GetOk("media_link"); !ok { + if _, ok := d.GetOk("storage"); !ok { + return fmt.Errorf("If not supplying 'media_link', you must supply 'storage'.") + } + } return nil } diff --git a/builtin/providers/azure/resource_azure_instance.go b/builtin/providers/azure/resource_azure_instance.go index 958a28bbe..3b682d018 100644 --- a/builtin/providers/azure/resource_azure_instance.go +++ b/builtin/providers/azure/resource_azure_instance.go @@ -2,6 +2,7 @@ package azure import ( "bytes" + "encoding/base64" "fmt" "log" @@ -16,8 +17,9 @@ import ( ) const ( - linux = "Linux" - windows = "Windows" + linux = "Linux" + windows = "Windows" + osDiskBlobStorageURL = "http://%s.blob.core.windows.net/vhds/%s.vhd" ) func resourceAzureInstance() *schema.Resource { @@ -183,28 +185,23 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err return err } - // Verify if we have all parameters required for the image OS type - if err := verifyParameters(d, osType); err != nil { + // Verify if we have all required parameters + if err := verifyInstanceParameters(d, osType); err != nil { return err } - log.Printf("[DEBUG] Creating Cloud Service for instance: %s", name) - req, err := hostedservice.NewClient(*mc). - CreateHostedService( - name, - d.Get("location").(string), - d.Get("reverse_dns").(string), - name, - fmt.Sprintf("Cloud Service created automatically for instance %s", name), - ) - if err != nil { - return fmt.Errorf("Error creating Cloud Service for instance %s: %s", name, err) + p := hostedservice.CreateHostedServiceParameters{ + ServiceName: name, + Label: base64.StdEncoding.EncodeToString([]byte(name)), + Description: fmt.Sprintf("Cloud Service created automatically for instance %s", name), + Location: d.Get("location").(string), + ReverseDNSFqdn: d.Get("reverse_dns").(string), } - // Wait until the Cloud Service is created - if err := mc.WaitAsyncOperation(req); err != nil { - return fmt.Errorf( - "Error waiting for Cloud Service of instance %s to be created: %s", name, err) + log.Printf("[DEBUG] Creating Cloud Service for instance: %s", name) + err = hostedservice.NewClient(*mc).CreateHostedService(p) + if err != nil { + return fmt.Errorf("Error creating Cloud Service for instance %s: %s", name, err) } // Put in this defer here, so we are sure to cleanup already created parts @@ -217,9 +214,9 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err } // Wait until the Cloud Service is deleted - if err := mc.WaitAsyncOperation(req); err != nil { + if err := mc.WaitForOperation(req, nil); err != nil { log.Printf( - "[DEBUG] Error waiting for Cloud Service of instance %s to be deleted : %s", name, err) + "[DEBUG] Error waiting for Cloud Service of instance %s to be deleted: %s", name, err) } } }(mc) @@ -308,13 +305,13 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err } log.Printf("[DEBUG] Creating the new instance...") - req, err = virtualmachine.NewClient(*mc).CreateDeployment(role, name, options) + req, err := virtualmachine.NewClient(*mc).CreateDeployment(role, name, options) if err != nil { return fmt.Errorf("Error creating instance %s: %s", name, err) } log.Printf("[DEBUG] Waiting for the new instance to be created...") - if err := mc.WaitAsyncOperation(req); err != nil { + if err := mc.WaitForOperation(req, nil); err != nil { return fmt.Errorf( "Error waiting for instance %s to be created: %s", name, err) } @@ -400,14 +397,15 @@ func resourceAzureInstanceRead(d *schema.ResourceData, meta interface{}) error { connType := "ssh" if dpmt.RoleList[0].OSVirtualHardDisk.OS == windows { - connType = windows + connType = "winrm" } // Set the connection info for any configured provisioners d.SetConnInfo(map[string]string{ - "type": connType, - "host": dpmt.VirtualIPs[0].Address, - "user": d.Get("username").(string), + "type": connType, + "host": dpmt.VirtualIPs[0].Address, + "user": d.Get("username").(string), + "password": d.Get("password").(string), }) return nil @@ -427,8 +425,8 @@ func resourceAzureInstanceUpdate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error retrieving role of instance %s: %s", d.Id(), err) } - // Verify if we have all parameters required for the image OS type - if err := verifyParameters(d, role.OSVirtualHardDisk.OS); err != nil { + // Verify if we have all required parameters + if err := verifyInstanceParameters(d, role.OSVirtualHardDisk.OS); err != nil { return err } @@ -481,7 +479,7 @@ func resourceAzureInstanceUpdate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error updating role of instance %s: %s", d.Id(), err) } - if err := mc.WaitAsyncOperation(req); err != nil { + if err := mc.WaitForOperation(req, nil); err != nil { return fmt.Errorf( "Error waiting for role of instance %s to be updated: %s", d.Id(), err) } @@ -499,7 +497,7 @@ func resourceAzureInstanceDelete(d *schema.ResourceData, meta interface{}) error } // Wait until the instance is deleted - if err := mc.WaitAsyncOperation(req); err != nil { + if err := mc.WaitForOperation(req, nil); err != nil { return fmt.Errorf( "Error waiting for instance %s to be deleted: %s", d.Id(), err) } @@ -524,12 +522,12 @@ func retrieveImageDetails( mc *management.Client, label string, storage string) (func(*virtualmachine.Role) error, string, error) { - configureForImage, osType, err := retrieveOSImageDetails(mc, label, storage) + configureForImage, osType, err := retrieveVMImageDetails(mc, label) if err == nil { return configureForImage, osType, nil } - configureForImage, osType, err = retrieveVMImageDetails(mc, label) + configureForImage, osType, err = retrieveOSImageDetails(mc, label, storage) if err == nil { return configureForImage, osType, nil } @@ -537,53 +535,15 @@ func retrieveImageDetails( return nil, "", fmt.Errorf("Could not find image with label '%s'", label) } -func retrieveOSImageDetails( - mc *management.Client, - label, - storage string) (func(*virtualmachine.Role) error, string, error) { - imgs, err := osimage.NewClient(*mc).GetImageList() - if err != nil { - return nil, "", fmt.Errorf("Error retrieving image details: %s", err) - } - - for _, img := range imgs { - if img.Label == label { - if img.OS != linux && img.OS != windows { - return nil, "", fmt.Errorf("Unsupported image OS: %s", img.OS) - } - if img.MediaLink == "" { - if storage == "" { - return nil, "", - fmt.Errorf("When using a platform image, the 'storage' parameter is required") - } - img.MediaLink = fmt.Sprintf("http://%s.blob.core.windows.net/vhds/%s.vhd", storage, label) - } - - configureForImage := func(role *virtualmachine.Role) error { - return vmutils.ConfigureDeploymentFromPlatformImage( - role, - img.Name, - img.MediaLink, - label, - ) - } - - return configureForImage, img.OS, nil - } - } - - return nil, "", fmt.Errorf("Could not find image with label '%s'", label) -} - func retrieveVMImageDetails( mc *management.Client, label string) (func(*virtualmachine.Role) error, string, error) { - imgs, err := virtualmachineimage.NewClient(*mc).GetImageList() + imgs, err := virtualmachineimage.NewClient(*mc).ListVirtualMachineImages() if err != nil { return nil, "", fmt.Errorf("Error retrieving image details: %s", err) } - for _, img := range imgs { + for _, img := range imgs.VMImages { if img.Label == label { if img.OSDiskConfiguration.OS != linux && img.OSDiskConfiguration.OS != windows { return nil, "", fmt.Errorf("Unsupported image OS: %s", img.OSDiskConfiguration.OS) @@ -605,6 +565,44 @@ func retrieveVMImageDetails( return nil, "", fmt.Errorf("Could not find image with label '%s'", label) } +func retrieveOSImageDetails( + mc *management.Client, + label, + storage string) (func(*virtualmachine.Role) error, string, error) { + imgs, err := osimage.NewClient(*mc).ListOSImages() + if err != nil { + return nil, "", fmt.Errorf("Error retrieving image details: %s", err) + } + + for _, img := range imgs.OSImages { + if img.Label == label { + if img.OS != linux && img.OS != windows { + return nil, "", fmt.Errorf("Unsupported image OS: %s", img.OS) + } + if img.MediaLink == "" { + if storage == "" { + return nil, "", + fmt.Errorf("When using a platform image, the 'storage' parameter is required") + } + img.MediaLink = fmt.Sprintf(osDiskBlobStorageURL, storage, label) + } + + configureForImage := func(role *virtualmachine.Role) error { + return vmutils.ConfigureDeploymentFromPlatformImage( + role, + img.Name, + img.MediaLink, + label, + ) + } + + return configureForImage, img.OS, nil + } + } + + return nil, "", fmt.Errorf("Could not find image with label '%s'", label) +} + func endpointProtocol(p string) virtualmachine.InputEndpointProtocol { if p == "tcp" { return virtualmachine.InputEndpointProtocolTCP @@ -613,7 +611,7 @@ func endpointProtocol(p string) virtualmachine.InputEndpointProtocol { return virtualmachine.InputEndpointProtocolUDP } -func verifyParameters(d *schema.ResourceData, osType string) error { +func verifyInstanceParameters(d *schema.ResourceData, osType string) error { if osType == linux { _, pass := d.GetOk("password") _, key := d.GetOk("ssh_key_thumbprint") diff --git a/builtin/providers/azure/resource_azure_security_group.go b/builtin/providers/azure/resource_azure_security_group.go index cbac36a3e..ec818ee07 100644 --- a/builtin/providers/azure/resource_azure_security_group.go +++ b/builtin/providers/azure/resource_azure_security_group.go @@ -124,7 +124,7 @@ func resourceAzureSecurityGroupCreate(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Error creating Network Security Group %s: %s", name, err) } - if err := mc.WaitAsyncOperation(req); err != nil { + if err := mc.WaitForOperation(req, nil); err != nil { return fmt.Errorf( "Error waiting for Network Security Group %s to be created: %s", name, err) } @@ -185,7 +185,7 @@ func resourceAzureSecurityGroupRuleCreate( return fmt.Errorf("Error creating Network Security Group rule %s: %s", name, err) } - if err := mc.WaitAsyncOperation(req); err != nil { + if err := mc.WaitForOperation(req, nil); err != nil { return fmt.Errorf( "Error waiting for Network Security Group rule %s to be created: %s", name, err) } @@ -278,7 +278,7 @@ func resourceAzureSecurityGroupDelete(d *schema.ResourceData, meta interface{}) } // Wait until the network security group is deleted - if err := mc.WaitAsyncOperation(req); err != nil { + if err := mc.WaitForOperation(req, nil); err != nil { return fmt.Errorf( "Error waiting for Network Security Group %s to be deleted: %s", d.Id(), err) } @@ -300,7 +300,7 @@ func resourceAzureSecurityGroupRuleDelete( return fmt.Errorf("Error deleting Network Security Group rule %s: %s", name, err) } - if err := mc.WaitAsyncOperation(req); err != nil { + if err := mc.WaitForOperation(req, nil); err != nil { return fmt.Errorf( "Error waiting for Network Security Group rule %s to be deleted: %s", name, err) } diff --git a/builtin/providers/azure/resource_azure_virtual_network.go b/builtin/providers/azure/resource_azure_virtual_network.go index ce636044d..eaf1ff049 100644 --- a/builtin/providers/azure/resource_azure_virtual_network.go +++ b/builtin/providers/azure/resource_azure_virtual_network.go @@ -76,11 +76,17 @@ func resourceAzureVirtualNetworkCreate(d *schema.ResourceData, meta interface{}) nc.Configuration.VirtualNetworkSites = append(nc.Configuration.VirtualNetworkSites, network) - err = virtualnetwork.NewClient(*mc).SetVirtualNetworkConfiguration(nc) + req, err := virtualnetwork.NewClient(*mc).SetVirtualNetworkConfiguration(nc) if err != nil { return fmt.Errorf("Error creating Virtual Network %s: %s", name, err) } + // Wait until the virtual network is created + if err := mc.WaitForOperation(req, nil); err != nil { + log.Printf( + "[DEBUG] Error waiting for Virtual Network %s to be created: %s", name, err) + } + d.SetId(name) return resourceAzureVirtualNetworkRead(d, meta) @@ -142,11 +148,17 @@ func resourceAzureVirtualNetworkUpdate(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Virtual Network %s does not exists!", d.Id()) } - err = virtualnetwork.NewClient(*mc).SetVirtualNetworkConfiguration(nc) + req, err := virtualnetwork.NewClient(*mc).SetVirtualNetworkConfiguration(nc) if err != nil { return fmt.Errorf("Error updating Virtual Network %s: %s", d.Id(), err) } + // Wait until the virtual network is updated + if err := mc.WaitForOperation(req, nil); err != nil { + log.Printf( + "[DEBUG] Error waiting for Virtual Network %s to be updated: %s", d.Id(), err) + } + return resourceAzureVirtualNetworkRead(d, meta) } @@ -167,11 +179,17 @@ func resourceAzureVirtualNetworkDelete(d *schema.ResourceData, meta interface{}) nc.Configuration.VirtualNetworkSites = filtered - err = virtualnetwork.NewClient(*mc).SetVirtualNetworkConfiguration(nc) + req, err := virtualnetwork.NewClient(*mc).SetVirtualNetworkConfiguration(nc) if err != nil { return fmt.Errorf("Error deleting Virtual Network %s: %s", d.Id(), err) } + // Wait until the virtual network is deleted + if err := mc.WaitForOperation(req, nil); err != nil { + log.Printf( + "[DEBUG] Error waiting for Virtual Network %s to be deleted: %s", d.Id(), err) + } + d.SetId("") return nil