diff --git a/builtin/providers/azurerm/resource_arm_virtual_machine.go b/builtin/providers/azurerm/resource_arm_virtual_machine.go index 95698b009..6c390c74f 100644 --- a/builtin/providers/azurerm/resource_arm_virtual_machine.go +++ b/builtin/providers/azurerm/resource_arm_virtual_machine.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "net/http" + "net/url" "strings" "github.com/Azure/azure-sdk-for-go/arm/compute" @@ -158,6 +159,12 @@ func resourceArmVirtualMachine() *schema.Resource { Set: resourceArmVirtualMachineStorageOsDiskHash, }, + "delete_os_disk_on_termination": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "storage_data_disk": { Type: schema.TypeList, Optional: true, @@ -550,9 +557,48 @@ func resourceArmVirtualMachineDelete(d *schema.ResourceData, meta interface{}) e resGroup := id.ResourceGroup name := id.Path["virtualMachines"] - _, err = vmClient.Delete(resGroup, name, make(chan struct{})) + if _, err = vmClient.Delete(resGroup, name, make(chan struct{})); err != nil { + return err + } - return err + if deleteOsDisk := d.Get("delete_os_disk_on_termination").(bool); !deleteOsDisk { + log.Printf("[INFO] delete_os_disk_on_termination is false, skipping delete") + return nil + } + + osDisk, err := expandAzureRmVirtualMachineOsDisk(d) + if err != nil { + return fmt.Errorf("Error expanding OS Disk") + } + + vhdURL, err := url.Parse(*osDisk.Vhd.URI) + if err != nil { + return fmt.Errorf("Cannot parse OS Disk VHD URI: %s", err) + } + + // VHD URI is in the form: https://storageAccountName.blob.core.windows.net/containerName/blobName + storageAccountName := strings.Split(vhdURL.Host, ".")[0] + path := strings.Split(strings.TrimPrefix(vhdURL.Path, "/"), "/") + containerName := path[0] + blobName := path[1] + + blobClient, storageAccountExists, err := meta.(*ArmClient).getBlobStorageClientForStorageAccount(id.ResourceGroup, storageAccountName) + if err != nil { + return fmt.Errorf("Error creating blob store account for VHD deletion: %s", err) + } + + if !storageAccountExists { + log.Printf("[INFO] Storage Account %q doesn't exist so the VHD blob won't exist", storageAccountName) + return nil + } + + log.Printf("[INFO] Deleting VHD blob %s", blobName) + _, err = blobClient.DeleteBlobIfExists(containerName, blobName, nil) + if err != nil { + return fmt.Errorf("Error deleting VHD blob: %s", err) + } + + return nil } func resourceArmVirtualMachinePlanHash(v interface{}) int { diff --git a/builtin/providers/azurerm/resource_arm_virtual_machine_test.go b/builtin/providers/azurerm/resource_arm_virtual_machine_test.go index eba46cd61..ef098a5a6 100644 --- a/builtin/providers/azurerm/resource_arm_virtual_machine_test.go +++ b/builtin/providers/azurerm/resource_arm_virtual_machine_test.go @@ -14,9 +14,13 @@ func TestAccAzureRMVirtualMachine_basicLinuxMachine(t *testing.T) { ri := acctest.RandInt() config := fmt.Sprintf(testAccAzureRMVirtualMachine_basicLinuxMachine, ri, ri, ri, ri, ri, ri, ri) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMVirtualMachineDestroy, + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: resource.ComposeTestCheckFunc( + testCheckAzureRMVirtualMachineDestroy, + // deletion of OS Disk VHD is opt in + testCheckAzureRMVirtualMachineOSDiskVHDExistance(true), + ), Steps: []resource.TestStep{ { Config: config, @@ -167,6 +171,29 @@ func TestAccAzureRMVirtualMachine_winRMConfig(t *testing.T) { }) } +func TestAccAzureRMVirtualMachine_deleteVHD(t *testing.T) { + ri := acctest.RandInt() + preConfig := fmt.Sprintf(testAccAzureRMVirtualMachine_basicLinuxMachineDestroyOSDisk, ri, ri, ri, ri, ri, ri, ri) + postConfig := fmt.Sprintf(testAccAzureRMVirtualMachine_basicLinuxMachineDeleteVM, ri, ri, ri, ri, ri) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMVirtualMachineDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMVirtualMachineExists("azurerm_virtual_machine.test"), + ), + }, + { + Config: postConfig, + Check: testCheckAzureRMVirtualMachineOSDiskVHDExistance(false), + }, + }, + }) +} + func testCheckAzureRMVirtualMachineExists(name string) resource.TestCheckFunc { return func(s *terraform.State) error { // Ensure we have enough information in state to look up in API @@ -221,6 +248,36 @@ func testCheckAzureRMVirtualMachineDestroy(s *terraform.State) error { return nil } +func testCheckAzureRMVirtualMachineOSDiskVHDExistance(shouldExist bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_storage_container" { + continue + } + + // fetch storage account and container name + resourceGroup := rs.Primary.Attributes["resource_group_name"] + storageAccountName := rs.Primary.Attributes["storage_account_name"] + containerName := rs.Primary.Attributes["name"] + storageClient, _, err := testAccProvider.Meta().(*ArmClient).getBlobStorageClientForStorageAccount(resourceGroup, storageAccountName) + if err != nil { + return fmt.Errorf("Error creating Blob storage client: %s", err) + } + + exists, err := storageClient.BlobExists(containerName, "myosdisk1.vhd") + if err != nil { + return fmt.Errorf("Error checking if OS Disk VHD Blob exists: %s", err) + } + + if exists && !shouldExist { + return fmt.Errorf("OS Disk VHD Blob still exists") + } + } + + return nil + } +} + var testAccAzureRMVirtualMachine_basicLinuxMachine = ` resource "azurerm_resource_group" "test" { name = "acctestrg-%d" @@ -309,6 +366,147 @@ resource "azurerm_virtual_machine" "test" { } ` +var testAccAzureRMVirtualMachine_basicLinuxMachineDestroyOSDisk = ` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "West US" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%d" + address_space = ["10.0.0.0/16"] + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "acctni-%d" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_storage_account" "test" { + name = "accsa%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "westus" + account_type = "Standard_LRS" + + tags { + environment = "staging" + } +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_virtual_machine" "test" { + name = "acctvm-%d" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.test.id}"] + vm_size = "Standard_A0" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "14.04.2-LTS" + version = "latest" + } + + storage_os_disk { + name = "myosdisk1" + vhd_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" + caching = "ReadWrite" + create_option = "FromImage" + } + + delete_os_disk_on_termination = true + + os_profile { + computer_name = "hostname%d" + admin_username = "testadmin" + admin_password = "Password1234!" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "Production" + cost-center = "Ops" + } +} +` + +var testAccAzureRMVirtualMachine_basicLinuxMachineDeleteVM = ` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "West US" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%d" + address_space = ["10.0.0.0/16"] + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "acctni-%d" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_storage_account" "test" { + name = "accsa%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "westus" + account_type = "Standard_LRS" + + tags { + environment = "staging" + } +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} +` + var testAccAzureRMVirtualMachine_withDataDisk = ` resource "azurerm_resource_group" "test" { name = "acctestrg-%d"