modified storageaccount fact return curated result with connection string (#49702)

pull/52472/head
Yuwei Zhou 6 years ago committed by Yunge Zhu
parent 896e320142
commit 664e6fb9c8

@ -205,7 +205,7 @@ def normalize_location_name(name):
AZURE_PKG_VERSIONS = { AZURE_PKG_VERSIONS = {
'StorageManagementClient': { 'StorageManagementClient': {
'package_name': 'storage', 'package_name': 'storage',
'expected_version': '1.5.0' 'expected_version': '3.1.0'
}, },
'ComputeManagementClient': { 'ComputeManagementClient': {
'package_name': 'compute', 'package_name': 'compute',
@ -379,6 +379,7 @@ class AzureRMModuleBase(object):
:param tags: metadata tags from the object :param tags: metadata tags from the object
:return: bool, dict :return: bool, dict
''' '''
tags = tags or dict()
new_tags = copy.copy(tags) if isinstance(tags, dict) else dict() new_tags = copy.copy(tags) if isinstance(tags, dict) else dict()
param_tags = self.module.params.get('tags') if isinstance(self.module.params.get('tags'), dict) else dict() param_tags = self.module.params.get('tags') if isinstance(self.module.params.get('tags'), dict) else dict()
append_tags = self.module.params.get('append_tags') if self.module.params.get('append_tags') is not None else True append_tags = self.module.params.get('append_tags') if self.module.params.get('append_tags') is not None else True
@ -790,12 +791,12 @@ class AzureRMModuleBase(object):
if not self._storage_client: if not self._storage_client:
self._storage_client = self.get_mgmt_svc_client(StorageManagementClient, self._storage_client = self.get_mgmt_svc_client(StorageManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager, base_url=self._cloud_environment.endpoints.resource_manager,
api_version='2017-10-01') api_version='2018-07-01')
return self._storage_client return self._storage_client
@property @property
def storage_models(self): def storage_models(self):
return StorageManagementClient.models("2017-10-01") return StorageManagementClient.models("2018-07-01")
@property @property
def network_client(self): def network_client(self):

@ -54,6 +54,7 @@ options:
- StandardSSD_LRS - StandardSSD_LRS
- Standard_RAGRS - Standard_RAGRS
- Standard_ZRS - Standard_ZRS
- Premium_ZRS
aliases: aliases:
- type - type
custom_domain: custom_domain:
@ -62,6 +63,8 @@ options:
keys where 'name' is the CNAME source. Only one custom domain is supported per storage account at this keys where 'name' is the CNAME source. Only one custom domain is supported per storage account at this
time. To clear the existing custom domain, use an empty string for the custom domain name property. time. To clear the existing custom domain, use an empty string for the custom domain name property.
- Can be added to an existing storage account. Will be ignored during storage account creation. - Can be added to an existing storage account. Will be ignored during storage account creation.
aliases:
- custom_dns_domain_suffix
kind: kind:
description: description:
- The 'kind' of storage. - The 'kind' of storage.
@ -78,10 +81,51 @@ options:
- Hot - Hot
- Cool - Cool
version_added: "2.4" version_added: "2.4"
force: force_delete_nonempty:
description: description:
- Attempt deletion if resource already exists and cannot be updated - Attempt deletion if resource already exists and cannot be updated
type: bool type: bool
aliases:
- force
https_only:
description:
- Allows https traffic only to storage service if sets to true.
type: bool
version_added: "2.8"
blob_cors:
description:
- Specifies CORS rules for the Blob service.
- You can include up to five CorsRule elements in the request.
- If no blob_cors elements are included in the argument list, nothing about CORS will be changed.
- "If you want to delete all CORS rules and disable CORS for the Blob service, explicitly set blob_cors: []."
type: list
version_added: "2.8"
suboptions:
allowed_origins:
description:
- A list of origin domains that will be allowed via CORS, or "*" to allow all domains.
type: list
required: true
allowed_methods:
description:
- A list of HTTP methods that are allowed to be executed by the origin.
type: list
required: true
max_age_in_seconds:
description:
- The number of seconds that the client/browser should cache a preflight response.
type: int
required: true
exposed_headers:
description:
- A list of response headers to expose to CORS clients.
type: list
required: true
allowed_headers:
description:
- A list of headers allowed to be part of the cross-origin request.
type: list
required: true
extends_documentation_fragment: extends_documentation_fragment:
- azure - azure
@ -90,7 +134,6 @@ extends_documentation_fragment:
author: author:
- "Chris Houseknecht (@chouseknecht)" - "Chris Houseknecht (@chouseknecht)"
- "Matt Davis (@nitzmahone)" - "Matt Davis (@nitzmahone)"
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -108,6 +151,25 @@ EXAMPLES = '''
tags: tags:
testing: testing testing: testing
delete: on-exit delete: on-exit
- name: create an account with blob CORS
azure_rm_storageaccount:
resource_group: Testing
name: clh002
type: Standard_RAGRS
blob_cors:
- allowed_origins:
- http://www.example.com/
allowed_methods:
- GET
- POST
allowed_headers:
- x-ms-meta-data*
- x-ms-meta-target*
- x-ms-meta-abc
exposed_headers:
- x-ms-meta-*
max_age_in_seconds: 200
''' '''
@ -151,7 +213,36 @@ except ImportError:
# This is handled in azure_rm_common # This is handled in azure_rm_common
pass pass
from ansible.module_utils.azure_rm_common import AZURE_SUCCESS_STATE, AzureRMModuleBase, HAS_AZURE import copy
from ansible.module_utils.azure_rm_common import AZURE_SUCCESS_STATE, AzureRMModuleBase
from ansible.module_utils._text import to_native
cors_rule_spec = dict(
allowed_origins=dict(type='list', elements='str', required=True),
allowed_methods=dict(type='list', elements='str', required=True),
max_age_in_seconds=dict(type='int', required=True),
exposed_headers=dict(type='list', elements='str', required=True),
allowed_headers=dict(type='list', elements='str', required=True),
)
def compare_cors(cors1, cors2):
if len(cors1) != len(cors2):
return False
copy2 = copy.copy(cors2)
for rule1 in cors1:
matched = False
for rule2 in copy2:
if (rule1['max_age_in_seconds'] == rule2['max_age_in_seconds']
and set(rule1['allowed_methods']) == set(rule2['allowed_methods'])
and set(rule1['allowed_origins']) == set(rule2['allowed_origins'])
and set(rule1['allowed_headers']) == set(rule2['allowed_headers'])
and set(rule1['exposed_headers']) == set(rule2['exposed_headers'])):
matched = True
copy2.remove(rule2)
if not matched:
return False
return True
class AzureRMStorageAccount(AzureRMModuleBase): class AzureRMStorageAccount(AzureRMModuleBase):
@ -159,24 +250,22 @@ class AzureRMStorageAccount(AzureRMModuleBase):
def __init__(self): def __init__(self):
self.module_arg_spec = dict( self.module_arg_spec = dict(
account_type=dict(type='str', choices=['Premium_LRS', 'Standard_GRS', 'Standard_LRS', 'StandardSSD_LRS', 'Standard_RAGRS', 'Standard_ZRS'], account_type=dict(type='str',
choices=['Premium_LRS', 'Standard_GRS', 'Standard_LRS', 'StandardSSD_LRS', 'Standard_RAGRS', 'Standard_ZRS', 'Premium_ZRS'],
aliases=['type']), aliases=['type']),
custom_domain=dict(type='dict'), custom_domain=dict(type='dict', aliases=['custom_dns_domain_suffix']),
location=dict(type='str'), location=dict(type='str'),
name=dict(type='str', required=True), name=dict(type='str', required=True),
resource_group=dict(required=True, type='str', aliases=['resource_group_name']), resource_group=dict(required=True, type='str', aliases=['resource_group_name']),
state=dict(default='present', choices=['present', 'absent']), state=dict(default='present', choices=['present', 'absent']),
force=dict(type='bool', default=False), force_delete_nonempty=dict(type='bool', default=False, aliases=['force']),
tags=dict(type='dict'), tags=dict(type='dict'),
kind=dict(type='str', default='Storage', choices=['Storage', 'StorageV2', 'BlobStorage']), kind=dict(type='str', default='Storage', choices=['Storage', 'StorageV2', 'BlobStorage']),
access_tier=dict(type='str', choices=['Hot', 'Cool']) access_tier=dict(type='str', choices=['Hot', 'Cool']),
https_only=dict(type='bool', default=False),
blob_cors=dict(type='list', options=cors_rule_spec, elements='dict')
) )
if HAS_AZURE:
for key in self.storage_models.SkuName:
if getattr(key, 'value') not in self.module_arg_spec['account_type']['choices']:
self.module_arg_spec['account_type']['choices'].append(getattr(key, 'value'))
self.results = dict( self.results = dict(
changed=False, changed=False,
state=dict() state=dict()
@ -190,9 +279,11 @@ class AzureRMStorageAccount(AzureRMModuleBase):
self.account_type = None self.account_type = None
self.custom_domain = None self.custom_domain = None
self.tags = None self.tags = None
self.force = None self.force_delete_nonempty = None
self.kind = None self.kind = None
self.access_tier = None self.access_tier = None
self.https_only = None
self.blob_cors = None
super(AzureRMStorageAccount, self).__init__(self.module_arg_spec, super(AzureRMStorageAccount, self).__init__(self.module_arg_spec,
supports_check_mode=True) supports_check_mode=True)
@ -254,19 +345,21 @@ class AzureRMStorageAccount(AzureRMModuleBase):
def get_account(self): def get_account(self):
self.log('Get properties for account {0}'.format(self.name)) self.log('Get properties for account {0}'.format(self.name))
account_obj = None account_obj = None
blob_service_props = None
account_dict = None account_dict = None
try: try:
account_obj = self.storage_client.storage_accounts.get_properties(self.resource_group, self.name) 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)
except CloudError: except CloudError:
pass pass
if account_obj: if account_obj:
account_dict = self.account_obj_to_dict(account_obj) account_dict = self.account_obj_to_dict(account_obj, blob_service_props)
return account_dict return account_dict
def account_obj_to_dict(self, account_obj): def account_obj_to_dict(self, account_obj, blob_service_props=None):
account_dict = dict( account_dict = dict(
id=account_obj.id, id=account_obj.id,
name=account_obj.name, name=account_obj.name,
@ -283,7 +376,8 @@ class AzureRMStorageAccount(AzureRMModuleBase):
if account_obj.status_of_primary is not None else None), if account_obj.status_of_primary is not None else None),
status_of_secondary=(account_obj.status_of_secondary.value status_of_secondary=(account_obj.status_of_secondary.value
if account_obj.status_of_secondary is not None else None), if account_obj.status_of_secondary is not None else None),
primary_location=account_obj.primary_location primary_location=account_obj.primary_location,
https_only=account_obj.enable_https_traffic_only
) )
account_dict['custom_domain'] = None account_dict['custom_domain'] = None
if account_obj.custom_domain: if account_obj.custom_domain:
@ -309,10 +403,30 @@ class AzureRMStorageAccount(AzureRMModuleBase):
account_dict['tags'] = None account_dict['tags'] = None
if account_obj.tags: if account_obj.tags:
account_dict['tags'] = 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:
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]
return account_dict return account_dict
def update_account(self): def update_account(self):
self.log('Update storage account {0}'.format(self.name)) self.log('Update storage account {0}'.format(self.name))
if bool(self.https_only) != bool(self.account_dict.get('https_only')):
self.results['changed'] = True
self.account_dict['https_only'] = self.https_only
if not self.check_mode:
try:
parameters = self.storage_models.StorageAccountUpdateParameters(enable_https_traffic_only=self.https_only)
self.storage_client.storage_accounts.update(self.resource_group,
self.name,
parameters)
except Exception as exc:
self.fail("Failed to update account type: {0}".format(str(exc)))
if self.account_type: if self.account_type:
if self.account_type != self.account_dict['sku_name']: if self.account_type != self.account_dict['sku_name']:
# change the account type # change the account type
@ -332,7 +446,7 @@ class AzureRMStorageAccount(AzureRMModuleBase):
try: try:
self.log("sku_name: %s" % self.account_dict['sku_name']) self.log("sku_name: %s" % self.account_dict['sku_name'])
self.log("sku_tier: %s" % self.account_dict['sku_tier']) self.log("sku_tier: %s" % self.account_dict['sku_tier'])
sku = self.storage_models.Sku(SkuName(self.account_dict['sku_name'])) sku = self.storage_models.Sku(name=SkuName(self.account_dict['sku_name']))
sku.tier = self.storage_models.SkuTier(self.account_dict['sku_tier']) sku.tier = self.storage_models.SkuTier(self.account_dict['sku_tier'])
parameters = self.storage_models.StorageAccountUpdateParameters(sku=sku) parameters = self.storage_models.StorageAccountUpdateParameters(sku=sku)
self.storage_client.storage_accounts.update(self.resource_group, self.storage_client.storage_accounts.update(self.resource_group,
@ -377,6 +491,11 @@ class AzureRMStorageAccount(AzureRMModuleBase):
except Exception as exc: except Exception as exc:
self.fail("Failed to update tags: {0}".format(str(exc))) self.fail("Failed to update tags: {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:
self.set_blob_cors()
def create_account(self): def create_account(self):
self.log("Creating account {0}".format(self.name)) self.log("Creating account {0}".format(self.name))
@ -398,16 +517,22 @@ class AzureRMStorageAccount(AzureRMModuleBase):
account_type=self.account_type, account_type=self.account_type,
name=self.name, name=self.name,
resource_group=self.resource_group, resource_group=self.resource_group,
enable_https_traffic_only=self.https_only,
tags=dict() tags=dict()
) )
if self.tags: if self.tags:
account_dict['tags'] = self.tags account_dict['tags'] = self.tags
if self.blob_cors:
account_dict['blob_cors'] = self.blob_cors
return account_dict return account_dict
sku = self.storage_models.Sku(self.storage_models.SkuName(self.account_type)) 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 \ sku.tier = self.storage_models.SkuTier.standard if 'Standard' in self.account_type else \
self.storage_models.SkuTier.premium self.storage_models.SkuTier.premium
parameters = self.storage_models.StorageAccountCreateParameters(sku, self.kind, self.location, parameters = self.storage_models.StorageAccountCreateParameters(sku=sku,
tags=self.tags, access_tier=self.access_tier) kind=self.kind,
location=self.location,
tags=self.tags,
access_tier=self.access_tier)
self.log(str(parameters)) self.log(str(parameters))
try: try:
poller = self.storage_client.storage_accounts.create(self.resource_group, self.name, parameters) poller = self.storage_client.storage_accounts.create(self.resource_group, self.name, parameters)
@ -415,13 +540,15 @@ class AzureRMStorageAccount(AzureRMModuleBase):
except CloudError as e: except CloudError as e:
self.log('Error creating storage account.') self.log('Error creating storage account.')
self.fail("Failed to create account: {0}".format(str(e))) self.fail("Failed to create account: {0}".format(str(e)))
if self.blob_cors:
self.set_blob_cors()
# the poller doesn't actually return anything # the poller doesn't actually return anything
return self.get_account() return self.get_account()
def delete_account(self): def delete_account(self):
if self.account_dict['provisioning_state'] == self.storage_models.ProvisioningState.succeeded.value and \ if self.account_dict['provisioning_state'] == self.storage_models.ProvisioningState.succeeded.value and \
self.account_has_blob_containers() and self.force: not self.force_delete_nonempty and self.account_has_blob_containers():
self.fail("Account contains blob containers. Is it in use? Use the force option to attempt deletion.") self.fail("Account contains blob containers. Is it in use? Use the force_delete_nonempty option to attempt deletion.")
self.log('Delete storage account {0}'.format(self.name)) self.log('Delete storage account {0}'.format(self.name))
self.results['changed'] = True self.results['changed'] = True
@ -451,6 +578,15 @@ class AzureRMStorageAccount(AzureRMModuleBase):
return True return True
return False return False
def set_blob_cors(self):
try:
cors_rules = self.storage_models.CorsRules(cors_rules=[self.storage_models.CorsRule(**x) for x in self.blob_cors])
self.storage_client.blob_services.set_service_properties(self.resource_group,
self.name,
self.storage_models.BlobServiceProperties(cors=cors_rules))
except Exception as exc:
self.fail("Failed to set CORS rules: {0}".format(str(exc)))
def main(): def main():
AzureRMStorageAccount() AzureRMStorageAccount()

@ -37,6 +37,18 @@ options:
tags: tags:
description: description:
- Limit results by providing a list of tags. Format tags as 'key' or 'key:value'. - Limit results by providing a list of tags. Format tags as 'key' or 'key:value'.
show_connection_string:
description:
- Show the connection string for each of the storageaccount's endpoints.
- Note that it will cost a lot of time when list all storageaccount rather than query a single one.
type: bool
version_added: "2.8"
show_blob_cors:
description:
- Show the blob CORS settings for each of the storageaccount's blob.
- Note that it will cost a lot time when list all storageaccount rather than querry a single one.
type: bool
version_added: "2.8"
extends_documentation_fragment: extends_documentation_fragment:
- azure - azure
@ -89,6 +101,176 @@ azure_storageaccounts:
"tags": {}, "tags": {},
"type": "Microsoft.Storage/storageAccounts" "type": "Microsoft.Storage/storageAccounts"
}] }]
storageaccounts:
description: List of storage account dicts in resource module's parameter format.
returned: always
type: complex
contains:
id:
description:
- Resource ID.
sample: "/subscriptions/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX/resourceGroups/testing/providers/Microsoft.Storage/storageAccounts/testaccount001"
name:
description:
- Name of the storage account to update or create.
sample: "testaccount001"
location:
description:
- Valid azure location. Defaults to location of the resource group.
sample: eastus
account_type:
description:
- Type of storage account.
- "NOTE: Standard_ZRS and Premium_LRS accounts cannot be changed to other account types."
- Other account types cannot be changed to Standard_ZRS or Premium_LRS.
sample: Standard_ZRS
custom_domain:
description:
- User domain assigned to the storage account.
- Must be a dictionary with 'name' and 'use_sub_domain' keys where 'name' is the CNAME source.
type: complex
contains:
name:
description:
- CNAME source.
sample: testaccount
use_sub_domain:
description:
- whether to use sub domain.
sample: true
kind:
description:
- The 'kind' of storage.
sample: Storage
access_tier:
description:
- The access tier for this storage account.
sample: Hot
https_only:
description:
- Allows https traffic only to storage service if sets to true.
sample: false
provisioning_state:
description:
- Gets the status of the storage account at the time the operation was called.
- Possible values include 'Creating', 'ResolvingDNS', 'Succeeded'.
sample: Succeeded
secondary_location:
description:
- Gets the location of the geo-replicated secondary for the storage account.
- Only available if the accountType is Standard_GRS or Standard_RAGRS.
sample: westus
status_of_primary:
description:
- Gets the status indicating whether the primary location of the storage account is available or unavailable.
sample: available
status_of_secondary:
description:
- Gets the status indicating whether the secondary location of the storage account is available or unavailable.
sample: available
primary_location:
description:
- Gets the location of the primary data center for the storage account.
sample: eastus
primary_endpoints:
description:
- Gets the URLs that are used to perform a retrieval of a public blob, queue, or table object.
- Note that Standard_ZRS and Premium_LRS accounts only return the blob endpoint.
type: complex
contains:
blob:
description:
- Gets the primary blob endpoint and connection string.
type: complex
contains:
endpoint:
description:
- Gets the primary blob endpoint.
sample: "https://testaccount001.blob.core.windows.net/"
connectionstring:
description:
- Connectionstring of the blob endpoint
sample: "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=X;AccountKey=X;BlobEndpoint=X"
queue:
description:
- Gets the primary queue endpoint and connection string.
type: complex
contains:
endpoint:
description:
- Gets the primary queue endpoint.
sample: "https://testaccount001.queue.core.windows.net/"
connectionstring:
description:
- Connectionstring of the queue endpoint
sample: "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=X;AccountKey=X;QueueEndpoint=X"
table:
description:
- Gets the primary table endpoint and connection string.
type: complex
contains:
endpoint:
description:
- Gets the primary table endpoint.
sample: "https://testaccount001.table.core.windows.net/"
connectionstring:
description:
- Connectionstring of the table endpoint
sample: "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=X;AccountKey=X;TableEndpoint=X"
secondary_endpoints:
description:
- Gets the URLs that are used to perform a retrieval of a public blob, queue, or table object from the secondary location.
- Only available if the SKU name is Standard_RAGRS.
type: complex
contains:
blob:
description:
- Gets the secondary blob endpoint and connection string.
type: complex
contains:
endpoint:
description:
- Gets the secondary blob endpoint.
sample: "https://testaccount001.blob.core.windows.net/"
connectionstring:
description:
- Connectionstring of the blob endpoint
sample: "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=X;AccountKey=X;BlobEndpoint=X"
queue:
description:
- Gets the secondary queue endpoint and connection string.
type: complex
contains:
endpoint:
description:
- Gets the secondary queue endpoint.
sample: "https://testaccount001.queue.core.windows.net/"
connectionstring:
description:
- Connectionstring of the queue endpoint
sample: "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=X;AccountKey=X;QueueEndpoint=X"
table:
description:
- Gets the secondary table endpoint and connection string.
type: complex
contains:
endpoint:
description:
- Gets the secondary table endpoint.
sample: "https://testaccount001.table.core.windows.net/"
connectionstring:
description:
- Connectionstring of the table endpoint
sample: "DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=X;AccountKey=X;TableEndpoint=X"
tags:
description:
- Resource tags.
type: dict
sample: { "tag1": "abc" }
blob_cors:
description:
- Blob CORS of blob.
type: list
''' '''
try: try:
@ -98,6 +280,7 @@ except Exception:
pass pass
from ansible.module_utils.azure_rm_common import AzureRMModuleBase from ansible.module_utils.azure_rm_common import AzureRMModuleBase
from ansible.module_utils._text import to_native
AZURE_OBJECT_CLASS = 'StorageAccount' AZURE_OBJECT_CLASS = 'StorageAccount'
@ -110,16 +293,21 @@ class AzureRMStorageAccountFacts(AzureRMModuleBase):
name=dict(type='str'), name=dict(type='str'),
resource_group=dict(type='str', aliases=['resource_group_name']), resource_group=dict(type='str', aliases=['resource_group_name']),
tags=dict(type='list'), tags=dict(type='list'),
show_connection_string=dict(type='bool'),
show_blob_cors=dict(type='bool')
) )
self.results = dict( self.results = dict(
changed=False, changed=False,
ansible_facts=dict(azure_storageaccounts=[]) ansible_facts=dict(azure_storageaccounts=[]),
storageaccounts=[]
) )
self.name = None self.name = None
self.resource_group = None self.resource_group = None
self.tags = None self.tags = None
self.show_connection_string = None
self.show_blob_cors = None
super(AzureRMStorageAccountFacts, self).__init__(self.module_arg_spec, super(AzureRMStorageAccountFacts, self).__init__(self.module_arg_spec,
supports_tags=False, supports_tags=False,
@ -133,29 +321,29 @@ class AzureRMStorageAccountFacts(AzureRMModuleBase):
if self.name and not self.resource_group: if self.name and not self.resource_group:
self.fail("Parameter error: resource group required when filtering by name.") self.fail("Parameter error: resource group required when filtering by name.")
results = []
if self.name: if self.name:
self.results['ansible_facts']['azure_storageaccounts'] = self.get_account() results = self.get_account()
elif self.resource_group: elif self.resource_group:
self.results['ansible_facts']['azure_storageaccounts'] = self.list_resource_group() results = self.list_resource_group()
else: else:
self.results['ansible_facts']['azure_storageaccounts'] = self.list_all() results = self.list_all()
filtered = self.filter_tag(results)
self.results['ansible_facts']['azure_storageaccounts'] = self.serialize(filtered)
self.results['ansible_facts']['storageaccounts'] = self.format_to_dict(filtered)
return self.results return self.results
def get_account(self): def get_account(self):
self.log('Get properties for account {0}'.format(self.name)) self.log('Get properties for account {0}'.format(self.name))
account = None account = None
result = []
try: try:
account = self.storage_client.storage_accounts.get_properties(self.resource_group, self.name) account = self.storage_client.storage_accounts.get_properties(self.resource_group, self.name)
return [account]
except CloudError: except CloudError:
pass pass
return []
if account and self.has_tags(account.tags, self.tags):
result = [self.serialize_obj(account, AZURE_OBJECT_CLASS)]
return result
def list_resource_group(self): def list_resource_group(self):
self.log('List items') self.log('List items')
@ -164,11 +352,7 @@ class AzureRMStorageAccountFacts(AzureRMModuleBase):
except Exception as exc: except Exception as exc:
self.fail("Error listing for resource group {0} - {1}".format(self.resource_group, str(exc))) self.fail("Error listing for resource group {0} - {1}".format(self.resource_group, str(exc)))
results = [] return response
for item in response:
if self.has_tags(item.tags, self.tags):
results.append(self.serialize_obj(item, AZURE_OBJECT_CLASS))
return results
def list_all(self): def list_all(self):
self.log('List all items') self.log('List all items')
@ -177,11 +361,110 @@ class AzureRMStorageAccountFacts(AzureRMModuleBase):
except Exception as exc: except Exception as exc:
self.fail("Error listing all items - {0}".format(str(exc))) self.fail("Error listing all items - {0}".format(str(exc)))
results = [] return response
for item in response:
if self.has_tags(item.tags, self.tags): def filter_tag(self, raw):
results.append(self.serialize_obj(item, AZURE_OBJECT_CLASS)) return [item for item in raw if self.has_tags(item.tags, self.tags)]
return results
def serialize(self, raw):
return [self.serialize_obj(item, AZURE_OBJECT_CLASS) for item in 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):
account_dict = dict(
id=account_obj.id,
name=account_obj.name,
location=account_obj.location,
access_tier=(account_obj.access_tier.value
if account_obj.access_tier is not None else None),
account_type=account_obj.sku.name.value,
kind=account_obj.kind.value if account_obj.kind else None,
provisioning_state=account_obj.provisioning_state.value,
secondary_location=account_obj.secondary_location,
status_of_primary=(account_obj.status_of_primary.value
if account_obj.status_of_primary is not None else None),
status_of_secondary=(account_obj.status_of_secondary.value
if account_obj.status_of_secondary is not None else None),
primary_location=account_obj.primary_location,
https_only=account_obj.enable_https_traffic_only
)
id_dict = self.parse_resource_to_dict(account_obj.id)
account_dict['resource_group'] = id_dict.get('resource_group')
account_key = self.get_connectionstring(account_dict['resource_group'], account_dict['name'])
account_dict['custom_domain'] = None
if account_obj.custom_domain:
account_dict['custom_domain'] = dict(
name=account_obj.custom_domain.name,
use_sub_domain=account_obj.custom_domain.use_sub_domain
)
account_dict['primary_endpoints'] = None
if account_obj.primary_endpoints:
account_dict['primary_endpoints'] = dict(
blob=self.format_endpoint_dict(account_dict['name'], account_key[0], account_obj.primary_endpoints.blob, 'blob'),
queue=self.format_endpoint_dict(account_dict['name'], account_key[0], account_obj.primary_endpoints.queue, 'queue'),
table=self.format_endpoint_dict(account_dict['name'], account_key[0], account_obj.primary_endpoints.table, 'table')
)
account_dict['secondary_endpoints'] = None
if account_obj.secondary_endpoints:
account_dict['secondary_endpoints'] = dict(
blob=self.format_endpoint_dict(account_dict['name'], account_key[1], account_obj.primary_endpoints.blob, 'blob'),
queue=self.format_endpoint_dict(account_dict['name'], account_key[1], account_obj.primary_endpoints.queue, 'queue'),
table=self.format_endpoint_dict(account_dict['name'], account_key[1], account_obj.primary_endpoints.table, 'table'),
)
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:
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]
return account_dict
def format_endpoint_dict(self, name, key, endpoint, storagetype, protocol='https'):
result = dict(endpoint=endpoint)
if key:
result['connectionstring'] = 'DefaultEndpointsProtocol={0};EndpointSuffix={1};AccountName={2};AccountKey={3};{4}Endpoint={5}'.format(
protocol,
self._cloud_environment.suffixes.storage_endpoint,
name,
key,
str.title(storagetype),
endpoint)
return result
def get_blob_service_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
except Exception:
pass
return None
def get_connectionstring(self, resource_group, name):
keys = ['', '']
if not self.show_connection_string:
return keys
try:
cred = self.storage_client.storage_accounts.list_keys(resource_group, name)
# get the following try catch from CLI
try:
keys = [cred.keys[0].value, cred.keys[1].value]
except AttributeError:
keys = [cred.key1, cred.key2]
except Exception:
pass
return keys
def main(): def main():

@ -1779,10 +1779,10 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
self.log("Storage account {0} found.".format(storage_account_name)) self.log("Storage account {0} found.".format(storage_account_name))
self.check_provisioning_state(account) self.check_provisioning_state(account)
return account return account
sku = self.storage_models.Sku(self.storage_models.SkuName.standard_lrs) sku = self.storage_models.Sku(name=self.storage_models.SkuName.standard_lrs)
sku.tier = self.storage_models.SkuTier.standard sku.tier = self.storage_models.SkuTier.standard
kind = self.storage_models.Kind.storage kind = self.storage_models.Kind.storage
parameters = self.storage_models.StorageAccountCreateParameters(sku, kind, self.location) parameters = self.storage_models.StorageAccountCreateParameters(sku=sku, kind=kind, location=self.location)
self.log("Creating storage account {0} in location {1}".format(storage_account_name, self.location)) self.log("Creating storage account {0} in location {1}".format(storage_account_name, self.location))
self.results['actions'].append("Created storage account {0}".format(storage_account_name)) self.results['actions'].append("Created storage account {0}".format(storage_account_name))
try: try:

@ -19,7 +19,7 @@ azure-mgmt-redis==5.0.0
azure-mgmt-resource==1.2.2 azure-mgmt-resource==1.2.2
azure-mgmt-rdbms==1.4.1 azure-mgmt-rdbms==1.4.1
azure-mgmt-sql==0.10.0 azure-mgmt-sql==0.10.0
azure-mgmt-storage==1.5.0 azure-mgmt-storage==3.1.0
azure-mgmt-trafficmanager==0.50.0 azure-mgmt-trafficmanager==0.50.0
azure-mgmt-web==0.32.0 azure-mgmt-web==0.32.0
azure-nspkg==2.0.0 azure-nspkg==2.0.0

@ -18,6 +18,7 @@
resource_group: "{{ resource_group }}" resource_group: "{{ resource_group }}"
name: "{{ storage_account }}" name: "{{ storage_account }}"
state: absent state: absent
force_delete_nonempty: True
- name: Create new storage account - name: Create new storage account
azure_rm_storageaccount: azure_rm_storageaccount:
@ -25,6 +26,19 @@
name: "{{ storage_account }}" name: "{{ storage_account }}"
account_type: Standard_LRS account_type: Standard_LRS
append_tags: no append_tags: no
blob_cors:
- allowed_origins:
- http://www.example.com/
allowed_methods:
- GET
- POST
allowed_headers:
- x-ms-meta-data*
- x-ms-meta-target*
- x-ms-meta-abc
exposed_headers:
- x-ms-meta-*
max_age_in_seconds: 200
tags: tags:
test: test test: test
galaxy: galaxy galaxy: galaxy
@ -35,6 +49,35 @@
that: that:
- output.changed - output.changed
- output.state.id is defined - output.state.id is defined
- output.state.blob_cors | length == 1
- name: Create new storage account (idempotence)
azure_rm_storageaccount:
resource_group: "{{ resource_group }}"
name: "{{ storage_account }}"
account_type: Standard_LRS
append_tags: no
blob_cors:
- allowed_origins:
- http://www.example.com/
allowed_methods:
- GET
- POST
allowed_headers:
- x-ms-meta-data*
- x-ms-meta-target*
- x-ms-meta-abc
exposed_headers:
- x-ms-meta-*
max_age_in_seconds: 200
tags:
test: test
galaxy: galaxy
register: output
- assert:
that:
- not output.changed
- name: Gather facts by tags - name: Gather facts by tags
azure_rm_storageaccount_facts: azure_rm_storageaccount_facts:
@ -69,22 +112,6 @@
- name: Assert CNAME failure - name: Assert CNAME failure
assert: { that: "'custom domain name could not be verified' in change_account['msg']" } assert: { that: "'custom domain name could not be verified' in change_account['msg']" }
- name: Update account tags
azure_rm_storageaccount:
resource_group: "{{ resource_group }}"
name: "{{ storage_account }}"
append_tags: no
tags:
testing: testing
delete: never
galaxy: 'no'
register: output
- assert:
that:
- "output.state.tags | length == 3"
- "output.state.tags.galaxy == 'no'"
- name: Update account tags - name: Update account tags
azure_rm_storageaccount: azure_rm_storageaccount:
resource_group: "{{ resource_group }}" resource_group: "{{ resource_group }}"
@ -105,10 +132,17 @@
azure_rm_storageaccount_facts: azure_rm_storageaccount_facts:
resource_group: "{{ resource_group }}" resource_group: "{{ resource_group }}"
name: "{{ storage_account }}" name: "{{ storage_account }}"
show_connection_string: True
show_blob_cors: True
- assert: - assert:
that: that:
- "azure_storageaccounts| length == 1" - "azure_storageaccounts| length == 1"
- "storageaccounts | length == 1"
- not storageaccounts[0].custom_domain
- storageaccounts[0].account_type == "Standard_GRS"
- storageaccounts[0].primary_endpoints.blob.connectionstring
- storageaccounts[0].blob_cors
- name: Gather facts - name: Gather facts
azure_rm_storageaccount_facts: azure_rm_storageaccount_facts:

@ -165,6 +165,7 @@
name: "{{ storage_account }}" name: "{{ storage_account }}"
type: Standard_LRS type: Standard_LRS
state: absent state: absent
force_delete_nonempty: true
- name: Delete Network Security Group that allows SSH - name: Delete Network Security Group that allows SSH
azure_rm_securitygroup: azure_rm_securitygroup:

@ -19,7 +19,7 @@ azure-mgmt-redis==5.0.0
azure-mgmt-resource==1.2.2 azure-mgmt-resource==1.2.2
azure-mgmt-rdbms==1.4.1 azure-mgmt-rdbms==1.4.1
azure-mgmt-sql==0.10.0 azure-mgmt-sql==0.10.0
azure-mgmt-storage==1.5.0 azure-mgmt-storage==3.1.0
azure-mgmt-trafficmanager==0.50.0 azure-mgmt-trafficmanager==0.50.0
azure-mgmt-web==0.32.0 azure-mgmt-web==0.32.0
azure-nspkg==2.0.0 azure-nspkg==2.0.0

Loading…
Cancel
Save