provider/azurerm: destroy azurerm_virtual_machine OS Disk VHD on deletion (#7584)

* provider/azurerm: destroy azurerm_virtual_machine OS Disk VHD on deletion

The OS Disk previously wasn't deleted with the VM, this causes subsequent
apply operations which recreate the VM to fail as the VHD blob already exists.

Fixes #6610

* provider/azurerm: add delete_os_disk_on_termination to azurerm_virtual_machine

delete_os_disk_on_termination is a bool which defaults to false to avoid making
a breaking change, and to follow the same flow as the Azure API
This commit is contained in:
Peter McAtominey 2016-07-13 23:52:20 +01:00 committed by Paul Stack
parent d1666ba76c
commit 37e3aa9d8c
2 changed files with 249 additions and 5 deletions

View File

@ -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
}
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 {

View File

@ -16,7 +16,11 @@ func TestAccAzureRMVirtualMachine_basicLinuxMachine(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMVirtualMachineDestroy,
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"