From e2da1ed6d2c6e1dcb5fc16cd016f6785c52f743c Mon Sep 17 00:00:00 2001 From: Fred-sun Date: Fri, 10 Jan 2025 14:27:16 +0800 Subject: [PATCH 1/3] Add support for immutable_storage_with_versioning --- plugins/modules/azure_rm_storageaccount.py | 113 ++++++++++++++++++ .../modules/azure_rm_storageaccount_info.py | 50 +++++++- .../azure_rm_storageaccount/tasks/main.yml | 73 +++++++++++ 3 files changed, 235 insertions(+), 1 deletion(-) diff --git a/plugins/modules/azure_rm_storageaccount.py b/plugins/modules/azure_rm_storageaccount.py index 54abafa94..a99640185 100644 --- a/plugins/modules/azure_rm_storageaccount.py +++ b/plugins/modules/azure_rm_storageaccount.py @@ -324,6 +324,41 @@ description: - A boolean indicating whether or not the service applies a secondary layer of encryption with platform managed keys for data at rest. type: bool + immutable_storage_with_versioning: + description: + - The property is immutable and can only be set to true at the account creation time. + - When set to true, it enables object level immutability for all the containers in the account by default. + type: dict + suboptions: + enabled: + description: + - A boolean flag which enables account-level immutability. + - All the containers under such an account have object-level immutability enabled by default. + type: bool + immutability_policy: + description: + - Specifies the default account-level immutability policy which is inherited and applied to objects that do not possess an explicit immutability policy at the object level. + - The object-level immutability policy has higher precedence than the container-level immutability policy, which has a higher precedence than the account-level immutability policy. + type: dict + suboptions: + allow_protected_append_writes: + description: + - This property can only be changed for disabled and unlocked time-based retention policies. + - When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. + - Only new blocks can be added and any existing blocks cannot be modified or deleted. + type: bool + state: + description: + - The ImmutabilityPolicy state defines the mode of the policy. + type: str + choices: + - Unlocked + - Locked + - Disabled + immutability_period_since_creation_in_days: + description: + - The immutability period for the blobs in the container since the policy creation, in days. + type: int extends_documentation_fragment: - azure.azcollection.azure @@ -518,6 +553,47 @@ type: bool returned: always sample: true + immutable_storage_with_versioning: + description: + - The property is immutable and can only be set to true at the account creation time. + - When set to true, it enables object level immutability for all the containers in the account by default. + type: complex + returned: when-used + contains: + enabled: + description: + - A boolean flag which enables account-level immutability. + - All the containers under such an account have object-level immutability enabled by default. + type: bool + returned: when-used + sample: true + immutability_policy: + description: + - Specifies the default account-level immutability policy which is inherited and applied to objects that do not possess an explicit immutability policy at the object level. + - The object-level immutability policy has higher precedence than the container-level immutability policy, which has a higher precedence than the account-level immutability policy. + type: dict + returned: when-used + contains: + allow_protected_append_writes: + description: + - This property can only be changed for disabled and unlocked time-based retention policies. + - When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. + - Only new blocks can be added and any existing blocks cannot be modified or deleted. + type: bool + returned: when-used + sample: true + state: + description: + - The ImmutabilityPolicy state defines the mode of the policy. + type: str + returned: when-used + sample: Unlocked + immutability_period_since_creation_in_days: + description: + - The immutability period for the blobs in the container since the policy creation, in days. + type: int + returned: when-used + sample: true enable_nfs_v3: description: - NFS 3.0 protocol. @@ -824,6 +900,20 @@ def __init__(self): type="dict", options=self.managed_identity_single_spec ), + immutable_storage_with_versioning=dict( + type='dict', + options=dict( + enabled=dict(type='bool'), + immutability_policy=dict( + type='dict', + options=dict( + immutability_period_since_creation_in_days=dict(type='int'), + state=dict(type='str', choices=['Unlocked', 'Locked', 'Disabled']), + allow_protected_append_writes=dict(type='bool') + ) + ) + ) + ) ) self.results = dict( @@ -859,6 +949,7 @@ def __init__(self): self._managed_identity = None self.identity = None self.update_identity = False + self.immutable_storage_with_versioning = None super(AzureRMStorageAccount, self).__init__(self.module_arg_spec, supports_check_mode=True) @@ -990,6 +1081,7 @@ def account_obj_to_dict(self, account_obj, blob_mgmt_props=None, blob_client_pro index_document=None, error_document404_path=None, ), + immutable_storage_with_versioning=dict() ) account_dict['custom_domain'] = None if account_obj.custom_domain: @@ -1066,6 +1158,16 @@ def account_obj_to_dict(self, account_obj, blob_mgmt_props=None, blob_client_pro if account_obj.identity: account_dict['identity'] = account_obj.identity.as_dict() + if account_obj.immutable_storage_with_versioning is not None: + account_dict['immutable_storage_with_versioning']['enabled'] = account_obj.immutable_storage_with_versioning.enabled + account_dict['immutable_storage_with_versioning']['immutability_policy'] = dict() + if account_obj.immutable_storage_with_versioning.immutability_policy is not None: + account_dict['immutable_storage_with_versioning']['immutability_policy']['immutability_period_since_creation_in_days'] = account_obj.immutable_storage_with_versioning.immutability_policy.immutability_period_since_creation_in_days + account_dict['immutable_storage_with_versioning']['immutability_policy']['allow_protected_append_writes'] = account_obj.immutable_storage_with_versioning.immutability_policy.allow_protected_append_writes + account_dict['immutable_storage_with_versioning']['immutability_policy']['state'] = account_obj.immutable_storage_with_versioning.immutability_policy.state + else: + account_dict['immutable_storage_with_versioning']['immutability_policy'] = None + return account_dict def failover_account(self): @@ -1322,6 +1424,15 @@ def update_account(self): except Exception as exc: self.fail("Failed to update tags: {0}".format(str(exc))) + if not self.default_compare({}, self.immutable_storage_with_versioning, self.account_dict['immutable_storage_with_versioning'], '', dict(compare=[])): + self.results['changed'] = True + if not self.check_mode: + parameters = self.storage_models.StorageAccountUpdateParameters(immutable_storage_with_versioning=self.immutable_storage_with_versioning) + try: + self.storage_client.storage_accounts.update(self.resource_group, self.name, parameters) + except Exception as exc: + self.fail("Failed to update immutable_storage_with_versioning: {0}".format(str(exc))) + if self.blob_cors and not compare_cors(self.account_dict.get('blob_cors', []), self.blob_cors): self.results['changed'] = True if not self.check_mode: @@ -1389,6 +1500,7 @@ def create_account(self): allow_cross_tenant_replication=self.allow_cross_tenant_replication, allow_shared_key_access=self.allow_shared_key_access, identity=self.identity, + immutable_storage_with_versioning=self.immutable_storage_with_versioning, tags=dict() ) if self.tags: @@ -1420,6 +1532,7 @@ def create_account(self): allow_shared_key_access=self.allow_shared_key_access, default_to_o_auth_authentication=self.default_to_o_auth_authentication, allow_cross_tenant_replication=self.allow_cross_tenant_replication, + immutable_storage_with_versioning=self.immutable_storage_with_versioning, large_file_shares_state=self.large_file_shares_state) self.log(str(parameters)) try: diff --git a/plugins/modules/azure_rm_storageaccount_info.py b/plugins/modules/azure_rm_storageaccount_info.py index a47139868..52f11a9cd 100644 --- a/plugins/modules/azure_rm_storageaccount_info.py +++ b/plugins/modules/azure_rm_storageaccount_info.py @@ -230,6 +230,47 @@ type: bool returned: always sample: true + immutable_storage_with_versioning: + description: + - The property is immutable and can only be set to true at the account creation time. + - When set to true, it enables object level immutability for all the containers in the account by default. + type: complex + returned: when-used + contains: + enabled: + description: + - A boolean flag which enables account-level immutability. + - All the containers under such an account have object-level immutability enabled by default. + type: bool + returned: when-used + sample: true + immutability_policy: + description: + - Specifies the default account-level immutability policy which is inherited and applied to objects that do not possess an explicit immutability policy at the object level. + - The object-level immutability policy has higher precedence than the container-level immutability policy, which has a higher precedence than the account-level immutability policy. + type: dict + returned: when-used + contains: + allow_protected_append_writes: + description: + - This property can only be changed for disabled and unlocked time-based retention policies. + - When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. + - Only new blocks can be added and any existing blocks cannot be modified or deleted. + type: bool + returned: when-used + sample: true + state: + description: + - The ImmutabilityPolicy state defines the mode of the policy. + type: str + returned: when-used + sample: Unlocked + immutability_period_since_creation_in_days: + description: + - The immutability period for the blobs in the container since the policy creation, in days. + type: int + returned: when-used + sample: true enable_nfs_v3: description: - NFS 3.0 protocol. @@ -715,6 +756,7 @@ def account_obj_to_dict(self, account_obj): index_document=None, error_document404_path=None, ), + immutable_storage_with_versioning=dict() ) account_dict['geo_replication_stats'] = None @@ -813,7 +855,13 @@ def account_obj_to_dict(self, account_obj): account_dict['identity'] = dict() if account_obj.identity: account_dict['identity'] = account_obj.identity.as_dict() - + if account_obj.immutable_storage_with_versioning is not None: + account_dict['immutable_storage_with_versioning']['enabled'] = account_obj.immutable_storage_with_versioning.enabled + account_dict['immutable_storage_with_versioning']['immutability_policy'] = dict() + if account_obj.immutable_storage_with_versioning.immutability_policy is not None: + account_dict['immutable_storage_with_versioning']['immutability_policy']['immutability_period_since_creation_in_days'] = account_obj.immutable_storage_with_versioning.immutability_policy.immutability_period_since_creation_in_days + account_dict['immutable_storage_with_versioning']['immutability_policy']['allow_protected_append_writes'] = account_obj.immutable_storage_with_versioning.immutability_policy.allow_protected_append_writes + account_dict['immutable_storage_with_versioning']['immutability_policy']['state'] = account_obj.immutable_storage_with_versioning.immutability_policy.state return account_dict def format_endpoint_dict(self, name, key, endpoint, storagetype, protocol='https'): diff --git a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml index bfa60b107..49788d539 100644 --- a/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml +++ b/tests/integration/targets/azure_rm_storageaccount/tasks/main.yml @@ -51,6 +51,8 @@ - "{{ storage_account_name_default }}03" - "{{ storage_account_name_default }}04" - "{{ storage_account_name_default }}06" + - "{{ storage_account_name_default }}07" + - "{{ storage_account_name_default }}08" - name: Create new storage account with defaults (omitted parameters) azure_rm_storageaccount: @@ -182,6 +184,76 @@ - output.storageaccounts[0].is_hns_enabled == true - output.storageaccounts[0].large_file_shares_state == 'Enabled' +- name: Create new storage account with immutable_storage_with_versioning + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}08" + account_type: Standard_GRS + kind: StorageV2 + immutable_storage_with_versioning: + enabled: true + immutability_policy: + immutability_period_since_creation_in_days: 10 + state: Disabled + allow_protected_append_writes: false + register: output + +- name: Assert the storage account well created + ansible.builtin.assert: + that: + - output.changed + +- name: Create new storage account with immutable_storage_with_versioning(Idempotent test) + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}08" + account_type: Standard_GRS + kind: StorageV2 + immutable_storage_with_versioning: + enabled: true + immutability_policy: + immutability_period_since_creation_in_days: 10 + state: Disabled + allow_protected_append_writes: false + register: output + +- name: Assert the storage account no change + ansible.builtin.assert: + that: + - not output.changed + +- name: Update the storage account with immutable_storage_with_versioning + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}08" + account_type: Standard_GRS + kind: StorageV2 + immutable_storage_with_versioning: + enabled: true + immutability_policy: + immutability_period_since_creation_in_days: 20 + state: Unlocked + allow_protected_append_writes: true + register: output + +- name: Assert the storage account well updated + ansible.builtin.assert: + that: + - output.changed + +- name: Gather facts of storage account + azure_rm_storageaccount_info: + resource_group: "{{ resource_group }}" + name: "{{ storage_account_name_default }}08" + register: output + +- name: Assert the storage account facts + ansible.builtin.assert: + that: + - output.storageaccounts[0].immutable_storage_with_versioning.enabled is true + - output.storageaccounts[0].immutable_storage_with_versioning.immutability_policy.allow_protected_append_writes is true + - output.storageaccounts[0].immutable_storage_with_versioning.immutability_policy.state == 'Unlocked' + - name: Create storage account with static website enabled azure_rm_storageaccount: resource_group: "{{ resource_group }}" @@ -741,6 +813,7 @@ - "{{ storage_account_name_default }}05" - "{{ storage_account_name_default }}06" - "{{ storage_account_name_default }}07" + - "{{ storage_account_name_default }}08" - name: Delete user managed identities ansible.builtin.include_tasks: "{{ role_path }}/../../../integration_common_tasks/managed_identity.yml" From 3df38800fa8cd2886612402a01efe93982c4b507 Mon Sep 17 00:00:00 2001 From: Fred-sun Date: Fri, 10 Jan 2025 14:57:34 +0800 Subject: [PATCH 2/3] Fix sanity error --- plugins/modules/azure_rm_storageaccount.py | 23 +++++++++++-------- .../modules/azure_rm_storageaccount_info.py | 13 +++++++---- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/plugins/modules/azure_rm_storageaccount.py b/plugins/modules/azure_rm_storageaccount.py index a99640185..479308a84 100644 --- a/plugins/modules/azure_rm_storageaccount.py +++ b/plugins/modules/azure_rm_storageaccount.py @@ -337,14 +337,16 @@ type: bool immutability_policy: description: - - Specifies the default account-level immutability policy which is inherited and applied to objects that do not possess an explicit immutability policy at the object level. - - The object-level immutability policy has higher precedence than the container-level immutability policy, which has a higher precedence than the account-level immutability policy. + - Specifies the default account-level immutability policy which is inherited and + applied to objects that do not possess an explicit immutability policy at the object level. + - The object-level immutability policy has higher precedence than the container-level immutability policy, + which has a higher precedence than the account-level immutability policy. type: dict suboptions: allow_protected_append_writes: description: - - This property can only be changed for disabled and unlocked time-based retention policies. - - When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. + - This property can only be changed for C(disabled) and C(unlocked) time-based retention policies. + - When C(enabled), new blocks can be written to an append blob while maintaining immutability protection and compliance. - Only new blocks can be added and any existing blocks cannot be modified or deleted. type: bool state: @@ -569,8 +571,10 @@ sample: true immutability_policy: description: - - Specifies the default account-level immutability policy which is inherited and applied to objects that do not possess an explicit immutability policy at the object level. - - The object-level immutability policy has higher precedence than the container-level immutability policy, which has a higher precedence than the account-level immutability policy. + - Specifies the default account-level immutability policy which is inherited and + applied to objects that do not possess an explicit immutability policy at the object level. + - The object-level immutability policy has higher precedence than the container-level immutability policy, + which has a higher precedence than the account-level immutability policy. type: dict returned: when-used contains: @@ -1162,9 +1166,10 @@ def account_obj_to_dict(self, account_obj, blob_mgmt_props=None, blob_client_pro account_dict['immutable_storage_with_versioning']['enabled'] = account_obj.immutable_storage_with_versioning.enabled account_dict['immutable_storage_with_versioning']['immutability_policy'] = dict() if account_obj.immutable_storage_with_versioning.immutability_policy is not None: - account_dict['immutable_storage_with_versioning']['immutability_policy']['immutability_period_since_creation_in_days'] = account_obj.immutable_storage_with_versioning.immutability_policy.immutability_period_since_creation_in_days - account_dict['immutable_storage_with_versioning']['immutability_policy']['allow_protected_append_writes'] = account_obj.immutable_storage_with_versioning.immutability_policy.allow_protected_append_writes - account_dict['immutable_storage_with_versioning']['immutability_policy']['state'] = account_obj.immutable_storage_with_versioning.immutability_policy.state + ipc = account_obj.immutable_storage_with_versioning.immutability_policy + account_dict['immutable_storage_with_versioning']['immutability_policy']['immutability_period_since_creation_in_days'] = ipc.immutability_period_since_creation_in_days + account_dict['immutable_storage_with_versioning']['immutability_policy']['allow_protected_append_writes'] = ipc.allow_protected_append_writes + account_dict['immutable_storage_with_versioning']['immutability_policy']['state'] = ipc.state else: account_dict['immutable_storage_with_versioning']['immutability_policy'] = None diff --git a/plugins/modules/azure_rm_storageaccount_info.py b/plugins/modules/azure_rm_storageaccount_info.py index 52f11a9cd..e4bb7e529 100644 --- a/plugins/modules/azure_rm_storageaccount_info.py +++ b/plugins/modules/azure_rm_storageaccount_info.py @@ -246,8 +246,10 @@ sample: true immutability_policy: description: - - Specifies the default account-level immutability policy which is inherited and applied to objects that do not possess an explicit immutability policy at the object level. - - The object-level immutability policy has higher precedence than the container-level immutability policy, which has a higher precedence than the account-level immutability policy. + - Specifies the default account-level immutability policy which is inherited and + applied to objects that do not possess an explicit immutability policy at the object level. + - The object-level immutability policy has higher precedence than the container-level immutability policy, + which has a higher precedence than the account-level immutability policy. type: dict returned: when-used contains: @@ -859,9 +861,10 @@ def account_obj_to_dict(self, account_obj): account_dict['immutable_storage_with_versioning']['enabled'] = account_obj.immutable_storage_with_versioning.enabled account_dict['immutable_storage_with_versioning']['immutability_policy'] = dict() if account_obj.immutable_storage_with_versioning.immutability_policy is not None: - account_dict['immutable_storage_with_versioning']['immutability_policy']['immutability_period_since_creation_in_days'] = account_obj.immutable_storage_with_versioning.immutability_policy.immutability_period_since_creation_in_days - account_dict['immutable_storage_with_versioning']['immutability_policy']['allow_protected_append_writes'] = account_obj.immutable_storage_with_versioning.immutability_policy.allow_protected_append_writes - account_dict['immutable_storage_with_versioning']['immutability_policy']['state'] = account_obj.immutable_storage_with_versioning.immutability_policy.state + ipc = account_obj.immutable_storage_with_versioning.immutability_policy + account_dict['immutable_storage_with_versioning']['immutability_policy']['immutability_period_since_creation_in_days'] = ipc.immutability_period_since_creation_in_days + account_dict['immutable_storage_with_versioning']['immutability_policy']['allow_protected_append_writes'] = ipc.allow_protected_append_writes + account_dict['immutable_storage_with_versioning']['immutability_policy']['state'] = ipc.state return account_dict def format_endpoint_dict(self, name, key, endpoint, storagetype, protocol='https'): From 8f3775bfde062f5f6f82bc6182311028df5c5685 Mon Sep 17 00:00:00 2001 From: Fred-sun Date: Fri, 10 Jan 2025 15:34:48 +0800 Subject: [PATCH 3/3] Fix sanity error 02 --- plugins/modules/azure_rm_storageaccount.py | 13 +------------ plugins/modules/azure_rm_storageaccount_info.py | 11 ++--------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/plugins/modules/azure_rm_storageaccount.py b/plugins/modules/azure_rm_storageaccount.py index 479308a84..eae1d4aef 100644 --- a/plugins/modules/azure_rm_storageaccount.py +++ b/plugins/modules/azure_rm_storageaccount.py @@ -1085,7 +1085,7 @@ def account_obj_to_dict(self, account_obj, blob_mgmt_props=None, blob_client_pro index_document=None, error_document404_path=None, ), - immutable_storage_with_versioning=dict() + immutable_storage_with_versioning=account_obj.immutable_storage_with_versioning.as_dict() if account_obj.immutable_storage_with_versioning else None ) account_dict['custom_domain'] = None if account_obj.custom_domain: @@ -1162,17 +1162,6 @@ def account_obj_to_dict(self, account_obj, blob_mgmt_props=None, blob_client_pro if account_obj.identity: account_dict['identity'] = account_obj.identity.as_dict() - if account_obj.immutable_storage_with_versioning is not None: - account_dict['immutable_storage_with_versioning']['enabled'] = account_obj.immutable_storage_with_versioning.enabled - account_dict['immutable_storage_with_versioning']['immutability_policy'] = dict() - if account_obj.immutable_storage_with_versioning.immutability_policy is not None: - ipc = account_obj.immutable_storage_with_versioning.immutability_policy - account_dict['immutable_storage_with_versioning']['immutability_policy']['immutability_period_since_creation_in_days'] = ipc.immutability_period_since_creation_in_days - account_dict['immutable_storage_with_versioning']['immutability_policy']['allow_protected_append_writes'] = ipc.allow_protected_append_writes - account_dict['immutable_storage_with_versioning']['immutability_policy']['state'] = ipc.state - else: - account_dict['immutable_storage_with_versioning']['immutability_policy'] = None - return account_dict def failover_account(self): diff --git a/plugins/modules/azure_rm_storageaccount_info.py b/plugins/modules/azure_rm_storageaccount_info.py index e4bb7e529..f840bc51d 100644 --- a/plugins/modules/azure_rm_storageaccount_info.py +++ b/plugins/modules/azure_rm_storageaccount_info.py @@ -758,7 +758,7 @@ def account_obj_to_dict(self, account_obj): index_document=None, error_document404_path=None, ), - immutable_storage_with_versioning=dict() + immutable_storage_with_versioning=account_obj.immutable_storage_with_versioning.as_dict() if account_obj.immutable_storage_with_versioning else None ) account_dict['geo_replication_stats'] = None @@ -857,14 +857,7 @@ def account_obj_to_dict(self, account_obj): account_dict['identity'] = dict() if account_obj.identity: account_dict['identity'] = account_obj.identity.as_dict() - if account_obj.immutable_storage_with_versioning is not None: - account_dict['immutable_storage_with_versioning']['enabled'] = account_obj.immutable_storage_with_versioning.enabled - account_dict['immutable_storage_with_versioning']['immutability_policy'] = dict() - if account_obj.immutable_storage_with_versioning.immutability_policy is not None: - ipc = account_obj.immutable_storage_with_versioning.immutability_policy - account_dict['immutable_storage_with_versioning']['immutability_policy']['immutability_period_since_creation_in_days'] = ipc.immutability_period_since_creation_in_days - account_dict['immutable_storage_with_versioning']['immutability_policy']['allow_protected_append_writes'] = ipc.allow_protected_append_writes - account_dict['immutable_storage_with_versioning']['immutability_policy']['state'] = ipc.state + return account_dict def format_endpoint_dict(self, name, key, endpoint, storagetype, protocol='https'):