Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements to azure.azcollection.azure_rm_storageaccount for Improved Security Compliance and Functionality #1330

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 133 additions & 9 deletions plugins/modules/azure_rm_storageaccount.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,28 @@
description:
- Create, update or delete a storage account.
options:
allow_shared_key_access:
description:
- When disabled, storage account key access denies all requests authorized by shared key or SAS.
type: bool
default: True
identity:
description:
- Identity for the resource.
type: dict
suboptions:
type:
description:
- The identity type. Required.
type: str
choices:
- None
- SystemAssigned
- UserAssigned
user_assigned_identities:
description:
- Sets a single User Assigned identity for the account, using key-value pairs where the key is the identity's ARM resource ID.
type: str
resource_group:
description:
- Name of the resource group to use.
Expand Down Expand Up @@ -294,11 +316,43 @@
choices:
- Microsoft.Storage
- Microsoft.Keyvault
key_vault_properties:
description:
- list of Microsoft Keyvault properties needed in order to create Storage account with encryption enabled with Microsoft KeyVault for CMK.
type: dict
suboptions:
key_vault_uri:
description:
- The Uri of KeyVault.
type: str
required: true
key_name:
description:
- The name of KeyVault key.
type: str
required: true
key_version:
description:
- The version of KeyVault key.
type: str

encryption_identity:
description:
- The identity to be used with service-side encryption at rest.
type: dict
suboptions:
encryption_user_assigned_identity:
description:
- Resource identifier of the UserAssigned identity to be associated with server-side encryption on the storage account.
type: str


require_infrastructure_encryption:
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


extends_documentation_fragment:
- azure.azcollection.azure
- azure.azcollection.azure_tags
Expand Down Expand Up @@ -450,6 +504,25 @@
type: dict
returned: always
sample: {'enabled': true}
key_vault_properties:
description:
- list of Microsoft Keyvault properties needed in order to create Storage account with encryption enabled with Microsoft KeyVault for CMK.
type: dict
sample: false
contains:
key_vault_uri:
description:
- The Uri of KeyVault.
type: str
key_name:
description:
- The name of KeyVault key.
type: str
key_version:
description:
- The version of KeyVault key.
type: str

id:
description:
- Resource ID.
Expand Down Expand Up @@ -711,6 +784,14 @@ def __init__(self):
blob_cors=dict(type='list', options=cors_rule_spec, elements='dict'),
static_website=dict(type='dict', options=static_website_spec),
is_hns_enabled=dict(type='bool'),
allow_shared_key_access=dict(type='bool', default='True'),
identity=dict(
type='dict',
options=dict(
type=dict(type='str', choices=['SystemAssigned', 'UserAssigned', 'None']),
user_assigned_identities=dict(type='str', required=False)
)
),
encryption=dict(
type='dict',
options=dict(
Expand All @@ -736,7 +817,21 @@ def __init__(self):
)
),
require_infrastructure_encryption=dict(type='bool'),
key_source=dict(type='str', choices=["Microsoft.Storage", "Microsoft.Keyvault"], default='Microsoft.Storage')
key_source=dict(type='str', choices=["Microsoft.Storage", "Microsoft.Keyvault"], default='Microsoft.Storage'),
key_vault_properties=dict(
type='dict',
options=dict(
key_vault_uri=dict(type='str', required=True, no_log=True),
key_name=dict(type='str', required=True),
key_version=dict(type='str', no_log=True)
), no_log=True
),
encryption_identity=dict(
type='dict',
options=dict(
encryption_user_assigned_identity=dict(type='str', required=False) # This will hold the resource ID of the user-assigned identity
)
)
)
)
)
Expand Down Expand Up @@ -766,6 +861,8 @@ def __init__(self):
self.static_website = None
self.encryption = None
self.is_hns_enabled = None
self.allow_shared_key_access = None
self.identity = None

super(AzureRMStorageAccount, self).__init__(self.module_arg_spec,
supports_check_mode=True)
Expand Down Expand Up @@ -815,7 +912,12 @@ def exec_module(self, **kwargs):
elif self.state == 'failover' and self.account_dict:
self.failover_account()
self.results['state'] = self.get_account()

# # Check if 'identity' parameter exists and process it
if hasattr(self, 'identity') and self.identity:
if 'user_assigned_identities' in self.identity and isinstance(self.identity['user_assigned_identities'], str):
# Convert the string to a dictionary
identity_resource_id = self.identity['user_assigned_identities']
self.identity['user_assigned_identities'] = {identity_resource_id: {}}
return self.results

def check_name_availability(self):
Expand All @@ -836,6 +938,15 @@ def get_account(self):

try:
account_obj = self.storage_client.storage_accounts.get_properties(self.resource_group, self.name)
if account_obj.identity and 'user_assigned_identities' in account_obj.identity:
# Check if user_assigned_identities is a dictionary
if isinstance(account_obj.identity['user_assigned_identities'], dict):
# Assuming there's only one key in the dictionary,
# convert the dictionary to a string (the key of the dictionary)
identity_resource_id_keys = list(account_obj.identity['user_assigned_identities'].keys())
if identity_resource_id_keys:
account_obj.identity['user_assigned_identities'] = identity_resource_id_keys[0]

blob_mgmt_props = self.storage_client.blob_services.get_service_properties(self.resource_group, self.name)
if self.kind != "FileStorage":
blob_client_props = self.get_blob_service_client(self.resource_group, self.name).get_service_properties()
Expand All @@ -844,7 +955,7 @@ def get_account(self):

if account_obj:
account_dict = self.account_obj_to_dict(account_obj, blob_mgmt_props, blob_client_props)

# print("account dict:{}".format(account_dict))
return account_dict

def account_obj_to_dict(self, account_obj, blob_mgmt_props=None, blob_client_props=None):
Expand All @@ -870,11 +981,12 @@ def account_obj_to_dict(self, account_obj, blob_mgmt_props=None, blob_client_pro
allow_blob_public_access=account_obj.allow_blob_public_access,
network_acls=account_obj.network_rule_set,
is_hns_enabled=account_obj.is_hns_enabled if account_obj.is_hns_enabled else False,
allow_shared_key_access=account_obj.allow_shared_key_access if account_obj.allow_shared_key_access else False,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be perfect if we could also increase this return value to azure_rm_storageaccount_info.py!

static_website=dict(
enabled=False,
index_document=None,
error_document404_path=None,
),
)
)
account_dict['custom_domain'] = None
if account_obj.custom_domain:
Expand Down Expand Up @@ -1182,6 +1294,12 @@ def create_account(self):

if not self.access_tier and self.kind == 'BlobStorage':
self.fail('Parameter error: access_tier required when creating a storage account of type BlobStorage.')
# # Check if 'identity' parameter exists and process it
if hasattr(self, 'identity') and self.identity:
if 'user_assigned_identities' in self.identity and isinstance(self.identity['user_assigned_identities'], str):
# Convert the string to a dictionary
identity_resource_id = self.identity['user_assigned_identities']
self.identity['user_assigned_identities'] = {identity_resource_id: {}}

self.check_name_availability()
self.results['changed'] = True
Expand All @@ -1198,12 +1316,15 @@ def create_account(self):
allow_blob_public_access=self.allow_blob_public_access,
encryption=self.encryption,
is_hns_enabled=self.is_hns_enabled,
allow_shared_key_access=self.allow_shared_key_access,
network_rule_set=self.network_acls,
identity=self.identity,
tags=dict()
)
if self.tags:
account_dict['tags'] = self.tags
if self.network_acls:
account_dict['network_acls'] = self.network_acls
# if self.network_acls:
# account_dict['network_acls'] = self.network_acls
if self.blob_cors:
account_dict['blob_cors'] = self.blob_cors
if self.static_website:
Expand All @@ -1223,16 +1344,19 @@ def create_account(self):
allow_blob_public_access=self.allow_blob_public_access,
encryption=self.encryption,
is_hns_enabled=self.is_hns_enabled,
access_tier=self.access_tier)
allow_shared_key_access=self.allow_shared_key_access,
access_tier=self.access_tier,
network_rule_set=self.network_acls,
identity=self.identity)
self.log(str(parameters))
try:
poller = self.storage_client.storage_accounts.begin_create(self.resource_group, self.name, parameters)
self.get_poller_result(poller)
except Exception as e:
self.log('Error creating storage account.')
self.fail("Failed to create account: {0}".format(str(e)))
if self.network_acls:
self.set_network_acls()
# if self.network_acls:
# self.set_network_acls()
if self.blob_cors:
self.set_blob_cors()
if self.static_website:
Expand Down