diff --git a/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py b/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py index f12f3fbeb09..266033df7a8 100644 --- a/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py +++ b/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py @@ -2,6 +2,7 @@ # # Copyright (c) 2016 Matt Davis, # Chris Houseknecht, +# Copyright (c) 2018 James E. King, III (@jeking3) # # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -277,8 +278,8 @@ options: remove_on_absent: description: - "When removing a VM using state 'absent', also remove associated resources." - - "It can be 'all' or 'all_autocreated' or a list with any of the following: ['network_interfaces', 'virtual_storage', 'public_ips']." - - "To remove all resources referred by VM use 'all'." + - "It can be a list with any of the following: ['all', 'all_autocreated', 'network_interfaces', 'virtual_storage', 'public_ips']." + - "To remove all resources referred by VM use 'all' (this includes autocreated)." - "To remove all resources that were automatically created while provisioning VM use 'all_autocreated'." - Any other input will be ignored. default: ['all'] @@ -352,6 +353,26 @@ options: description: - Specifies the certificate store on the Virtual Machine to which the certificate should be added. The specified certificate store is implicitly in the LocalMachine account. + boot_diagnostics: + description: + - Manage boot diagnostics settings for a virtual machine. Boot diagnostics + includes a serial console and remote console screenshots. + version_added: '2.9' + suboptions: + enabled: + description: + - Flag indicating if boot diagnostics is enabled. + required: true + type: bool + storage_account: + description: + - The name of an existing storage account to use for boot diagnostics. + - If omitted and C(storage_account_name) is defined one level up, that + will be used instead. + - If omitted and C(storage_account_name) is not defined one level up, and + C(enabled) is I(true), then a default storage account will be created + or used for the virtual machine to hold the boot diagnostics data. + required: false extends_documentation_fragment: - azure @@ -361,7 +382,7 @@ author: - "Chris Houseknecht (@chouseknecht)" - "Matt Davis (@nitzmahone)" - "Christopher Perrin (@cperrin88)" - + - "James E. King III (@jeking3)" ''' EXAMPLES = ''' @@ -452,6 +473,8 @@ EXAMPLES = ''' network_interfaces: testvm001 storage_container: osdisk storage_blob: osdisk.vhd + boot_diagnostics: + enabled: yes image: offer: CoreOS publisher: CoreOS @@ -798,7 +821,8 @@ class AzureRMVirtualMachine(AzureRMModuleBase): accept_terms=dict(type='bool', default=False), license_type=dict(type='str', choices=['Windows_Server', 'Windows_Client']), vm_identity=dict(type='str', choices=['SystemAssigned']), - winrm=dict(type='list') + winrm=dict(type='list'), + boot_diagnostics=dict(type='dict'), ) self.resource_group = None @@ -842,6 +866,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase): self.zones = None self.license_type = None self.vm_identity = None + self.boot_diagnostics = None self.results = dict( changed=False, @@ -853,6 +878,45 @@ class AzureRMVirtualMachine(AzureRMModuleBase): super(AzureRMVirtualMachine, self).__init__(derived_arg_spec=self.module_arg_spec, supports_check_mode=True) + @property + def boot_diagnostics_present(self): + return self.boot_diagnostics is not None and 'enabled' in self.boot_diagnostics + + def get_boot_diagnostics_storage_account(self, limited=False, vm_dict=None): + """ + Get the boot diagnostics storage account. + + Arguments: + - limited - if true, limit the logic to the boot_diagnostics storage account + this is used if initial creation of the VM has a stanza with + boot_diagnostics disabled, so we only create a storage account + if the user specifies a storage account name inside the boot_diagnostics + schema + - vm_dict - if invoked on an update, this is the current state of the vm including + tags, like the default storage group tag '_own_sa_'. + + Normal behavior: + - try the self.boot_diagnostics.storage_account field + - if not there, try the self.storage_account_name field + - if not there, use the default storage account + + If limited is True: + - try the self.boot_diagnostics.storage_account field + - if not there, None + """ + bsa = None + if 'storage_account' in self.boot_diagnostics: + bsa = self.get_storage_account(self.boot_diagnostics['storage_account']) + elif limited: + return None + elif self.storage_account_name: + bsa = self.get_storage_account(self.storage_account_name) + else: + bsa = self.create_default_storage_account(vm_dict=vm_dict) + self.log("boot diagnostics storage account:") + self.log(self.serialize_obj(bsa, 'StorageAccount'), pretty_print=True) + return bsa + def exec_module(self, **kwargs): for key in list(self.module_arg_spec.keys()) + ['tags']: @@ -869,6 +933,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase): results = dict() vm = None network_interfaces = [] + requested_storage_uri = None requested_vhd_uri = None data_disk_requested_vhd_uri = None disable_ssh_password = None @@ -943,7 +1008,8 @@ class AzureRMVirtualMachine(AzureRMModuleBase): if self.storage_account_name and not self.managed_disk_type: properties = self.get_storage_account(self.storage_account_name) - requested_vhd_uri = '{0}{1}/{2}'.format(properties.primary_endpoints.blob, + requested_storage_uri = properties.primary_endpoints.blob + requested_vhd_uri = '{0}{1}/{2}'.format(requested_storage_uri, self.storage_container_name, self.storage_blob_name) @@ -1044,6 +1110,46 @@ class AzureRMVirtualMachine(AzureRMModuleBase): if self.license_type is not None and vm_dict['properties'].get('licenseType') != self.license_type: differences.append('License Type') changed = True + + # Defaults for boot diagnostics + if 'diagnosticsProfile' not in vm_dict['properties']: + vm_dict['properties']['diagnosticsProfile'] = {} + if 'bootDiagnostics' not in vm_dict['properties']['diagnosticsProfile']: + vm_dict['properties']['diagnosticsProfile']['bootDiagnostics'] = { + 'enabled': False, + 'storageUri': None + } + if self.boot_diagnostics_present: + current_boot_diagnostics = vm_dict['properties']['diagnosticsProfile']['bootDiagnostics'] + boot_diagnostics_changed = False + + if self.boot_diagnostics['enabled'] != current_boot_diagnostics['enabled']: + current_boot_diagnostics['enabled'] = self.boot_diagnostics['enabled'] + boot_diagnostics_changed = True + + boot_diagnostics_storage_account = self.get_boot_diagnostics_storage_account( + limited=not self.boot_diagnostics['enabled'], vm_dict=vm_dict) + boot_diagnostics_blob = boot_diagnostics_storage_account.primary_endpoints.blob if boot_diagnostics_storage_account else None + if current_boot_diagnostics['storageUri'] != boot_diagnostics_blob: + current_boot_diagnostics['storageUri'] = boot_diagnostics_blob + boot_diagnostics_changed = True + + if boot_diagnostics_changed: + differences.append('Boot Diagnostics') + changed = True + + # Adding boot diagnostics can create a default storage account after initial creation + # this means we might also need to update the _own_sa_ tag + own_sa = (self.tags or {}).get('_own_sa_', None) + cur_sa = vm_dict.get('tags', {}).get('_own_sa_', None) + if own_sa and own_sa != cur_sa: + if 'Tags' not in differences: + differences.append('Tags') + if 'tags' not in vm_dict: + vm_dict['tags'] = {} + vm_dict['tags']['_own_sa_'] = own_sa + changed = True + self.differences = differences elif self.state == 'absent': @@ -1066,7 +1172,6 @@ class AzureRMVirtualMachine(AzureRMModuleBase): if changed: if self.state == 'present': - default_storage_account = None if not vm: # Create the VM self.log("Create virtual machine {0}".format(self.name)) @@ -1103,14 +1208,15 @@ class AzureRMVirtualMachine(AzureRMModuleBase): # os disk if not self.storage_account_name and not self.managed_disk_type: storage_account = self.create_default_storage_account() - self.log("storage account:") + self.log("os disk storage account:") self.log(self.serialize_obj(storage_account, 'StorageAccount'), pretty_print=True) - requested_vhd_uri = 'https://{0}.blob.{1}/{2}/{3}'.format( + requested_storage_uri = 'https://{0}.blob.{1}/'.format( storage_account.name, - self._cloud_environment.suffixes.storage_endpoint, + self._cloud_environment.suffixes.storage_endpoint) + requested_vhd_uri = '{0}{1}/{2}'.format( + requested_storage_uri, self.storage_container_name, self.storage_blob_name) - default_storage_account = storage_account # store for use by data disks if necessary if not self.short_hostname: self.short_hostname = self.name @@ -1135,7 +1241,9 @@ class AzureRMVirtualMachine(AzureRMModuleBase): publisher=self.plan.get('publisher'), promotion_code=self.plan.get('promotion_code')) - license_type = self.license_type + # do this before creating vm_resource as it can modify tags + if self.boot_diagnostics_present and self.boot_diagnostics['enabled']: + boot_diag_storage_account = self.get_boot_diagnostics_storage_account() vm_resource = self.compute_models.VirtualMachine( location=self.location, @@ -1206,6 +1314,12 @@ class AzureRMVirtualMachine(AzureRMModuleBase): elif not vm_resource.os_profile.windows_configuration.win_rm: vm_resource.os_profile.windows_configuration.win_rm = winrm + if self.boot_diagnostics_present: + vm_resource.diagnostics_profile = self.compute_models.DiagnosticsProfile( + boot_diagnostics=self.compute_models.BootDiagnostics( + enabled=self.boot_diagnostics['enabled'], + storage_uri=boot_diag_storage_account.primary_endpoints.blob)) + if self.admin_password: vm_resource.os_profile.admin_password = self.admin_password @@ -1237,13 +1351,9 @@ class AzureRMVirtualMachine(AzureRMModuleBase): if data_disk.get('storage_account_name'): data_disk_storage_account = self.get_storage_account(data_disk['storage_account_name']) else: - if(not default_storage_account): - data_disk_storage_account = self.create_default_storage_account() - self.log("data disk storage account:") - self.log(self.serialize_obj(data_disk_storage_account, 'StorageAccount'), pretty_print=True) - default_storage_account = data_disk_storage_account # store for use by future data disks if necessary - else: - data_disk_storage_account = default_storage_account + data_disk_storage_account = self.create_default_storage_account() + self.log("data disk storage account:") + self.log(self.serialize_obj(data_disk_storage_account, 'StorageAccount'), pretty_print=True) if not data_disk.get('storage_container_name'): data_disk['storage_container_name'] = 'vhds' @@ -1292,7 +1402,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase): term = self.marketplace_client.marketplace_agreements.get( publisher_id=plan_publisher, offer_id=plan_product, plan_id=plan_name) term.accepted = True - agreement = self.marketplace_client.marketplace_agreements.create( + self.marketplace_client.marketplace_agreements.create( publisher_id=plan_publisher, offer_id=plan_product, plan_id=plan_name, parameters=term) except Exception as exc: self.fail(("Error accepting terms for virtual machine {0} with plan {1}. " + @@ -1353,9 +1463,6 @@ class AzureRMVirtualMachine(AzureRMModuleBase): ) else: os_profile = None - license_type = None - if self.license_type is None: - license_type = "None" vm_resource = self.compute_models.VirtualMachine( location=vm_dict['location'], @@ -1384,6 +1491,12 @@ class AzureRMVirtualMachine(AzureRMModuleBase): if self.license_type is not None: vm_resource.license_type = self.license_type + if self.boot_diagnostics is not None: + vm_resource.diagnostics_profile = self.compute_models.DiagnosticsProfile( + boot_diagnostics=self.compute_models.BootDiagnostics( + enabled=vm_dict['properties']['diagnosticsProfile']['bootDiagnostics']['enabled'], + storage_uri=vm_dict['properties']['diagnosticsProfile']['bootDiagnostics']['storageUri'])) + if vm_dict.get('tags'): vm_resource.tags = vm_dict['tags'] @@ -1661,26 +1774,26 @@ class AzureRMVirtualMachine(AzureRMModuleBase): except Exception as exc: self.fail("Error deleting virtual machine {0} - {1}".format(self.name, str(exc))) - if 'all_autocreated' in self.remove_on_absent: + if 'all' in self.remove_on_absent or '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: best-effort to keep deleting other linked resources if we encounter an error - if self.remove_on_absent.intersection(set(['all', 'virtual_storage'])): - self.log('Deleting VHDs') - self.delete_vm_storage(vhd_uris) - self.log('Deleting managed disks') - self.delete_managed_disks(managed_disk_ids) - if self.remove_on_absent.intersection(set(['all', 'network_interfaces'])): - self.log('Deleting network interfaces') - for nic_dict in nic_names: - self.delete_nic(nic_dict['resource_group'], nic_dict['name']) + # 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 + if self.remove_on_absent.intersection(set(['all', 'virtual_storage'])): + self.log('Deleting VHDs') + self.delete_vm_storage(vhd_uris) + self.log('Deleting managed disks') + self.delete_managed_disks(managed_disk_ids) + + if self.remove_on_absent.intersection(set(['all', 'network_interfaces'])): + self.log('Deleting network interfaces') + for nic_dict in nic_names: + self.delete_nic(nic_dict['resource_group'], nic_dict['name']) - if self.remove_on_absent.intersection(set(['all', 'public_ips'])): - self.log('Deleting public IPs') - for pip_dict in pip_names: - self.delete_pip(pip_dict['resource_group'], pip_dict['name']) + if self.remove_on_absent.intersection(set(['all', 'public_ips'])): + self.log('Deleting public IPs') + for pip_dict in pip_names: + self.delete_pip(pip_dict['resource_group'], pip_dict['name']) return True @@ -1841,10 +1954,15 @@ class AzureRMVirtualMachine(AzureRMModuleBase): return True return False - def create_default_storage_account(self): + def create_default_storage_account(self, vm_dict=None): ''' - Create a default storage account XXXX, where XXXX is a random number. If XXXX exists, use it. - Otherwise, create one. + Create (once) a default storage account XXXX, where XXXX is a random number. + NOTE: If XXXX exists, use it instead of failing. Highly unlikely. + If this method is called multiple times across executions it will return the same + storage account created with the random name which is stored in a tag on the VM. + + vm_dict is passed in during an update, so we can obtain the _own_sa_ tag and return + the default storage account we created in a previous invocation :return: storage account object ''' @@ -1853,6 +1971,15 @@ class AzureRMVirtualMachine(AzureRMModuleBase): if self.tags is None: self.tags = {} + if self.tags.get('_own_sa_', None): + # We previously created one in the same invocation + return self.get_storage_account(self.tags['_own_sa_']) + + if vm_dict and vm_dict.get('tags', {}).get('_own_sa_', None): + # We previously created one in a previous invocation + # We must be updating, like adding boot diagnostics + return self.get_storage_account(vm_dict['tags']['_own_sa_']) + # Attempt to find a valid storage account name storage_account_name_base = re.sub('[^a-zA-Z0-9]', '', self.name[:20].lower()) for i in range(0, 5): diff --git a/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine_facts.py b/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine_facts.py index 56237db57df..02ccb13cfc4 100644 --- a/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine_facts.py +++ b/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine_facts.py @@ -24,7 +24,7 @@ version_added: "2.7" short_description: Get virtual machine facts. description: - - Get facts for all virtual machines of a resource group. + - Get facts for one or all virtual machines in a resource group. options: resource_group: @@ -76,6 +76,32 @@ vms: returned: always type: str sample: admin + boot_diagnostics: + description: + - Information about the boot diagnostics settings. + returned: always + type: complex + contains: + enabled: + description: + - Indicates if boot diagnostics are enabled. + type: bool + sample: true + storage_uri: + description: + - Indicates the storage account used by boot diagnostics. + type: str + sample: https://mystorageaccountname.blob.core.windows.net/ + console_screenshot_uri: + description: + - Contains a URI to grab a console screenshot. + - Only present if enabled. + type: str + serial_console_log_uri: + description: + - Contains a URI to grab the serial console log. + - Only present if enabled. + type: str data_disks: description: - List of attached data disks. @@ -206,8 +232,7 @@ except Exception: # This is handled in azure_rm_common pass -from ansible.module_utils.azure_rm_common import AzureRMModuleBase, azure_id_to_dict -from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict +from ansible.module_utils.azure_rm_common import AzureRMModuleBase from ansible.module_utils.six.moves.urllib.parse import urlparse import re @@ -358,6 +383,18 @@ class AzureRMVirtualMachineFacts(AzureRMModuleBase): 'id': image.get('id', None) } + new_result['boot_diagnostics'] = { + 'enabled': 'diagnosticsProfile' in result['properties'] and + 'bootDiagnostics' in result['properties']['diagnosticsProfile'] and + result['properties']['diagnosticsProfile']['bootDiagnostics']['enabled'] or False, + 'storage_uri': 'diagnosticsProfile' in result['properties'] and + 'bootDiagnostics' in result['properties']['diagnosticsProfile'] and + result['properties']['diagnosticsProfile']['bootDiagnostics']['storageUri'] or None + } + if new_result['boot_diagnostics']['enabled']: + new_result['boot_diagnostics']['console_screenshot_uri'] = result['properties']['instanceView']['bootDiagnostics']['consoleScreenshotBlobUri'] + new_result['boot_diagnostics']['serial_console_log_uri'] = result['properties']['instanceView']['bootDiagnostics']['serialConsoleLogBlobUri'] + vhd = result['properties']['storageProfile']['osDisk'].get('vhd') if vhd is not None: url = urlparse(vhd['uri']) diff --git a/test/integration/targets/azure_rm_virtualmachine/tasks/main.yml b/test/integration/targets/azure_rm_virtualmachine/tasks/main.yml index 2c7edd10847..e549ec3ac0a 100644 --- a/test/integration/targets/azure_rm_virtualmachine/tasks/main.yml +++ b/test/integration/targets/azure_rm_virtualmachine/tasks/main.yml @@ -1 +1,3 @@ +- include: setup.yml - include: virtualmachine.yml +- include: teardown.yml diff --git a/test/integration/targets/azure_rm_virtualmachine/tasks/setup.yml b/test/integration/targets/azure_rm_virtualmachine/tasks/setup.yml new file mode 100644 index 00000000000..493f0a14ca3 --- /dev/null +++ b/test/integration/targets/azure_rm_virtualmachine/tasks/setup.yml @@ -0,0 +1,74 @@ +- name: Create random names + set_fact: + storage_account: "{{ resource_group | hash('md5') | truncate(24, True, '') }}" + storage_account2: "{{ resource_group | hash('md5') | truncate(18, True, '') }}" + vm_name1: "vm1{{ 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, '') }}" + vm_name4: "vm4{{ resource_group | hash('md5') | truncate(5, True, '') }}" + abs_name1: "avbs1{{ resource_group | hash('md5') | truncate(3, True, '') }}" + abs_name2: "avbs2{{ resource_group | hash('md5') | truncate(3, True, '') }}" + +- name: Create storage account + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account }}" + account_type: Standard_LRS + +- name: Create 2nd storage account + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account2 }}" + account_type: Standard_LRS + +- name: Create an availability set + azure_rm_availabilityset: + name: "{{ abs_name1 }}" + resource_group: "{{ resource_group }}" + +- name: Create virtual network + azure_rm_virtualnetwork: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + address_prefixes: "10.10.0.0/16" + +- name: Add subnet + azure_rm_subnet: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + address_prefix: "10.10.0.0/24" + virtual_network: "{{ vm_name1 }}" + +- name: Create public ip + azure_rm_publicipaddress: + resource_group: "{{ resource_group }}" + allocation_method: Static + name: "{{ vm_name1 }}" + +- name: Create security group + azure_rm_securitygroup: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + purge_rules: yes + rules: + - name: ALLOW_SSH + protocol: Tcp + destination_port_range: 22 + access: Allow + priority: 100 + direction: Inbound + - name: ALLOW_HTTP + protocol: Tcp + destination_port_range: 80 + access: Allow + priority: 110 + direction: Inbound + +- name: Create NIC for single nic VM + azure_rm_networkinterface: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + virtual_network: "{{ vm_name1 }}" + subnet: "{{ vm_name1 }}" + public_ip_name: "{{ vm_name1 }}" + security_group: "{{ vm_name1 }}" diff --git a/test/integration/targets/azure_rm_virtualmachine/tasks/teardown.yml b/test/integration/targets/azure_rm_virtualmachine/tasks/teardown.yml new file mode 100644 index 00000000000..08b1cacc65d --- /dev/null +++ b/test/integration/targets/azure_rm_virtualmachine/tasks/teardown.yml @@ -0,0 +1,62 @@ +- name: Destroy NIC for single nic VM + azure_rm_networkinterface: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + state: absent + +- name: Destroy 2nd security group + azure_rm_securitygroup: + resource_group: "{{ resource_group }}" + name: "{{ vm_name2 }}" + state: absent + +- name: Destroy security group + azure_rm_securitygroup: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + state: absent + +- name: Destroy subnet + azure_rm_subnet: + resource_group: "{{ resource_group }}" + virtual_network: "{{ vm_name1 }}" + name: "{{ vm_name1 }}" + state: absent + +- name: Destroy virtual network + azure_rm_virtualnetwork: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + state: absent + +- name: Destroy public ip + azure_rm_publicipaddress: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + state: absent + +- name: Destroy 2nd availability set + azure_rm_availabilityset: + resource_group: "{{ resource_group }}" + name: "{{ abs_name2 }}" + state: absent + +- name: Destroy an availability set + azure_rm_availabilityset: + resource_group: "{{ resource_group }}" + name: "{{ abs_name1 }}" + state: absent + +- name: Destroy 2nd storage account + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account2 }}" + force_delete_nonempty: true + state: absent + +- name: Destroy storage account + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account }}" + force_delete_nonempty: true + state: absent diff --git a/test/integration/targets/azure_rm_virtualmachine/tasks/virtualmachine.yml b/test/integration/targets/azure_rm_virtualmachine/tasks/virtualmachine.yml index 56d361ba5fe..9ee36ca869e 100644 --- a/test/integration/targets/azure_rm_virtualmachine/tasks/virtualmachine.yml +++ b/test/integration/targets/azure_rm_virtualmachine/tasks/virtualmachine.yml @@ -1,71 +1,4 @@ -- name: Create random names - set_fact: - storage_account: "{{ resource_group | hash('md5') | truncate(24, True, '') }}" - vm_name1: "vm1{{ 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_name2: "avbs2{{ resource_group | hash('md5') | truncate(3, True, '') }}" - -- name: Create storage account - azure_rm_storageaccount: - resource_group: "{{ resource_group }}" - name: "{{ storage_account }}" - account_type: Standard_LRS - -- name: Create an availability set - azure_rm_availabilityset: - name: "{{ abs_name1 }}" - resource_group: "{{ resource_group }}" - -- name: Create virtual network - azure_rm_virtualnetwork: - resource_group: "{{ resource_group }}" - name: "{{ vm_name1 }}" - address_prefixes: "10.10.0.0/16" - -- name: Add subnet - azure_rm_subnet: - resource_group: "{{ resource_group }}" - name: "{{ vm_name1 }}" - address_prefix: "10.10.0.0/24" - virtual_network: "{{ vm_name1 }}" - -- name: Create public ip - azure_rm_publicipaddress: - resource_group: "{{ resource_group }}" - allocation_method: Static - name: "{{ vm_name1 }}" - -- name: Create security group - azure_rm_securitygroup: - resource_group: "{{ resource_group }}" - name: "{{ vm_name1 }}" - purge_rules: yes - rules: - - name: ALLOW_SSH - protocol: Tcp - destination_port_range: 22 - access: Allow - priority: 100 - direction: Inbound - - name: ALLOW_HTTP - protocol: Tcp - destination_port_range: 80 - access: Allow - priority: 110 - direction: Inbound - -- name: Create NIC for single nic VM - azure_rm_networkinterface: - resource_group: "{{ resource_group }}" - name: "{{ vm_name1 }}" - virtual_network: "{{ vm_name1 }}" - subnet: "{{ vm_name1 }}" - public_ip_name: "{{ vm_name1 }}" - security_group: "{{ vm_name1 }}" - -- name: Create virtual machine with a single NIC +- name: Create virtual machine with a single NIC and no boot diagnostics register: output azure_rm_virtualmachine: resource_group: "{{ resource_group }}" @@ -91,7 +24,92 @@ - assert: that: + - azure_vm.properties.provisioningState == 'Succeeded' - azure_vm.properties.availabilitySet.id + # initial response from creation has no diagnosticsProfile + # if you run it again however, there is one in the response + # so we handle both cases + - "'diagnosticsProfile' not in azure_vm.properties or not azure_vm.properties.diagnosticsProfile.bootDiagnostics.enabled" + +- name: Get facts for virtual machine without boot diagnostics disabled + azure_rm_virtualmachine_facts: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + register: output + +- assert: + that: + - output.vms != [] + - not output.vms[0].boot_diagnostics.enabled + - not output.vms[0].boot_diagnostics.storage_uri + +- name: Enable boot diagnostics on an existing VM for the first time without specifying a storage account + azure_rm_virtualmachine: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + boot_diagnostics: + enabled: true + # without specifying storage_account you get a new default storage account for the VM + register: output + +- assert: + that: + - azure_vm.properties.diagnosticsProfile.bootDiagnostics.enabled + - azure_vm.properties.diagnosticsProfile.bootDiagnostics.storageUri is defined + - azure_vm.properties.instanceView.bootDiagnostics.consoleScreenshotBlobUri is defined + - azure_vm.properties.instanceView.bootDiagnostics.serialConsoleLogBlobUri is defined + +- name: Get facts for virtual machine with boot diagnostics enabled + azure_rm_virtualmachine_facts: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + register: output + +- assert: + that: + - output.vms != [] + - output.vms[0].boot_diagnostics.enabled + - output.vms[0].boot_diagnostics.storage_uri is defined + - output.vms[0].boot_diagnostics.console_screenshot_uri is defined + - output.vms[0].boot_diagnostics.serial_console_log_uri is defined + +- name: Change the boot diagnostics storage account while enabled + azure_rm_virtualmachine: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + boot_diagnostics: + enabled: true + storage_account: "{{ storage_account2 }}" + ignore_errors: true + register: output + +- name: Disable boot diagnostics and change the storage account at the same time + azure_rm_virtualmachine: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + boot_diagnostics: + enabled: false + storage_account: "{{ storage_account }}" + register: output + +- assert: + that: + - not azure_vm.properties.diagnosticsProfile.bootDiagnostics.enabled + +- name: Re-enable boot diagnostics on an existing VM where it was previously configured + azure_rm_virtualmachine: + resource_group: "{{ resource_group }}" + name: "{{ vm_name1 }}" + boot_diagnostics: + enabled: true + register: output + +- assert: + that: + - azure_vm.properties.diagnosticsProfile.bootDiagnostics.enabled + - azure_vm.properties.diagnosticsProfile.bootDiagnostics.storageUri is defined + - azure_vm.properties.instanceView.bootDiagnostics.consoleScreenshotBlobUri is defined + - azure_vm.properties.instanceView.bootDiagnostics.serialConsoleLogBlobUri is defined # - add_host: # name: new_azure_vm @@ -244,8 +262,7 @@ - assert: that: azure_publicipaddresses | length == 0 -- name: Create virtual machine without public ip address - register: output +- name: Create virtual machine without public ip address and with boot diagnostics enabled azure_rm_virtualmachine: resource_group: "{{ resource_group }}" name: testvmnoip @@ -256,14 +273,21 @@ os_type: Linux public_ip_allocation_method: Disabled availability_set: "{{ abs_name1 }}" + boot_diagnostics: + enabled: true image: offer: UbuntuServer publisher: Canonical sku: 16.04-LTS version: latest + register: output - assert: that: + - azure_vm.properties.diagnosticsProfile.bootDiagnostics.enabled + - azure_vm.properties.diagnosticsProfile.bootDiagnostics.storageUri is defined + - azure_vm.properties.instanceView.bootDiagnostics.consoleScreenshotBlobUri is defined + - azure_vm.properties.instanceView.bootDiagnostics.serialConsoleLogBlobUri is defined - not 'publicIPAddress' in output.ansible_facts.azure_vm.properties.networkProfile.networkInterfaces[0].properties.ipConfigurations[0].properties - name: Delete VM with no public ip @@ -271,6 +295,7 @@ resource_group: "{{ resource_group }}" name: testvmnoip state: absent + remove_on_absent: all_autocreated vm_size: Standard_A0 async: 5000 poll: 0 @@ -280,7 +305,7 @@ - name: testnic011 resource_group: "{{ resource_group_secondary }}" - name: testnic012 - resource_group: "{{ resource_group_secondary }}" + resource_group: "{{ resource_group_secondary }}" - name: Create an availability set azure_rm_availabilityset: @@ -307,11 +332,10 @@ name: "{{ item.name }}" virtual_network: "{{ vn.state.id }}" subnet: "{{ vm_name2 }}" - security_group: "{{ vm_name1 }}" + security_group: "{{ vm_name2 }}" loop: "{{ niclist }}" - name: Create virtual machine with two NICs - register: output azure_rm_virtualmachine: resource_group: "{{ resource_group }}" name: "{{ vm_name2 }}" @@ -334,6 +358,8 @@ version: latest tags: abc: def + register: output + - assert: that: - azure_vm.properties.availabilitySet.id @@ -350,7 +376,7 @@ that: - results.vms | length == 1 - results.vms[0].name == "{{ vm_name2 }}" - - results.vms[0].location == 'eastus' + - results.vms[0].location - results.vms[0].admin_username == 'adminuser' - results.vms[0].resource_group == "{{ resource_group }}" - results.vms[0].power_state != None @@ -548,6 +574,7 @@ - name: Assert that autocreated resources were deleted assert: that: + # what about the default storage group? - output_nic.ansible_facts.azure_networkinterfaces | length == 0 - output_nsg.ansible_facts.azure_securitygroups | length == 0 - output_pip.ansible_facts.azure_publicipaddresses | length == 0