From 0d2fddd0cad6c69038c0a0f77ca3b973abcfa123 Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Fri, 22 Apr 2022 18:11:22 -0500 Subject: [PATCH 01/14] remove unused storage imports --- plugins/module_utils/azure_rm_common.py | 1 - plugins/modules/azure_rm_storageaccount.py | 1 - 2 files changed, 2 deletions(-) diff --git a/plugins/module_utils/azure_rm_common.py b/plugins/module_utils/azure_rm_common.py index 9599417c2..dd52b38a2 100644 --- a/plugins/module_utils/azure_rm_common.py +++ b/plugins/module_utils/azure_rm_common.py @@ -246,7 +246,6 @@ def default_api_version(self): from azure.mgmt.containerservice import ContainerServiceClient from azure.mgmt.marketplaceordering import MarketplaceOrderingAgreements from azure.mgmt.trafficmanager import TrafficManagerManagementClient - from azure.storage.cloudstorageaccount import CloudStorageAccount from azure.storage.blob import PageBlobService, BlockBlobService from adal.authentication_context import AuthenticationContext from azure.mgmt.authorization import AuthorizationManagementClient diff --git a/plugins/modules/azure_rm_storageaccount.py b/plugins/modules/azure_rm_storageaccount.py index bdbd27570..0b6c5b67d 100644 --- a/plugins/modules/azure_rm_storageaccount.py +++ b/plugins/modules/azure_rm_storageaccount.py @@ -405,7 +405,6 @@ try: from azure.core.exceptions import ResourceNotFoundError - from azure.storage.cloudstorageaccount import CloudStorageAccount from azure.common import AzureMissingResourceHttpError except ImportError: # This is handled in azure_rm_common From dc672b2f37da5cf92029e611f715591b8179a75c Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Sat, 23 Apr 2022 17:08:12 -0500 Subject: [PATCH 02/14] correct test lints --- .../azure_rm_storageaccount/tasks/main.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml index fa3c22ed0..72c7c9481 100644 --- a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml +++ b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml @@ -9,8 +9,7 @@ name: "invalid_char$" account_type: Standard_LRS register: output - ignore_errors: yes - + ignore_errors: true - name: Check intentional name failure. assert: that: @@ -22,7 +21,7 @@ resource_group: "{{ resource_group }}" name: "{{ item }}" state: absent - force_delete_nonempty: True + force_delete_nonempty: true loop: - "{{ storage_account_name_default }}" - "{{ storage_account_name_explicit }}" @@ -65,7 +64,7 @@ access_tier: Hot account_type: Premium_LRS allow_blob_public_access: False - append_tags: no + append_tags: false blob_cors: - allowed_origins: - http://www.example.com/ @@ -114,7 +113,7 @@ access_tier: Hot account_type: Premium_LRS allow_blob_public_access: False - append_tags: no + append_tags: false blob_cors: - allowed_origins: - http://www.example.com/ @@ -200,7 +199,7 @@ resource_group: "{{ resource_group }}" name: "{{ storage_account_name_default }}" allow_blob_public_access: False - append_tags: no + append_tags: false blob_cors: - allowed_origins: - http://www.example.com/ @@ -249,7 +248,7 @@ name: "{{ storage_account_name_default }}" account_type: Premium_LRS register: output - ignore_errors: yes + ignore_errors: true - name: Assert account type change failed assert: @@ -263,8 +262,8 @@ name: "{{ storage_account_name_default }}" custom_domain: name: ansible.com - use_sub_domain: no - ignore_errors: yes + use_sub_domain: false + ignore_errors: true register: output - name: Assert CNAME failure @@ -288,7 +287,7 @@ azure_rm_storageaccount: resource_group: "{{ resource_group }}" name: "{{ storage_account_name_explicit }}" - append_tags: no + append_tags: false tags: testing: testing delete: never From 1791c897c14c5b06f161f3e5f0f9be89139898bc Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Sun, 24 Apr 2022 21:28:17 -0500 Subject: [PATCH 03/14] update azure-storage dependency --- plugins/module_utils/azure_rm_common.py | 30 +++--- plugins/modules/azure_rm_storageaccount.py | 4 +- plugins/modules/azure_rm_storageblob.py | 106 ++++++++++++--------- plugins/modules/azure_rm_virtualmachine.py | 4 +- requirements-azure.txt | 2 +- 5 files changed, 78 insertions(+), 68 deletions(-) diff --git a/plugins/module_utils/azure_rm_common.py b/plugins/module_utils/azure_rm_common.py index dd52b38a2..34ff8a935 100644 --- a/plugins/module_utils/azure_rm_common.py +++ b/plugins/module_utils/azure_rm_common.py @@ -246,7 +246,7 @@ def default_api_version(self): from azure.mgmt.containerservice import ContainerServiceClient from azure.mgmt.marketplaceordering import MarketplaceOrderingAgreements from azure.mgmt.trafficmanager import TrafficManagerManagementClient - from azure.storage.blob import PageBlobService, BlockBlobService + from azure.storage.blob import BlobServiceClient from adal.authentication_context import AuthenticationContext from azure.mgmt.authorization import AuthorizationManagementClient from azure.mgmt.sql import SqlManagementClient @@ -676,30 +676,22 @@ def check_provisioning_state(self, azure_object, requested_state='present'): self.fail("Error {0} has a provisioning state of {1}. Expecting state to be {2}.".format( azure_object.name, azure_object.provisioning_state, AZURE_SUCCESS_STATE)) - def get_blob_client(self, resource_group_name, storage_account_name, storage_blob_type='block'): - keys = dict() + def get_blob_service_client(self, resource_group_name, storage_account_name): try: # Get keys from the storage account - self.log('Getting keys') - account_keys = self.storage_client.storage_accounts.list_keys(resource_group_name, storage_account_name) + self.log("Getting account properties") + account = self.storage_client.storage_accounts.get_properties(resource_group_name=resource_group_name, account_name=storage_account_name) except Exception as exc: - self.fail("Error getting keys for account {0} - {1}".format(storage_account_name, str(exc))) + self.fail("Error getting storage account properties for {0}: {1}".format(storage_account_name, str(exc))) try: - self.log('Create blob service') - if storage_blob_type == 'page': - return PageBlobService(endpoint_suffix=self._cloud_environment.suffixes.storage_endpoint, - account_name=storage_account_name, - account_key=account_keys.keys[0].value) - elif storage_blob_type == 'block': - return BlockBlobService(endpoint_suffix=self._cloud_environment.suffixes.storage_endpoint, - account_name=storage_account_name, - account_key=account_keys.keys[0].value) - else: - raise Exception("Invalid storage blob type defined.") + self.log("Create blob service client") + return BlobServiceClient( + account_url=account.primary_endpoints.blob, + credential=self.azure_auth.azure_credential_track2, + ) except Exception as exc: - self.fail("Error creating blob service client for storage account {0} - {1}".format(storage_account_name, - str(exc))) + self.fail("Error creating blob service client for storage account {0} - {1}".format(storage_account_name, str(exc))) def create_default_pip(self, resource_group, location, public_ip_name, allocation_method='Dynamic', sku=None): ''' diff --git a/plugins/modules/azure_rm_storageaccount.py b/plugins/modules/azure_rm_storageaccount.py index 0b6c5b67d..bbfa05cba 100644 --- a/plugins/modules/azure_rm_storageaccount.py +++ b/plugins/modules/azure_rm_storageaccount.py @@ -867,14 +867,14 @@ def account_has_blob_containers(self): not be deleted. ''' self.log('Checking for existing blob containers') - blob_service = self.get_blob_client(self.resource_group, self.name) + blob_service = self.get_blob_service_client(self.resource_group, self.name) try: response = blob_service.list_containers() except Exception: # No blob storage available? return False - if len(response.items) > 0: + if len(list(response)) > 0: return True return False diff --git a/plugins/modules/azure_rm_storageblob.py b/plugins/modules/azure_rm_storageblob.py index 05f0d21d7..545e14447 100644 --- a/plugins/modules/azure_rm_storageblob.py +++ b/plugins/modules/azure_rm_storageblob.py @@ -190,8 +190,8 @@ import mimetypes try: - from azure.storage.blob.models import ContentSettings - from azure.common import AzureMissingResourceHttpError, AzureHttpError + from azure.storage.blob._models import BlobType, ContentSettings + from azure.core.exceptions import ResourceNotFoundError except ImportError: # This is handled in azure_rm_common pass @@ -226,7 +226,7 @@ def __init__(self): mutually_exclusive = [('src', 'dest'), ('src', 'batch_upload_src'), ('dest', 'batch_upload_src')] - self.blob_client = None + self.blob_service_client = None self.blob_details = None self.storage_account_name = None self.blob = None @@ -264,7 +264,7 @@ def exec_module(self, **kwargs): # add file path validation - self.blob_client = self.get_blob_client(self.resource_group, self.storage_account_name, self.blob_type) + self.blob_service_client = self.get_blob_service_client(self.resource_group, self.storage_account_name) self.container_obj = self.get_container() if self.state == 'present': @@ -373,28 +373,39 @@ def _guess_content_type(file_path, original): blob_path = _normalize_blob_file_path(self.batch_upload_dst, blob_path) if not self.check_mode: try: - self.blob_client.create_blob_from_path(self.container, blob_path, src, - metadata=self.tags, content_settings=_guess_content_type(src, content_settings)) - except AzureHttpError as exc: + client = self.blob_service_client.get_blob_client(container=self.container, blob=blob_path) + client.upload_blob(data=src, + blob_type=self.get_blob_type(self.blob_type), + metadata=self.tags, + content_settings=_guess_content_type(src, content_settings)) + except Exception as exc: self.fail("Error creating blob {0} - {1}".format(src, str(exc))) self.results['actions'].append('created blob from {0}'.format(src)) self.results['changed'] = True self.results['container'] = self.container_obj + def get_blob_type(self, blob_type): + if blob_type == "block": + return BlobType.BlockBlob + elif blob_type == "page": + return BlobType.PageBlob + else: + return BlobType.AppendBlob + def get_container(self): result = {} container = None if self.container: try: - container = self.blob_client.get_container_properties(self.container) - except AzureMissingResourceHttpError: + container = self.blob_service_client.get_container_client(container=self.container).get_container_properties() + except ResourceNotFoundError: pass if container: result = dict( - name=container.name, - tags=container.metadata, - last_modified=container.properties.last_modified.strftime('%d-%b-%Y %H:%M:%S %z'), + name=container["name"], + tags=container["metadata"], + last_modified=container["last_modified"].strftime('%d-%b-%Y %H:%M:%S %z'), ) return result @@ -403,23 +414,23 @@ def get_blob(self): blob = None if self.blob: try: - blob = self.blob_client.get_blob_properties(self.container, self.blob) - except AzureMissingResourceHttpError: + blob = self.blob_service_client.get_blob_client(container=self.container, blob=blob).get_blob_properties() + except ResourceNotFoundError: pass if blob: result = dict( - name=blob.name, - tags=blob.metadata, - last_modified=blob.properties.last_modified.strftime('%d-%b-%Y %H:%M:%S %z'), - type=blob.properties.blob_type, - content_length=blob.properties.content_length, + name=blob["name"], + tags=blob["metadata"], + last_modified=blob["last_modified"].strftime('%d-%b-%Y %H:%M:%S %z'), + type=blob["blob_type"], + content_length=blob["size"], content_settings=dict( - content_type=blob.properties.content_settings.content_type, - content_encoding=blob.properties.content_settings.content_encoding, - content_language=blob.properties.content_settings.content_language, - content_disposition=blob.properties.content_settings.content_disposition, - cache_control=blob.properties.content_settings.cache_control, - content_md5=blob.properties.content_settings.content_md5 + content_type=blob["content_settings"]["content_type"], + content_encoding=blob["content_settings"]["content_encoding"], + content_language=blob["content_settings"]["content_language"], + content_disposition=blob["content_settings"]["content_disposition"], + cache_control=blob["content_settings"]["cache_control"], + content_md5=blob["content_settings"]["content_md5"], ) ) return result @@ -434,8 +445,9 @@ def create_container(self): if not self.check_mode: try: - self.blob_client.create_container(self.container, metadata=tags, public_access=self.public_access) - except AzureHttpError as exc: + client = self.blob_service_client.get_container_client(container=self.container) + client.create_container(metadata=tags, public_access=self.public_access) + except Exception as exc: self.fail("Error creating container {0} - {1}".format(self.container, str(exc))) self.container_obj = self.get_container() self.results['changed'] = True @@ -456,9 +468,12 @@ def upload_blob(self): ) if not self.check_mode: try: - self.blob_client.create_blob_from_path(self.container, self.blob, self.src, - metadata=self.tags, content_settings=content_settings) - except AzureHttpError as exc: + client = self.blob_service_client.get_blob_client(container=self.container, blob=self.blob) + client.upload_blob(data=self.src, + blob_type=self.get_blob_type(self.blob_type), + metadata=self.tags, + content_settings=content_settings) + except Exception as exc: self.fail("Error creating blob {0} - {1}".format(self.blob, str(exc))) self.blob_obj = self.get_blob() @@ -470,7 +485,10 @@ def upload_blob(self): def download_blob(self): if not self.check_mode: try: - self.blob_client.get_blob_to_path(self.container, self.blob, self.dest) + client = self.blob_service_client.get_blob_client(container=self.container, blob=self.blob) + with open(self.dest, "wb") as blob_stream: + blob_data = client.download_blob() + blob_data.readinto(blob_stream) except Exception as exc: self.fail("Failed to download blob {0}:{1} to {2} - {3}".format(self.container, self.blob, @@ -527,8 +545,8 @@ def dest_is_valid(self): def delete_container(self): if not self.check_mode: try: - self.blob_client.delete_container(self.container) - except AzureHttpError as exc: + self.blob_service_client.get_container_client(container=self.container).delete_container() + except Exception as exc: self.fail("Error deleting container {0} - {1}".format(self.container, str(exc))) self.results['changed'] = True @@ -536,18 +554,18 @@ def delete_container(self): def container_has_blobs(self): try: - list_generator = self.blob_client.list_blobs(self.container) - except AzureHttpError as exc: + blobs = self.blob_service_client.get_container_client(container=self.container).list_blobs() + except Exception as exc: self.fail("Error list blobs in {0} - {1}".format(self.container, str(exc))) - if len(list_generator.items) > 0: + if len(list(blobs)) > 0: return True return False def delete_blob(self): if not self.check_mode: try: - self.blob_client.delete_blob(self.container, self.blob) - except AzureHttpError as exc: + self.blob_service_client.get_container_client(container=self.container).delete_blob(blob=self.blob) + except Exception as exc: self.fail("Error deleting blob {0}:{1} - {2}".format(self.container, self.blob, str(exc))) self.results['changed'] = True @@ -557,8 +575,8 @@ def delete_blob(self): def update_container_tags(self, tags): if not self.check_mode: try: - self.blob_client.set_container_metadata(self.container, metadata=tags) - except AzureHttpError as exc: + self.blob_service_client.get_container_client(container=self.container).set_container_metadata(metadata=tags) + except Exception as exc: self.fail("Error updating container tags {0} - {1}".format(self.container, str(exc))) self.container_obj = self.get_container() self.results['changed'] = True @@ -568,8 +586,8 @@ def update_container_tags(self, tags): def update_blob_tags(self, tags): if not self.check_mode: try: - self.blob_client.set_blob_metadata(self.container, self.blob, metadata=tags) - except AzureHttpError as exc: + self.blob_service_client.get_blob_client(container=self.container, blob=self.blob).set_blob_metadata(metadata=tags) + except Exception as exc: self.fail("Update blob tags {0}:{1} - {2}".format(self.container, self.blob, str(exc))) self.blob_obj = self.get_blob() self.results['changed'] = True @@ -604,8 +622,8 @@ def update_blob_content_settings(self): ) if not self.check_mode: try: - self.blob_client.set_blob_properties(self.container, self.blob, content_settings=content_settings) - except AzureHttpError as exc: + self.blob_service_client.get_blob_client(container=self.container, blob=self.blob).set_http_headers(content_settings=content_settings) + except Exception as exc: self.fail("Update blob content settings {0}:{1} - {2}".format(self.container, self.blob, str(exc))) self.blob_obj = self.get_blob() diff --git a/plugins/modules/azure_rm_virtualmachine.py b/plugins/modules/azure_rm_virtualmachine.py index 40e998b5a..bf7824326 100644 --- a/plugins/modules/azure_rm_virtualmachine.py +++ b/plugins/modules/azure_rm_virtualmachine.py @@ -2026,12 +2026,12 @@ def delete_vm_storage(self, vhd_uris): container_name = blob_parts['containername'] blob_name = blob_parts['blobname'] - blob_client = self.get_blob_client(self.resource_group, storage_account_name) + blob_service_client = self.get_blob_service_client(self.resource_group, storage_account_name) self.log("Delete blob {0}:{1}".format(container_name, blob_name)) self.results['actions'].append("Deleted blob {0}:{1}".format(container_name, blob_name)) try: - blob_client.delete_blob(container_name, blob_name) + blob_service_client.get_blob_client(container=container_name, blob=blob_name).delete_blob() except Exception as exc: self.fail("Error deleting blob {0}:{1} - {2}".format(container_name, blob_name, str(exc))) return True diff --git a/requirements-azure.txt b/requirements-azure.txt index f3ae9147f..7d889f285 100644 --- a/requirements-azure.txt +++ b/requirements-azure.txt @@ -32,7 +32,7 @@ azure-mgmt-storage==19.0.0 azure-mgmt-trafficmanager==0.50.0 azure-mgmt-web==0.41.0 azure-nspkg==2.0.0 -azure-storage==0.35.1 +azure-storage-blob==12.11.0 msrest==0.6.21 msrestazure==0.6.4 azure-keyvault==1.0.0a1 From 9b5560bf5aea0ab45c75d6773fd5b291acd2c2c4 Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Sun, 24 Apr 2022 21:57:20 -0500 Subject: [PATCH 04/14] fix storage blob test lint issues --- .../azure_rm_storageblob/tasks/main.yml | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/tests/integration/targets/azure_rm_storageblob/tasks/main.yml b/tests/integration/targets/azure_rm_storageblob/tasks/main.yml index 8137738d1..890801697 100644 --- a/tests/integration/targets/azure_rm_storageblob/tasks/main.yml +++ b/tests/integration/targets/azure_rm_storageblob/tasks/main.yml @@ -1,6 +1,6 @@ - name: Create storage account name set_fact: - storage_account: "sb{{ resource_group | hash('md5') | truncate(22, True, '') }}" + storage_account: "sb{{ resource_group | hash('md5') | truncate(22, True, '') }}" - name: Create storage account azure_rm_storageaccount: @@ -23,9 +23,9 @@ src: './targets/azure_rm_storageblob/files/Ratings.png' content_type: image/png tags: - val1: foo - val2: bar - force: yes + val1: foo + val2: bar + force: true - name: storage blob seems to have some timing issues wait_for: @@ -40,12 +40,11 @@ src: './targets/azure_rm_storageblob/files/Ratings.png' content_type: image/png tags: - val1: foo - val2: bar + val1: foo + val2: bar register: upload_facts - - assert: - that: "not upload_facts.changed" + that: "not upload_facts.changed" - name: Download file idempotence azure_rm_storageblob: @@ -55,9 +54,8 @@ blob: 'Ratings.png' dest: './targets/azure_rm_storageblob/files/Ratings.png' register: download_results - - assert: - that: not download_results.changed + that: not download_results.changed - file: path="/tmp/Ratings.png" state=absent @@ -69,14 +67,13 @@ blob: 'Ratings.png' dest: '/tmp/Ratings.png' register: download_results - - assert: - that: "download_results.changed" + that: "download_results.changed" - find: paths='/tmp' patterns="Ratings.png" register: find_results - -- assert: { that: "find_results['matched'] == 1" } +- assert: + that: "find_results['matched'] == 1" - name: Do not delete container that has blobs azure_rm_storageblob: @@ -85,9 +82,8 @@ container_name: my-blobs state: absent register: output - - assert: - that: "not output.changed" + that: "not output.changed" - name: Delete blob object azure_rm_storageblob: @@ -97,9 +93,8 @@ blob: "Ratings.png" state: absent register: output - - assert: - that: "output.changed" + that: "output.changed" - name: Delete container azure_rm_storageblob: @@ -108,9 +103,8 @@ container_name: my-blobs state: absent register: output - - assert: - that: "output.changed" + that: "output.changed" - name: Delete storage account azure_rm_storageaccount: From e5c5664584f36b5a90eeb762d2c684b1beef904f Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Mon, 25 Apr 2022 17:02:53 -0500 Subject: [PATCH 05/14] clean up storage test --- .../targets/azure_rm_storageaccount/tasks/main.yml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml index 72c7c9481..8a33a7c6f 100644 --- a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml +++ b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml @@ -33,7 +33,6 @@ name: "{{ storage_account_name_default }}" account_type: Standard_LRS register: defaults_output - - name: Assert status succeeded and results match expectations assert: that: @@ -52,7 +51,6 @@ account_type: Premium_ZRS kind: FileStorage register: filestorage_output - - name: Assert status succeeded and results match I(kind=FileStorage) assert: that: @@ -94,7 +92,6 @@ test: test galaxy: galaxy register: explicit_output - - name: Assert status succeeded and correct parameter results assert: that: @@ -143,7 +140,6 @@ test: test galaxy: galaxy register: output - - name: Assert that properties have not changed assert: that: @@ -171,7 +167,6 @@ resource_group: "{{ resource_group }}" name: "{{ storage_account_name_explicit }}" register: output - - name: Assert that properties have not changed assert: that: @@ -226,7 +221,6 @@ test: test galaxy: galaxy register: output - - name: Assert account change success assert: that: @@ -249,7 +243,6 @@ account_type: Premium_LRS register: output ignore_errors: true - - name: Assert account type change failed assert: that: @@ -265,7 +258,6 @@ use_sub_domain: false ignore_errors: true register: output - - name: Assert CNAME failure assert: that: @@ -279,9 +271,8 @@ - test - galaxy register: output - - assert: - that: output.storageaccounts | length >= 1 + that: output.storageaccounts | length >= 1 - name: Update account tags azure_rm_storageaccount: @@ -292,7 +283,6 @@ testing: testing delete: never register: output - - assert: that: - "output.state.tags | length == 2" @@ -320,12 +310,10 @@ - output.storageaccounts[0].network_acls.bypass == "AzureServices" - output.storageaccounts[0].network_acls.default_action == "Deny" - output.storageaccounts[0].network_acls.ip_rules | length == 1 - - name: List storage accounts by resource group. azure_rm_storageaccount_info: resource_group: "{{ resource_group }}" register: output - - assert: that: - "output.storageaccounts | length >= 2" From a26afb0daefc7fb0c930fdf3eaed35d9695bc732 Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Mon, 25 Apr 2022 18:10:05 -0500 Subject: [PATCH 06/14] correct blob issues --- plugins/module_utils/azure_rm_common.py | 8 ++++---- plugins/modules/azure_rm_storageblob.py | 11 ++++++----- .../targets/azure_rm_storageblob/tasks/main.yml | 11 ++++------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/plugins/module_utils/azure_rm_common.py b/plugins/module_utils/azure_rm_common.py index 34ff8a935..eb8e885b9 100644 --- a/plugins/module_utils/azure_rm_common.py +++ b/plugins/module_utils/azure_rm_common.py @@ -678,17 +678,17 @@ def check_provisioning_state(self, azure_object, requested_state='present'): def get_blob_service_client(self, resource_group_name, storage_account_name): try: - # Get keys from the storage account - self.log("Getting account properties") + self.log("Getting storage account detail") account = self.storage_client.storage_accounts.get_properties(resource_group_name=resource_group_name, account_name=storage_account_name) + account_keys = self.storage_client.storage_accounts.list_keys(resource_group_name=resource_group_name, account_name=storage_account_name) except Exception as exc: - self.fail("Error getting storage account properties for {0}: {1}".format(storage_account_name, str(exc))) + self.fail("Error getting storage account detail for {0}: {1}".format(storage_account_name, str(exc))) try: self.log("Create blob service client") return BlobServiceClient( account_url=account.primary_endpoints.blob, - credential=self.azure_auth.azure_credential_track2, + credential=account_keys.keys[0].value, ) except Exception as exc: self.fail("Error creating blob service client for storage account {0} - {1}".format(storage_account_name, str(exc))) diff --git a/plugins/modules/azure_rm_storageblob.py b/plugins/modules/azure_rm_storageblob.py index 545e14447..ff14e1236 100644 --- a/plugins/modules/azure_rm_storageblob.py +++ b/plugins/modules/azure_rm_storageblob.py @@ -266,6 +266,8 @@ def exec_module(self, **kwargs): self.blob_service_client = self.get_blob_service_client(self.resource_group, self.storage_account_name) self.container_obj = self.get_container() + if self.blob: + self.blob_obj = self.get_blob() if self.state == 'present': if not self.container_obj: @@ -283,11 +285,9 @@ def exec_module(self, **kwargs): if self.blob: # create, update or download blob - self.blob_obj = self.get_blob() if self.src and self.src_is_valid(): if self.blob_obj and not self.force: - self.log("Cannot upload to {0}. Blob with that name already exists. " - "Use the force option".format(self.blob)) + self.log("Cannot upload to {0}. Blob with that name already exists. Use the force option".format(self.blob)) else: self.upload_blob() elif self.dest and self.dest_is_valid(): @@ -414,7 +414,7 @@ def get_blob(self): blob = None if self.blob: try: - blob = self.blob_service_client.get_blob_client(container=self.container, blob=blob).get_blob_properties() + blob = self.blob_service_client.get_blob_client(container=self.container, blob=self.blob).get_blob_properties() except ResourceNotFoundError: pass if blob: @@ -472,7 +472,8 @@ def upload_blob(self): client.upload_blob(data=self.src, blob_type=self.get_blob_type(self.blob_type), metadata=self.tags, - content_settings=content_settings) + content_settings=content_settings, + overwrite=self.force) except Exception as exc: self.fail("Error creating blob {0} - {1}".format(self.blob, str(exc))) diff --git a/tests/integration/targets/azure_rm_storageblob/tasks/main.yml b/tests/integration/targets/azure_rm_storageblob/tasks/main.yml index 890801697..27dd684ad 100644 --- a/tests/integration/targets/azure_rm_storageblob/tasks/main.yml +++ b/tests/integration/targets/azure_rm_storageblob/tasks/main.yml @@ -1,6 +1,7 @@ - name: Create storage account name set_fact: storage_account: "sb{{ resource_group | hash('md5') | truncate(22, True, '') }}" + test1_file: "Ratings.png" - name: Create storage account azure_rm_storageaccount: @@ -20,24 +21,20 @@ account_name: "{{ storage_account }}" container_name: my-blobs blob: 'Ratings.png' - src: './targets/azure_rm_storageblob/files/Ratings.png' + src: '{{ test1_file }}' content_type: image/png tags: val1: foo val2: bar force: true -- name: storage blob seems to have some timing issues - wait_for: - delay: 10 - - name: Upload blob idempotence azure_rm_storageblob: resource_group: "{{ resource_group }}" account_name: "{{ storage_account }}" container_name: my-blobs blob: 'Ratings.png' - src: './targets/azure_rm_storageblob/files/Ratings.png' + src: '{{ test1_file }}' content_type: image/png tags: val1: foo @@ -52,7 +49,7 @@ account_name: "{{ storage_account }}" container_name: my-blobs blob: 'Ratings.png' - dest: './targets/azure_rm_storageblob/files/Ratings.png' + dest: '{{ test1_file }}' register: download_results - assert: that: not download_results.changed From af6a642e948dfdabb8f407720b7f18f20522390e Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Mon, 25 Apr 2022 18:18:12 -0500 Subject: [PATCH 07/14] correct file upload with new APIs --- plugins/modules/azure_rm_storageblob.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/plugins/modules/azure_rm_storageblob.py b/plugins/modules/azure_rm_storageblob.py index ff14e1236..f936f8618 100644 --- a/plugins/modules/azure_rm_storageblob.py +++ b/plugins/modules/azure_rm_storageblob.py @@ -374,10 +374,11 @@ def _guess_content_type(file_path, original): if not self.check_mode: try: client = self.blob_service_client.get_blob_client(container=self.container, blob=blob_path) - client.upload_blob(data=src, - blob_type=self.get_blob_type(self.blob_type), - metadata=self.tags, - content_settings=_guess_content_type(src, content_settings)) + with open(src, "rb") as data: + client.upload_blob(data=data, + blob_type=self.get_blob_type(self.blob_type), + metadata=self.tags, + content_settings=_guess_content_type(src, content_settings)) except Exception as exc: self.fail("Error creating blob {0} - {1}".format(src, str(exc))) self.results['actions'].append('created blob from {0}'.format(src)) @@ -469,11 +470,12 @@ def upload_blob(self): if not self.check_mode: try: client = self.blob_service_client.get_blob_client(container=self.container, blob=self.blob) - client.upload_blob(data=self.src, - blob_type=self.get_blob_type(self.blob_type), - metadata=self.tags, - content_settings=content_settings, - overwrite=self.force) + with open(self.src, "rb") as data: + client.upload_blob(data=data, + blob_type=self.get_blob_type(self.blob_type), + metadata=self.tags, + content_settings=content_settings, + overwrite=self.force) except Exception as exc: self.fail("Error creating blob {0} - {1}".format(self.blob, str(exc))) From 43483486853c265ef7670884a8aee4ca6d492443 Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Tue, 26 Apr 2022 10:06:39 -0500 Subject: [PATCH 08/14] update storageblob for ansible-test --- tests/integration/targets/azure_rm_storageblob/aliases | 1 - tests/integration/targets/azure_rm_storageblob/tasks/main.yml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/integration/targets/azure_rm_storageblob/aliases b/tests/integration/targets/azure_rm_storageblob/aliases index e82395432..aa77c071a 100644 --- a/tests/integration/targets/azure_rm_storageblob/aliases +++ b/tests/integration/targets/azure_rm_storageblob/aliases @@ -1,4 +1,3 @@ cloud/azure shippable/azure/group2 destructive -unstable diff --git a/tests/integration/targets/azure_rm_storageblob/tasks/main.yml b/tests/integration/targets/azure_rm_storageblob/tasks/main.yml index 27dd684ad..8ad07331f 100644 --- a/tests/integration/targets/azure_rm_storageblob/tasks/main.yml +++ b/tests/integration/targets/azure_rm_storageblob/tasks/main.yml @@ -1,7 +1,7 @@ - name: Create storage account name set_fact: storage_account: "sb{{ resource_group | hash('md5') | truncate(22, True, '') }}" - test1_file: "Ratings.png" + test1_file: "./targets/azure_rm_storageblob/files/Ratings.png" - name: Create storage account azure_rm_storageaccount: From 04a8d269f6971e2d01ab8743d7e1789142c991ed Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Wed, 18 May 2022 16:01:56 -0500 Subject: [PATCH 09/14] allow configuration of static website on storage account --- plugins/modules/azure_rm_storageaccount.py | 95 +++++++++++++++++-- .../azure_rm_storageaccount/tasks/main.yml | 92 +++++++++++++++++- 2 files changed, 173 insertions(+), 14 deletions(-) diff --git a/plugins/modules/azure_rm_storageaccount.py b/plugins/modules/azure_rm_storageaccount.py index bbfa05cba..d818f05be 100644 --- a/plugins/modules/azure_rm_storageaccount.py +++ b/plugins/modules/azure_rm_storageaccount.py @@ -184,6 +184,25 @@ type: list elements: str required: true + static_website: + description: + - Manage static website configuration for the storage account. + type: dict + version_added: "1.12.0" + suboptions: + enabled: + description: + - Indicates whether this account is hosting a static website. + type: bool + default: false + index_document: + description: + - The default name of the index page under each directory. + type: str + error_document404_path: + description: + - The absolute path of the custom 404 page. + type: str extends_documentation_fragment: - azure.azcollection.azure @@ -401,6 +420,31 @@ returned: always type: str sample: "Microsoft.Storage/storageAccounts" + static_website: + description: + - Static website configuration for the storage account. + returned: always + version_added: "1.12.0" + type: complex + contains: + enabled: + description: + - Whether this account is hosting a static website. + returned: always + type: bool + sample: true + index_document: + description: + - The default name of the index page under each directory. + returned: always + type: str + sample: index.html + error_document404_path: + description: + - The absolute path of the custom 404 page. + returned: always + type: str + sample: error.html ''' try: @@ -422,6 +466,12 @@ allowed_headers=dict(type='list', elements='str', required=True), ) +static_website_spec = dict( + enabled=dict(type='bool', default=False), + index_document=dict(type='str'), + error_document404_path=dict(type='str'), +) + def compare_cors(cors1, cors2): if len(cors1) != len(cors2): @@ -463,7 +513,8 @@ def __init__(self): minimum_tls_version=dict(type='str', choices=['TLS1_0', 'TLS1_1', 'TLS1_2']), allow_blob_public_access=dict(type='bool'), network_acls=dict(type='dict'), - blob_cors=dict(type='list', options=cors_rule_spec, elements='dict') + blob_cors=dict(type='list', options=cors_rule_spec, elements='dict'), + static_website=dict(type='dict', options=static_website_spec), ) self.results = dict( @@ -487,6 +538,7 @@ def __init__(self): self.allow_blob_public_access = None self.network_acls = None self.blob_cors = None + self.static_website = None super(AzureRMStorageAccount, self).__init__(self.module_arg_spec, supports_check_mode=True) @@ -540,7 +592,7 @@ def check_name_availability(self): self.log('Checking name availability for {0}'.format(self.name)) try: account_name = self.storage_models.StorageAccountCheckNameAvailabilityParameters(name=self.name) - response = self.storage_client.storage_accounts.check_name_availability(account_name) + self.storage_client.storage_accounts.check_name_availability(account_name) except Exception as e: self.log('Error attempting to validate name.') self.fail("Error checking name availability: {0}".format(str(e))) @@ -548,21 +600,23 @@ def check_name_availability(self): def get_account(self): self.log('Get properties for account {0}'.format(self.name)) account_obj = None - blob_service_props = None + blob_mgmt_props = None + blob_client_props = None account_dict = None try: account_obj = self.storage_client.storage_accounts.get_properties(self.resource_group, self.name) - blob_service_props = self.storage_client.blob_services.get_service_properties(self.resource_group, self.name) + blob_mgmt_props = self.storage_client.blob_services.get_service_properties(self.resource_group, self.name) + blob_client_props = self.get_blob_service_client(self.resource_group, self.name).get_service_properties() except Exception: pass if account_obj: - account_dict = self.account_obj_to_dict(account_obj, blob_service_props) + account_dict = self.account_obj_to_dict(account_obj, blob_mgmt_props, blob_client_props) return account_dict - def account_obj_to_dict(self, account_obj, blob_service_props=None): + def account_obj_to_dict(self, account_obj, blob_mgmt_props=None, blob_client_props=None): account_dict = dict( id=account_obj.id, name=account_obj.name, @@ -606,14 +660,22 @@ def account_obj_to_dict(self, account_obj, blob_service_props=None): account_dict['tags'] = None if account_obj.tags: account_dict['tags'] = account_obj.tags - if blob_service_props and blob_service_props.cors and blob_service_props.cors.cors_rules: + if blob_mgmt_props and blob_mgmt_props.cors and blob_mgmt_props.cors.cors_rules: account_dict['blob_cors'] = [dict( allowed_origins=[to_native(y) for y in x.allowed_origins], allowed_methods=[to_native(y) for y in x.allowed_methods], max_age_in_seconds=x.max_age_in_seconds, exposed_headers=[to_native(y) for y in x.exposed_headers], allowed_headers=[to_native(y) for y in x.allowed_headers] - ) for x in blob_service_props.cors.cors_rules] + ) for x in blob_mgmt_props.cors.cors_rules] + + if blob_client_props and blob_client_props['static_website']: + static_website = blob_client_props['static_website'] + account_dict['static_website'] = dict( + enabled=static_website.enabled, + index_document=static_website.index_document, + error_document404_path=static_website.error_document404_path, + ) account_dict['network_acls'] = None if account_obj.network_rule_set: @@ -785,6 +847,11 @@ def update_account(self): if not self.check_mode: self.set_blob_cors() + if self.static_website and self.static_website != self.account_dict.get("static_website", dict()): + self.results['changed'] = True + self.account_dict['static_website'] = self.static_website + self.update_static_website() + def create_account(self): self.log("Creating account {0}".format(self.name)) @@ -809,7 +876,6 @@ def create_account(self): enable_https_traffic_only=self.https_only, minimum_tls_version=self.minimum_tls_version, allow_blob_public_access=self.allow_blob_public_access, - networks_acls=dict(), tags=dict() ) if self.tags: @@ -818,6 +884,8 @@ def create_account(self): account_dict['network_acls'] = self.network_acls if self.blob_cors: account_dict['blob_cors'] = self.blob_cors + if self.static_website: + account_dict['static_website'] = self.static_website return account_dict sku = self.storage_models.Sku(name=self.storage_models.SkuName(self.account_type)) sku.tier = self.storage_models.SkuTier.standard if 'Standard' in self.account_type else \ @@ -842,7 +910,8 @@ def create_account(self): self.set_network_acls() if self.blob_cors: self.set_blob_cors() - # the poller doesn't actually return anything + if self.static_website: + self.update_static_website() return self.get_account() def delete_account(self): @@ -887,6 +956,12 @@ def set_blob_cors(self): except Exception as exc: self.fail("Failed to set CORS rules: {0}".format(str(exc))) + def update_static_website(self): + try: + self.get_blob_service_client(self.resource_group, self.name).set_service_properties(static_website=self.static_website) + except Exception as exc: + self.fail("Failed to set static website config: {0}".format(str(exc))) + def set_network_acls(self): try: parameters = self.storage_models.StorageAccountUpdateParameters(network_rule_set=self.network_acls) diff --git a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml index 8a33a7c6f..12bcfab51 100644 --- a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml +++ b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml @@ -11,8 +11,8 @@ register: output ignore_errors: true - name: Check intentional name failure. - assert: - that: + assert: + that: - output.failed - output.msg is regex('AccountNameInvalid') @@ -25,6 +25,7 @@ loop: - "{{ storage_account_name_default }}" - "{{ storage_account_name_explicit }}" + - "{{ storage_account_name_default }}01" - "{{ storage_account_name_default }}02" - name: Create new storage account with defaults (omitted parameters) @@ -44,6 +45,88 @@ - defaults_output.state.allow_blob_public_access == true - defaults_output.state.minimum_tls_version == "TLS1_0" +- name: Create storage account with static website disabled + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}01" + account_type: Standard_LRS + kind: StorageV2 + static_website: + enabled: false + register: output +- name: Assert output + assert: + that: + - output.changed + - output.state.static_website is defined + - not output.state.static_website.enabled + +- name: Create storage account with static website disabled (idempotency test) + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}01" + account_type: Standard_LRS + kind: StorageV2 + static_website: + enabled: false + register: output +- name: Assert not changed + assert: + that: + - not output.changed + +- name: Enable storage account static website + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}01" + account_type: Standard_LRS + kind: StorageV2 + static_website: + enabled: true + register: output +- name: Assert output + assert: + that: + - output.changed + - output.state.static_website is defined + - output.state.static_website.enabled + +- name: Configure additional storage account static website properties + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}01" + account_type: Standard_LRS + kind: StorageV2 + static_website: + enabled: true + index_document: "index.html" + error_document404_path: "error.html" + register: output +- name: Assert output + assert: + that: + - output.changed + - output.state.static_website is defined + - output.state.static_website.enabled + - output.state.static_website.index_document == 'index.html' + - output.state.static_website.error_document404_path == 'error.html' + +- name: Configure additional storage account static website properties (idempotency test) + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}01" + account_type: Standard_LRS + kind: StorageV2 + static_website: + enabled: true + index_document: "index.html" + error_document404_path: "error.html" + register: output +- name: Assert not changed + assert: + that: + - not output.changed + - name: Create new storage account with I(kind=FileStorage) azure_rm_storageaccount: resource_group: "{{ resource_group }}" @@ -259,8 +342,8 @@ ignore_errors: true register: output - name: Assert CNAME failure - assert: - that: + assert: + that: - output.failed - output.msg is regex('custom domain name could not be verified') @@ -326,5 +409,6 @@ force_delete_nonempty: True loop: - "{{ storage_account_name_default }}" + - "{{ storage_account_name_default }}01" - "{{ storage_account_name_default }}02" - "{{ storage_account_name_explicit }}" From 62f1d210e4843e4ad38282df09b2fec1d7cd4a7d Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Wed, 18 May 2022 16:41:37 -0500 Subject: [PATCH 10/14] add static website output to storage info module --- .../modules/azure_rm_storageaccount_info.py | 53 ++++++++++++++++--- .../azure_rm_storageaccount/tasks/main.yml | 15 +++++- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/plugins/modules/azure_rm_storageaccount_info.py b/plugins/modules/azure_rm_storageaccount_info.py index 0ffeafc72..5343bb5a7 100644 --- a/plugins/modules/azure_rm_storageaccount_info.py +++ b/plugins/modules/azure_rm_storageaccount_info.py @@ -400,6 +400,31 @@ returned: always type: dict sample: { "tag1": "abc" } + static_website: + description: + - Static website configuration for the storage account. + returned: always + version_added: "1.12.0" + type: complex + contains: + enabled: + description: + - Whether this account is hosting a static website. + returned: always + type: bool + sample: true + index_document: + description: + - The default name of the index page under each directory. + returned: always + type: str + sample: index.html + error_document404_path: + description: + - The absolute path of the custom 404 page. + returned: always + type: str + sample: error.html ''' try: @@ -508,7 +533,7 @@ def serialize(self, raw): def format_to_dict(self, raw): return [self.account_obj_to_dict(item) for item in raw] - def account_obj_to_dict(self, account_obj, blob_service_props=None): + def account_obj_to_dict(self, account_obj): account_dict = dict( id=account_obj.id, name=account_obj.name, @@ -579,15 +604,23 @@ def account_obj_to_dict(self, account_obj, blob_service_props=None): account_dict['tags'] = None if account_obj.tags: account_dict['tags'] = account_obj.tags - blob_service_props = self.get_blob_service_props(account_dict['resource_group'], account_dict['name']) - if blob_service_props and blob_service_props.cors and blob_service_props.cors.cors_rules: + blob_mgmt_props = self.get_blob_mgmt_props(account_dict['resource_group'], account_dict['name']) + if blob_mgmt_props and blob_mgmt_props.cors and blob_mgmt_props.cors.cors_rules: account_dict['blob_cors'] = [dict( allowed_origins=to_native(x.allowed_origins), allowed_methods=to_native(x.allowed_methods), max_age_in_seconds=x.max_age_in_seconds, exposed_headers=to_native(x.exposed_headers), allowed_headers=to_native(x.allowed_headers) - ) for x in blob_service_props.cors.cors_rules] + ) for x in blob_mgmt_props.cors.cors_rules] + blob_client_props = self.get_blob_client_props(account_dict['resource_group'], account_dict['name']) + if blob_client_props and blob_client_props['static_website']: + static_website = blob_client_props['static_website'] + account_dict['static_website'] = dict( + enabled=static_website.enabled, + index_document=static_website.index_document, + error_document404_path=static_website.error_document404_path, + ) return account_dict def format_endpoint_dict(self, name, key, endpoint, storagetype, protocol='https'): @@ -602,12 +635,18 @@ def format_endpoint_dict(self, name, key, endpoint, storagetype, protocol='https endpoint) return result - def get_blob_service_props(self, resource_group, name): + def get_blob_mgmt_props(self, resource_group, name): if not self.show_blob_cors: return None try: - blob_service_props = self.storage_client.blob_services.get_service_properties(resource_group, name) - return blob_service_props + return self.storage_client.blob_services.get_service_properties(resource_group, name) + except Exception: + pass + return None + + def get_blob_client_props(self, resource_group, name): + try: + return self.get_blob_service_client(resource_group, name).get_service_properties() except Exception: pass return None diff --git a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml index 12bcfab51..0241bbd0b 100644 --- a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml +++ b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml @@ -379,7 +379,6 @@ show_connection_string: True show_blob_cors: True register: output - - assert: that: - "output.storageaccounts | length == 1" @@ -393,6 +392,20 @@ - output.storageaccounts[0].network_acls.bypass == "AzureServices" - output.storageaccounts[0].network_acls.default_action == "Deny" - output.storageaccounts[0].network_acls.ip_rules | length == 1 + +- name: Gather static website properties + azure_rm_storageaccount_info: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}01" + register: output +- assert: + that: + - output.storageaccounts | length == 1 + - output.storageaccounts[0].static_website is defined + - output.storageaccounts[0].static_website.enabled + - output.storageaccounts[0].static_website.index_document == 'index.html' + - output.storageaccounts[0].static_website.error_document404_path == 'error.html' + - name: List storage accounts by resource group. azure_rm_storageaccount_info: resource_group: "{{ resource_group }}" From a5de356467274c084c66ba972469c7ee9b396c1c Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Sat, 11 Jun 2022 14:59:46 -0500 Subject: [PATCH 11/14] handle blob client access for non-blob account types --- plugins/modules/azure_rm_storageaccount.py | 7 ++++++- plugins/modules/azure_rm_storageaccount_info.py | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/plugins/modules/azure_rm_storageaccount.py b/plugins/modules/azure_rm_storageaccount.py index d818f05be..93a725a25 100644 --- a/plugins/modules/azure_rm_storageaccount.py +++ b/plugins/modules/azure_rm_storageaccount.py @@ -607,7 +607,8 @@ def get_account(self): try: account_obj = self.storage_client.storage_accounts.get_properties(self.resource_group, self.name) blob_mgmt_props = self.storage_client.blob_services.get_service_properties(self.resource_group, self.name) - blob_client_props = self.get_blob_service_client(self.resource_group, self.name).get_service_properties() + if self.kind != "FileStorage": + blob_client_props = self.get_blob_service_client(self.resource_group, self.name).get_service_properties() except Exception: pass @@ -935,6 +936,8 @@ def account_has_blob_containers(self): If there are blob containers, then there are likely VMs depending on this account and it should not be deleted. ''' + if self.kind == "FileStorage": + return False self.log('Checking for existing blob containers') blob_service = self.get_blob_service_client(self.resource_group, self.name) try: @@ -957,6 +960,8 @@ def set_blob_cors(self): self.fail("Failed to set CORS rules: {0}".format(str(exc))) def update_static_website(self): + if self.kind == "FileStorage": + return try: self.get_blob_service_client(self.resource_group, self.name).set_service_properties(static_website=self.static_website) except Exception as exc: diff --git a/plugins/modules/azure_rm_storageaccount_info.py b/plugins/modules/azure_rm_storageaccount_info.py index 5343bb5a7..73f6593c9 100644 --- a/plugins/modules/azure_rm_storageaccount_info.py +++ b/plugins/modules/azure_rm_storageaccount_info.py @@ -613,7 +613,7 @@ def account_obj_to_dict(self, account_obj): exposed_headers=to_native(x.exposed_headers), allowed_headers=to_native(x.allowed_headers) ) for x in blob_mgmt_props.cors.cors_rules] - blob_client_props = self.get_blob_client_props(account_dict['resource_group'], account_dict['name']) + blob_client_props = self.get_blob_client_props(account_dict['resource_group'], account_dict['name'], account_dict['kind']) if blob_client_props and blob_client_props['static_website']: static_website = blob_client_props['static_website'] account_dict['static_website'] = dict( @@ -644,7 +644,9 @@ def get_blob_mgmt_props(self, resource_group, name): pass return None - def get_blob_client_props(self, resource_group, name): + def get_blob_client_props(self, resource_group, name, kind): + if kind == "FileStorage": + return None try: return self.get_blob_service_client(resource_group, name).get_service_properties() except Exception: From e8ca1a8bf82b95b7b270f02b55efc91bf3e13571 Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Sat, 11 Jun 2022 15:27:32 -0500 Subject: [PATCH 12/14] ensure static website props always returned --- plugins/modules/azure_rm_storageaccount.py | 11 ++++++++--- plugins/modules/azure_rm_storageaccount_info.py | 9 +++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/plugins/modules/azure_rm_storageaccount.py b/plugins/modules/azure_rm_storageaccount.py index 93a725a25..2e2406869 100644 --- a/plugins/modules/azure_rm_storageaccount.py +++ b/plugins/modules/azure_rm_storageaccount.py @@ -188,7 +188,7 @@ description: - Manage static website configuration for the storage account. type: dict - version_added: "1.12.0" + version_added: "1.13.0" suboptions: enabled: description: @@ -424,7 +424,7 @@ description: - Static website configuration for the storage account. returned: always - version_added: "1.12.0" + version_added: "1.13.0" type: complex contains: enabled: @@ -635,7 +635,12 @@ def account_obj_to_dict(self, account_obj, blob_mgmt_props=None, blob_client_pro https_only=account_obj.enable_https_traffic_only, minimum_tls_version=account_obj.minimum_tls_version, allow_blob_public_access=account_obj.allow_blob_public_access, - network_acls=account_obj.network_rule_set + network_acls=account_obj.network_rule_set, + static_website=dict( + enabled=False, + index_document=None, + error_document404_path=None, + ), ) account_dict['custom_domain'] = None if account_obj.custom_domain: diff --git a/plugins/modules/azure_rm_storageaccount_info.py b/plugins/modules/azure_rm_storageaccount_info.py index 73f6593c9..7584b0264 100644 --- a/plugins/modules/azure_rm_storageaccount_info.py +++ b/plugins/modules/azure_rm_storageaccount_info.py @@ -404,7 +404,7 @@ description: - Static website configuration for the storage account. returned: always - version_added: "1.12.0" + version_added: "1.13.0" type: complex contains: enabled: @@ -551,7 +551,12 @@ def account_obj_to_dict(self, account_obj): primary_location=account_obj.primary_location, https_only=account_obj.enable_https_traffic_only, minimum_tls_version=account_obj.minimum_tls_version, - allow_blob_public_access=account_obj.allow_blob_public_access + allow_blob_public_access=account_obj.allow_blob_public_access, + static_website=dict( + enabled=False, + index_document=None, + error_document404_path=None, + ), ) id_dict = self.parse_resource_to_dict(account_obj.id) From c89d6c78f5438d76d5b9ffce8927afc6aedf29a7 Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Sat, 11 Jun 2022 15:28:19 -0500 Subject: [PATCH 13/14] additional static website tests --- .../azure_rm_storageaccount/tasks/main.yml | 75 ++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml index 0241bbd0b..7775511c6 100644 --- a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml +++ b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml @@ -27,6 +27,7 @@ - "{{ storage_account_name_explicit }}" - "{{ storage_account_name_default }}01" - "{{ storage_account_name_default }}02" + - "{{ storage_account_name_default }}03" - name: Create new storage account with defaults (omitted parameters) azure_rm_storageaccount: @@ -60,6 +61,8 @@ - output.changed - output.state.static_website is defined - not output.state.static_website.enabled + - output.state.static_website.index_document == None + - output.state.static_website.error_document404_path == None - name: Create storage account with static website disabled (idempotency test) azure_rm_storageaccount: @@ -90,6 +93,8 @@ - output.changed - output.state.static_website is defined - output.state.static_website.enabled + - output.state.static_website.index_document == None + - output.state.static_website.error_document404_path == None - name: Configure additional storage account static website properties azure_rm_storageaccount: @@ -127,6 +132,58 @@ that: - not output.changed +- name: Create storage account with static website enabled + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}03" + account_type: Standard_LRS + kind: StorageV2 + static_website: + enabled: true + index_document: "abc.htm" + register: output +- name: Assert output + assert: + that: + - output.changed + - output.state.static_website is defined + - output.state.static_website.enabled + - output.state.static_website.index_document == "abc.htm" + - output.state.static_website.error_document404_path == None + +- name: Create storage account with static website enabled (idempotency test) + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}03" + account_type: Standard_LRS + kind: StorageV2 + static_website: + enabled: true + index_document: "abc.htm" + register: output +- name: Assert not changed + assert: + that: + - not output.changed + +- name: Disable storage account static website + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}03" + account_type: Standard_LRS + kind: StorageV2 + static_website: + enabled: false + register: output +- name: Assert output + assert: + that: + - output.changed + - output.state.static_website is defined + - not output.state.static_website.enabled + - output.state.static_website.index_document == None + - output.state.static_website.error_document404_path == None + - name: Create new storage account with I(kind=FileStorage) azure_rm_storageaccount: resource_group: "{{ resource_group }}" @@ -393,7 +450,7 @@ - output.storageaccounts[0].network_acls.default_action == "Deny" - output.storageaccounts[0].network_acls.ip_rules | length == 1 -- name: Gather static website properties +- name: Gather enabled static website properties azure_rm_storageaccount_info: resource_group: "{{ resource_group }}" name: "{{ storage_account_name_default }}01" @@ -406,6 +463,19 @@ - output.storageaccounts[0].static_website.index_document == 'index.html' - output.storageaccounts[0].static_website.error_document404_path == 'error.html' +- name: Gather disabled static website properties + azure_rm_storageaccount_info: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}03" + register: output +- assert: + that: + - output.storageaccounts | length == 1 + - output.storageaccounts[0].static_website is defined + - not output.storageaccounts[0].static_website.enabled + - output.storageaccounts[0].static_website.index_document == None + - output.storageaccounts[0].static_website.error_document404_path == None + - name: List storage accounts by resource group. azure_rm_storageaccount_info: resource_group: "{{ resource_group }}" @@ -422,6 +492,7 @@ force_delete_nonempty: True loop: - "{{ storage_account_name_default }}" + - "{{ storage_account_name_explicit }}" - "{{ storage_account_name_default }}01" - "{{ storage_account_name_default }}02" - - "{{ storage_account_name_explicit }}" + - "{{ storage_account_name_default }}03" From eed17008164ab973e9fcb2abe5225e6bb4b08d75 Mon Sep 17 00:00:00 2001 From: Ross Bender Date: Sat, 11 Jun 2022 15:44:59 -0500 Subject: [PATCH 14/14] bump test storage name to reduce pr conflicts --- .../targets/azure_rm_storageaccount/tasks/main.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml index 7775511c6..bb1002009 100644 --- a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml +++ b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml @@ -27,7 +27,7 @@ - "{{ storage_account_name_explicit }}" - "{{ storage_account_name_default }}01" - "{{ storage_account_name_default }}02" - - "{{ storage_account_name_default }}03" + - "{{ storage_account_name_default }}04" - name: Create new storage account with defaults (omitted parameters) azure_rm_storageaccount: @@ -135,7 +135,7 @@ - name: Create storage account with static website enabled azure_rm_storageaccount: resource_group: "{{ resource_group }}" - name: "{{ storage_account_name_default }}03" + name: "{{ storage_account_name_default }}04" account_type: Standard_LRS kind: StorageV2 static_website: @@ -154,7 +154,7 @@ - name: Create storage account with static website enabled (idempotency test) azure_rm_storageaccount: resource_group: "{{ resource_group }}" - name: "{{ storage_account_name_default }}03" + name: "{{ storage_account_name_default }}04" account_type: Standard_LRS kind: StorageV2 static_website: @@ -169,7 +169,7 @@ - name: Disable storage account static website azure_rm_storageaccount: resource_group: "{{ resource_group }}" - name: "{{ storage_account_name_default }}03" + name: "{{ storage_account_name_default }}04" account_type: Standard_LRS kind: StorageV2 static_website: @@ -466,7 +466,7 @@ - name: Gather disabled static website properties azure_rm_storageaccount_info: resource_group: "{{ resource_group }}" - name: "{{ storage_account_name_default }}03" + name: "{{ storage_account_name_default }}04" register: output - assert: that: @@ -495,4 +495,4 @@ - "{{ storage_account_name_explicit }}" - "{{ storage_account_name_default }}01" - "{{ storage_account_name_default }}02" - - "{{ storage_account_name_default }}03" + - "{{ storage_account_name_default }}04"