Skip to content

Commit

Permalink
[Storage] GA account level and blob version level immutable storage (#…
Browse files Browse the repository at this point in the history
…20233)

* GA account level WORM

* GA blob version level WORM
  • Loading branch information
evelyn-ys authored Nov 8, 2021
1 parent 8ea26d6 commit c56a4a3
Show file tree
Hide file tree
Showing 7 changed files with 462 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/azure-cli-core/azure/cli/core/profiles/_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def default_api_version(self):
ResourceType.DATA_KEYVAULT_ADMINISTRATION_BACKUP: '7.2-preview',
ResourceType.DATA_KEYVAULT_ADMINISTRATION_ACCESS_CONTROL: '7.2-preview',
ResourceType.DATA_STORAGE: '2018-11-09',
ResourceType.DATA_STORAGE_BLOB: '2020-06-12',
ResourceType.DATA_STORAGE_BLOB: '2020-10-02',
ResourceType.DATA_STORAGE_FILEDATALAKE: '2020-02-10',
ResourceType.DATA_STORAGE_FILESHARE: '2019-07-07',
ResourceType.DATA_STORAGE_QUEUE: '2018-03-28',
Expand Down
33 changes: 33 additions & 0 deletions src/azure-cli/azure/cli/command_modules/storage/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,39 @@
crafted: true
"""

helps['storage blob immutability-policy'] = """
type: group
short-summary: Manage blob immutability policy.
"""

helps['storage blob immutability-policy set'] = """
type: command
short-summary: Set blob's immutability policy.
examples:
- name: Set an unlocked immutability policy.
text: az storage blob immutability-policy set --expiry-time 2021-09-07T08:00:00Z --policy-mode Unlocked -c mycontainer -n myblob --account-name mystorageaccount
- name: Lock a immutability policy.
text: az storage blob immutability-policy set --policy-mode Locked -c mycontainer -n myblob --account-name mystorageaccount
"""

helps['storage blob immutability-policy delete'] = """
type: command
short-summary: Delete blob's immutability policy.
examples:
- name: Delete an unlocked immutability policy.
text: az storage blob immutability-policy delete -c mycontainer -n myblob --account-name mystorageaccount --account-key 0000-0000
"""

helps['storage blob set-legal-hold'] = """
type: command
short-summary: Set blob legal hold.
examples:
- name: Configure blob legal hold.
text: az storage blob set-legal-hold --legal-hold -c mycontainer -n myblob --account-name mystorageaccount --account-key 0000-0000
- name: Clear blob legal hold.
text: az storage blob set-legal-hold --legal-hold false -c mycontainer -n myblob --account-name mystorageaccount --account-key 0000-0000
"""

helps['storage blob show'] = """
type: command
short-summary: Get the details of a blob.
Expand Down
29 changes: 21 additions & 8 deletions src/azure-cli/azure/cli/command_modules/storage/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,7 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem

immutability_period_since_creation_in_days_type = CLIArgumentType(
options_list=['--immutability-period-in-days', '--immutability-period'], min_api='2021-06-01',
help='The immutability period for the blobs in the container since the policy creation, in days.',
is_preview=True
help='The immutability period for the blobs in the container since the policy creation, in days.'
)

account_immutability_policy_state_enum = self.get_sdk(
Expand All @@ -238,7 +237,7 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
'Locked state only allows the increase of the immutability retention time. '
'A policy can only be created in a Disabled or Unlocked state and can be toggled between the '
'two states. Only a policy in an Unlocked state can transition to a Locked state which cannot '
'be reverted.', is_preview=True)
'be reverted.')

public_network_access_enum = self.get_sdk('models._storage_management_client_enums#PublicNetworkAccess',
resource_type=ResourceType.MGMT_STORAGE)
Expand Down Expand Up @@ -365,14 +364,14 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
' at the account creation time. When set to true, it enables object level immutability for all '
'the containers in the account by default.',
arg_group='Account Level Immutability',
validator=validate_immutability_arguments, is_preview=True)
validator=validate_immutability_arguments)
c.argument('immutability_period_since_creation_in_days',
arg_type=immutability_period_since_creation_in_days_type,
arg_group='Account Level Immutability',
validator=validate_immutability_arguments, is_preview=True)
validator=validate_immutability_arguments)
c.argument('immutability_policy_state', arg_type=immutability_policy_state_type,
arg_group='Account Level Immutability',
validator=validate_immutability_arguments, is_preview=True)
validator=validate_immutability_arguments)
c.argument('allow_protected_append_writes', arg_type=get_three_state_flag(),
options_list=['--allow-protected-append-writes', '--allow-append', '-w'],
min_api='2021-06-01',
Expand All @@ -381,7 +380,7 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
'protection and compliance. Only new blocks can be added and any existing blocks cannot be '
'modified or deleted.',
arg_group='Account Level Immutability',
validator=validate_immutability_arguments, is_preview=True)
validator=validate_immutability_arguments)
c.argument('public_network_access', arg_type=get_enum_type(public_network_access_enum), min_api='2021-06-01',
help='Enable or disable public network access to the storage account. '
'Possible values include: `Enabled` or `Disabled`.')
Expand Down Expand Up @@ -460,7 +459,7 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
'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.',
arg_group='Account Level Immutability', is_preview=True)
arg_group='Account Level Immutability')
c.argument('public_network_access', arg_type=get_enum_type(public_network_access_enum), min_api='2021-06-01',
help='Enable or disable public network access to the storage account. '
'Possible values include: `Enabled` or `Disabled`.')
Expand Down Expand Up @@ -867,6 +866,20 @@ def load_arguments(self, _): # pylint: disable=too-many-locals, too-many-statem
is_preview=True, help="Indicate the priority with which to rehydrate an archived blob. "
"The priority can be set on a blob only once, default value is Standard.")

with self.argument_context('storage blob set-legal-hold') as c:
c.register_blob_arguments()
c.argument('legal_hold', arg_type=get_three_state_flag(),
help='Specified if a legal hold should be set on the blob.')

with self.argument_context('storage blob immutability-policy delete') as c:
c.register_blob_arguments()

with self.argument_context('storage blob immutability-policy set') as c:
c.register_blob_arguments()
c.argument('expiry_time', type=get_datetime_type(False),
help='expiration UTC datetime in (Y-m-d\'T\'H:M:S\'Z\')')
c.argument('policy_mode', arg_type=get_enum_type(['Locked', 'Unlocked']), help='Lock or Unlock the policy')

with self.argument_context('storage blob service-properties delete-policy update') as c:
c.argument('enable', arg_type=get_enum_type(['true', 'false']), help='Enables/disables soft-delete.')
c.argument('days_retained', type=int,
Expand Down
3 changes: 3 additions & 0 deletions src/azure-cli/azure/cli/command_modules/storage/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,9 @@ def get_custom_sdk(custom_module, client_factory, resource_type=ResourceType.DAT
g.storage_custom_command_oauth('query', 'query_blob',
is_preview=True, min_api='2019-12-12')
g.storage_custom_command_oauth('rewrite', 'rewrite_blob', is_preview=True, min_api='2020-04-08')
g.storage_command_oauth('set-legal-hold', 'set_legal_hold', min_api='2020-10-02')
g.storage_custom_command_oauth('immutability-policy set', 'set_immutability_policy', min_api='2020-10-02')
g.storage_command_oauth('immutability-policy delete', 'delete_immutability_policy', min_api='2020-10-02')

blob_lease_client_sdk = CliCommandType(
operations_tmpl='azure.multiapi.storagev2.blob._lease#BlobLeaseClient.{}',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,15 @@ def set_delete_policy(client, enable=None, days_retained=None):
return client.get_blob_service_properties().delete_retention_policy


def set_immutability_policy(cmd, client, expiry_time=None, policy_mode=None, **kwargs):
ImmutabilityPolicy = cmd.get_models("_models#ImmutabilityPolicy", resource_type=ResourceType.DATA_STORAGE_BLOB)
if not expiry_time and not policy_mode:
from azure.cli.core.azclierror import InvalidArgumentValueError
raise InvalidArgumentValueError('Please specify --expiry-time | --policy-mode')
immutability_policy = ImmutabilityPolicy(expiry_time=expiry_time, policy_mode=policy_mode)
return client.set_immutability_policy(immutability_policy=immutability_policy, **kwargs)


def set_service_properties(client, parameters, delete_retention=None, delete_retention_period=None,
static_website=None, index_document=None, error_document_404_path=None):
# update
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,42 @@ def test_storage_block_blob_set_tier(self, resource_group, storage_account):
JMESPathCheck('properties.rehydrationStatus', 'rehydrate-pending-to-hot'))


@api_version_constraint(ResourceType.DATA_STORAGE_BLOB, min_api='2020-10-02')
class StorageBlobImmutabilityTests(StorageScenarioMixin, ScenarioTest):
@ResourceGroupPreparer(name_prefix='clitest')
@StorageAccountPreparer(name_prefix='version', kind='StorageV2', location='centraluseuap')
def test_storage_blob_vlm(self, resource_group, storage_account_info):
container = self.create_random_name(prefix='container', length=18)
blob = self.create_random_name(prefix='blob', length=18)
self.kwargs.update({
'container': container,
'blob': blob
})
# Enable blob versioning
self.cmd('storage account blob-service-properties update -n {sa} -g {rg} --enable-versioning')
# Enable vlm on container creation
self.cmd('storage container-rm create -n {container} --storage-account {sa} -g {rg} --enable-vlw')
# Prepare blob resource
file = self.create_temp_file(10)
self.storage_cmd('storage blob upload -c {} -f "{}" -n {} ', storage_account_info, container, file, blob)

# Test set immutability policy
from datetime import datetime, timedelta
expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ')
result = self.storage_cmd('storage blob immutability-policy set -n {} -c {} '
'--expiry-time {} --policy-mode Unlocked',
storage_account_info, blob, container, expiry).get_output_in_json()
self.assertEqual(result.get('immutability_policy_mode'), 'unlocked')
self.assertIsNotNone(result.get('immutability_policy_until_date'))
# Test delete immutability policy
self.storage_cmd('storage blob immutability-policy delete -n {} -c {}', storage_account_info, blob, container)
# Test set legal hold
self.storage_cmd('storage blob set-legal-hold --legal-hold -n {} -c {}', storage_account_info, blob, container)\
.assert_with_checks(JMESPathCheck('legal_hold', True))
self.storage_cmd('storage blob set-legal-hold --legal-hold false -n {} -c {}', storage_account_info, blob, container) \
.assert_with_checks(JMESPathCheck('legal_hold', False))


@api_version_constraint(ResourceType.DATA_STORAGE_BLOB, min_api='2019-02-02')
class StorageBlobCommonTests(StorageScenarioMixin, ScenarioTest):
@ResourceGroupPreparer(name_prefix='clitest')
Expand Down

0 comments on commit c56a4a3

Please sign in to comment.