mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Automatically removing all resources allocated by VM (#50652)
This commit is contained in:
parent
1a105a99dc
commit
be859a9f8e
2 changed files with 166 additions and 69 deletions
|
@ -276,9 +276,11 @@ options:
|
||||||
- subnet
|
- subnet
|
||||||
remove_on_absent:
|
remove_on_absent:
|
||||||
description:
|
description:
|
||||||
- When removing a VM using state 'absent', also remove associated resources
|
- "When removing a VM using state 'absent', also remove associated resources."
|
||||||
- "It can be 'all' or a list with any of the following: ['network_interfaces', 'virtual_storage', 'public_ips']"
|
- "It can be 'all' or 'all_autocreated' or a list with any of the following: ['network_interfaces', 'virtual_storage', 'public_ips']."
|
||||||
- Any other input will be ignored
|
- "To remove all resources referred by VM use 'all'."
|
||||||
|
- "To remove all resources that were automatically created while provisioning VM use 'all_autocreated'."
|
||||||
|
- Any other input will be ignored.
|
||||||
default: ['all']
|
default: ['all']
|
||||||
plan:
|
plan:
|
||||||
description:
|
description:
|
||||||
|
@ -497,15 +499,6 @@ EXAMPLES = '''
|
||||||
name: testvm002
|
name: testvm002
|
||||||
restarted: yes
|
restarted: yes
|
||||||
|
|
||||||
- name: remove vm and all resources except public ips
|
|
||||||
azure_rm_virtualmachine:
|
|
||||||
resource_group: Testing
|
|
||||||
name: testvm002
|
|
||||||
state: absent
|
|
||||||
remove_on_absent:
|
|
||||||
- network_interfaces
|
|
||||||
- virtual_storage
|
|
||||||
|
|
||||||
- name: Create a VM with an Availability Zone
|
- name: Create a VM with an Availability Zone
|
||||||
azure_rm_virtualmachine:
|
azure_rm_virtualmachine:
|
||||||
resource_group: Testing
|
resource_group: Testing
|
||||||
|
@ -515,6 +508,13 @@ EXAMPLES = '''
|
||||||
admin_password: password01
|
admin_password: password01
|
||||||
image: customimage001
|
image: customimage001
|
||||||
zones: [1]
|
zones: [1]
|
||||||
|
|
||||||
|
- name: Remove a VM and all resources that were autocreated
|
||||||
|
azure_rm_virtualmachine:
|
||||||
|
resource_group: Testing
|
||||||
|
name: testvm002
|
||||||
|
remove_on_absent: all_autocreated
|
||||||
|
state: absent
|
||||||
'''
|
'''
|
||||||
|
|
||||||
RETURN = '''
|
RETURN = '''
|
||||||
|
@ -1210,7 +1210,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
"from the marketplace. - {2}").format(self.name, self.plan, str(exc)))
|
"from the marketplace. - {2}").format(self.name, self.plan, str(exc)))
|
||||||
|
|
||||||
self.log("Create virtual machine with parameters:")
|
self.log("Create virtual machine with parameters:")
|
||||||
self.create_or_update_vm(vm_resource)
|
self.create_or_update_vm(vm_resource, 'all_autocreated' in self.remove_on_absent)
|
||||||
|
|
||||||
elif self.differences and len(self.differences) > 0:
|
elif self.differences and len(self.differences) > 0:
|
||||||
# Update the VM based on detected config differences
|
# Update the VM based on detected config differences
|
||||||
|
@ -1342,7 +1342,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
vm_resource.storage_profile.data_disks = data_disks
|
vm_resource.storage_profile.data_disks = data_disks
|
||||||
|
|
||||||
self.log("Update virtual machine with parameters:")
|
self.log("Update virtual machine with parameters:")
|
||||||
self.create_or_update_vm(vm_resource)
|
self.create_or_update_vm(vm_resource, False)
|
||||||
|
|
||||||
# Make sure we leave the machine in requested power state
|
# Make sure we leave the machine in requested power state
|
||||||
if (powerstate_change == 'poweron' and
|
if (powerstate_change == 'poweron' and
|
||||||
|
@ -1421,7 +1421,6 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
nic_dict = self.serialize_obj(nic, 'NetworkInterface')
|
nic_dict = self.serialize_obj(nic, 'NetworkInterface')
|
||||||
interface_dict['name'] = int_dict['networkInterfaces']
|
interface_dict['name'] = int_dict['networkInterfaces']
|
||||||
interface_dict['properties'] = nic_dict['properties']
|
interface_dict['properties'] = nic_dict['properties']
|
||||||
|
|
||||||
# Expand public IPs to include config properties
|
# Expand public IPs to include config properties
|
||||||
for interface in result['properties']['networkProfile']['networkInterfaces']:
|
for interface in result['properties']['networkProfile']['networkInterfaces']:
|
||||||
for config in interface['properties']['ipConfigurations']:
|
for config in interface['properties']['ipConfigurations']:
|
||||||
|
@ -1493,12 +1492,28 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
self.fail("Error generalizing virtual machine {0} - {1}".format(self.name, str(exc)))
|
self.fail("Error generalizing virtual machine {0} - {1}".format(self.name, str(exc)))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def remove_autocreated_resources(self, tags):
|
||||||
|
if tags:
|
||||||
|
sa_name = tags.get('_own_sa_')
|
||||||
|
nic_name = tags.get('_own_nic_')
|
||||||
|
pip_name = tags.get('_own_pip_')
|
||||||
|
nsg_name = tags.get('_own_nsg_')
|
||||||
|
if sa_name:
|
||||||
|
self.delete_storage_account(self.resource_group, sa_name)
|
||||||
|
if nic_name:
|
||||||
|
self.delete_nic(self.resource_group, nic_name)
|
||||||
|
if pip_name:
|
||||||
|
self.delete_pip(self.resource_group, pip_name)
|
||||||
|
if nsg_name:
|
||||||
|
self.delete_nsg(self.resource_group, nsg_name)
|
||||||
|
|
||||||
def delete_vm(self, vm):
|
def delete_vm(self, vm):
|
||||||
vhd_uris = []
|
vhd_uris = []
|
||||||
managed_disk_ids = []
|
managed_disk_ids = []
|
||||||
nic_names = []
|
nic_names = []
|
||||||
pip_names = []
|
pip_names = []
|
||||||
|
|
||||||
|
if 'all_autocreated' not in self.remove_on_absent:
|
||||||
if self.remove_on_absent.intersection(set(['all', 'virtual_storage'])):
|
if self.remove_on_absent.intersection(set(['all', 'virtual_storage'])):
|
||||||
# store the attached vhd info so we can nuke it after the VM is gone
|
# store the attached vhd info so we can nuke it after the VM is gone
|
||||||
if(vm.storage_profile.os_disk.managed_disk):
|
if(vm.storage_profile.os_disk.managed_disk):
|
||||||
|
@ -1550,6 +1565,9 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self.fail("Error deleting virtual machine {0} - {1}".format(self.name, str(exc)))
|
self.fail("Error deleting virtual machine {0} - {1}".format(self.name, str(exc)))
|
||||||
|
|
||||||
|
if 'all_autocreated' in self.remove_on_absent:
|
||||||
|
self.remove_autocreated_resources(vm.tags)
|
||||||
|
else:
|
||||||
# TODO: parallelize nic, vhd, and public ip deletions with begin_deleting
|
# TODO: parallelize nic, vhd, and public ip deletions with begin_deleting
|
||||||
# TODO: best-effort to keep deleting other linked resources if we encounter an error
|
# TODO: best-effort to keep deleting other linked resources if we encounter an error
|
||||||
if self.remove_on_absent.intersection(set(['all', 'virtual_storage'])):
|
if self.remove_on_absent.intersection(set(['all', 'virtual_storage'])):
|
||||||
|
@ -1567,6 +1585,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
self.log('Deleting public IPs')
|
self.log('Deleting public IPs')
|
||||||
for pip_dict in pip_names:
|
for pip_dict in pip_names:
|
||||||
self.delete_pip(pip_dict['resource_group'], pip_dict['name'])
|
self.delete_pip(pip_dict['resource_group'], pip_dict['name'])
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_network_interface(self, resource_group, name):
|
def get_network_interface(self, resource_group, name):
|
||||||
|
@ -1575,6 +1594,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
return nic
|
return nic
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self.fail("Error fetching network interface {0} - {1}".format(name, str(exc)))
|
self.fail("Error fetching network interface {0} - {1}".format(name, str(exc)))
|
||||||
|
return True
|
||||||
|
|
||||||
def delete_nic(self, resource_group, name):
|
def delete_nic(self, resource_group, name):
|
||||||
self.log("Deleting network interface {0}".format(name))
|
self.log("Deleting network interface {0}".format(name))
|
||||||
|
@ -1597,6 +1617,15 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
# Delete returns nada. If we get here, assume that all is well.
|
# Delete returns nada. If we get here, assume that all is well.
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def delete_nsg(self, resource_group, name):
|
||||||
|
self.results['actions'].append("Deleted NSG {0}".format(name))
|
||||||
|
try:
|
||||||
|
poller = self.network_client.network_security_groups.delete(resource_group, name)
|
||||||
|
self.get_poller_result(poller)
|
||||||
|
except Exception as exc:
|
||||||
|
self.fail("Error deleting {0} - {1}".format(name, str(exc)))
|
||||||
|
return True
|
||||||
|
|
||||||
def delete_managed_disks(self, managed_disk_ids):
|
def delete_managed_disks(self, managed_disk_ids):
|
||||||
for mdi in managed_disk_ids:
|
for mdi in managed_disk_ids:
|
||||||
try:
|
try:
|
||||||
|
@ -1604,6 +1633,16 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
self.get_poller_result(poller)
|
self.get_poller_result(poller)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self.fail("Error deleting managed disk {0} - {1}".format(mdi, str(exc)))
|
self.fail("Error deleting managed disk {0} - {1}".format(mdi, str(exc)))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def delete_storage_account(self, resource_group, name):
|
||||||
|
self.log("Delete storage account {0}".format(name))
|
||||||
|
self.results['actions'].append("Deleted storage account {0}".format(name))
|
||||||
|
try:
|
||||||
|
self.storage_client.storage_accounts.delete(self.resource_group, name)
|
||||||
|
except Exception as exc:
|
||||||
|
self.fail("Error deleting storage account {0} - {2}".format(name, str(exc)))
|
||||||
|
return True
|
||||||
|
|
||||||
def delete_vm_storage(self, vhd_uris):
|
def delete_vm_storage(self, vhd_uris):
|
||||||
# FUTURE: figure out a cloud_env indepdendent way to delete these
|
# FUTURE: figure out a cloud_env indepdendent way to delete these
|
||||||
|
@ -1625,6 +1664,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
blob_client.delete_blob(container_name, blob_name)
|
blob_client.delete_blob(container_name, blob_name)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self.fail("Error deleting blob {0}:{1} - {2}".format(container_name, blob_name, str(exc)))
|
self.fail("Error deleting blob {0}:{1} - {2}".format(container_name, blob_name, str(exc)))
|
||||||
|
return True
|
||||||
|
|
||||||
def get_marketplace_image_version(self):
|
def get_marketplace_image_version(self):
|
||||||
try:
|
try:
|
||||||
|
@ -1681,11 +1721,13 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self.fail("Error fetching storage account {0} - {1}".format(name, str(exc)))
|
self.fail("Error fetching storage account {0} - {1}".format(name, str(exc)))
|
||||||
|
|
||||||
def create_or_update_vm(self, params):
|
def create_or_update_vm(self, params, remove_autocreated_on_failure):
|
||||||
try:
|
try:
|
||||||
poller = self.compute_client.virtual_machines.create_or_update(self.resource_group, self.name, params)
|
poller = self.compute_client.virtual_machines.create_or_update(self.resource_group, self.name, params)
|
||||||
self.get_poller_result(poller)
|
self.get_poller_result(poller)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
if remove_autocreated_on_failure:
|
||||||
|
self.remove_autocreated_resources(params.tags)
|
||||||
self.fail("Error creating or updating virtual machine {0} - {1}".format(self.name, str(exc)))
|
self.fail("Error creating or updating virtual machine {0} - {1}".format(self.name, str(exc)))
|
||||||
|
|
||||||
def vm_size_is_valid(self):
|
def vm_size_is_valid(self):
|
||||||
|
@ -1712,6 +1754,8 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
'''
|
'''
|
||||||
account = None
|
account = None
|
||||||
valid_name = False
|
valid_name = False
|
||||||
|
if self.tags is None:
|
||||||
|
self.tags = {}
|
||||||
|
|
||||||
# Attempt to find a valid storage account name
|
# Attempt to find a valid storage account name
|
||||||
storage_account_name_base = re.sub('[^a-zA-Z0-9]', '', self.name[:20].lower())
|
storage_account_name_base = re.sub('[^a-zA-Z0-9]', '', self.name[:20].lower())
|
||||||
|
@ -1746,6 +1790,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
self.get_poller_result(poller)
|
self.get_poller_result(poller)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self.fail("Failed to create storage account: {0} - {1}".format(storage_account_name, str(exc)))
|
self.fail("Failed to create storage account: {0} - {1}".format(storage_account_name, str(exc)))
|
||||||
|
self.tags['_own_sa_'] = storage_account_name
|
||||||
return self.get_storage_account(storage_account_name)
|
return self.get_storage_account(storage_account_name)
|
||||||
|
|
||||||
def check_storage_account_name(self, name):
|
def check_storage_account_name(self, name):
|
||||||
|
@ -1769,6 +1814,8 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
|
|
||||||
network_interface_name = self.name + '01'
|
network_interface_name = self.name + '01'
|
||||||
nic = None
|
nic = None
|
||||||
|
if self.tags is None:
|
||||||
|
self.tags = {}
|
||||||
|
|
||||||
self.log("Create default NIC {0}".format(network_interface_name))
|
self.log("Create default NIC {0}".format(network_interface_name))
|
||||||
self.log("Check to see if NIC {0} exists".format(network_interface_name))
|
self.log("Check to see if NIC {0} exists".format(network_interface_name))
|
||||||
|
@ -1849,10 +1896,12 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
sku = self.network_models.PublicIPAddressSku(name="Standard") if self.zones else None
|
sku = self.network_models.PublicIPAddressSku(name="Standard") if self.zones else None
|
||||||
pip_info = self.create_default_pip(self.resource_group, self.location, self.name + '01', self.public_ip_allocation_method, sku=sku)
|
pip_info = self.create_default_pip(self.resource_group, self.location, self.name + '01', self.public_ip_allocation_method, sku=sku)
|
||||||
pip = self.network_models.PublicIPAddress(id=pip_info.id, location=pip_info.location, resource_guid=pip_info.resource_guid, sku=sku)
|
pip = self.network_models.PublicIPAddress(id=pip_info.id, location=pip_info.location, resource_guid=pip_info.resource_guid, sku=sku)
|
||||||
|
self.tags['_own_pip_'] = self.name + '01'
|
||||||
|
|
||||||
self.results['actions'].append('Created default security group {0}'.format(self.name + '01'))
|
self.results['actions'].append('Created default security group {0}'.format(self.name + '01'))
|
||||||
group = self.create_default_securitygroup(self.resource_group, self.location, self.name + '01', self.os_type,
|
group = self.create_default_securitygroup(self.resource_group, self.location, self.name + '01', self.os_type,
|
||||||
self.open_ports)
|
self.open_ports)
|
||||||
|
self.tags['_own_nsg_'] = self.name + '01'
|
||||||
|
|
||||||
parameters = self.network_models.NetworkInterface(
|
parameters = self.network_models.NetworkInterface(
|
||||||
location=self.location,
|
location=self.location,
|
||||||
|
@ -1877,6 +1926,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
network_interface_name,
|
network_interface_name,
|
||||||
parameters)
|
parameters)
|
||||||
new_nic = self.get_poller_result(poller)
|
new_nic = self.get_poller_result(poller)
|
||||||
|
self.tags['_own_nic_'] = network_interface_name
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self.fail("Error creating network interface {0} - {1}".format(network_interface_name, str(exc)))
|
self.fail("Error creating network interface {0} - {1}".format(network_interface_name, str(exc)))
|
||||||
return new_nic
|
return new_nic
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
storage_account: "{{ resource_group | hash('md5') | truncate(24, True, '') }}"
|
storage_account: "{{ resource_group | hash('md5') | truncate(24, True, '') }}"
|
||||||
vm_name1: "vm1{{ resource_group | hash('md5') | truncate(5, True, '') }}"
|
vm_name1: "vm1{{ resource_group | hash('md5') | truncate(5, True, '') }}"
|
||||||
vm_name2: "vm2{{ resource_group | hash('md5') | truncate(5, True, '') }}"
|
vm_name2: "vm2{{ resource_group | hash('md5') | truncate(5, True, '') }}"
|
||||||
|
vm_name3: "vm3{{ resource_group | hash('md5') | truncate(5, True, '') }}"
|
||||||
abs_name1: "avbs1{{ resource_group | hash('md5') | truncate(3, True, '') }}"
|
abs_name1: "avbs1{{ resource_group | hash('md5') | truncate(3, True, '') }}"
|
||||||
abs_name2: "avbs2{{ resource_group | hash('md5') | truncate(3, True, '') }}"
|
abs_name2: "avbs2{{ resource_group | hash('md5') | truncate(3, True, '') }}"
|
||||||
|
|
||||||
|
@ -500,3 +501,49 @@
|
||||||
|
|
||||||
#- assert:
|
#- assert:
|
||||||
#that: not output.changed
|
#that: not output.changed
|
||||||
|
|
||||||
|
- name: Create minimal VM with defaults
|
||||||
|
azure_rm_virtualmachine:
|
||||||
|
resource_group: "{{ resource_group }}"
|
||||||
|
name: "{{ vm_name3 }}"
|
||||||
|
admin_username: "testuser"
|
||||||
|
admin_password: "Pass123$$$abx!"
|
||||||
|
vm_size: Standard_B1ms
|
||||||
|
image:
|
||||||
|
offer: UbuntuServer
|
||||||
|
publisher: Canonical
|
||||||
|
sku: 16.04-LTS
|
||||||
|
version: latest
|
||||||
|
register: vm_output
|
||||||
|
|
||||||
|
- name: Delete VM
|
||||||
|
azure_rm_virtualmachine:
|
||||||
|
resource_group: "{{ resource_group }}"
|
||||||
|
name: "{{ vm_name3 }}"
|
||||||
|
remove_on_absent: all_autocreated
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Query NIC
|
||||||
|
azure_rm_networkinterface_facts:
|
||||||
|
resource_group: "{{ resource_group }}"
|
||||||
|
name: "{{ vm_name3 }}01"
|
||||||
|
register: output_nic
|
||||||
|
|
||||||
|
- name: Query NSG
|
||||||
|
azure_rm_securitygroup_facts:
|
||||||
|
resource_group: "{{ resource_group }}"
|
||||||
|
name: "{{ vm_name3 }}01"
|
||||||
|
register: output_nsg
|
||||||
|
|
||||||
|
- name: Query PIP
|
||||||
|
azure_rm_publicipaddress_facts:
|
||||||
|
resource_group: "{{ resource_group }}"
|
||||||
|
name: "{{ vm_name3 }}01"
|
||||||
|
register: output_pip
|
||||||
|
|
||||||
|
- name: Assert that autocreated resources were deleted
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- output_nic.ansible_facts.azure_networkinterfaces | length == 0
|
||||||
|
- output_nsg.ansible_facts.azure_securitygroups | length == 0
|
||||||
|
- output_pip.ansible_facts.azure_publicipaddresses | length == 0
|
||||||
|
|
Loading…
Reference in a new issue