%PDF- %PDF-
Direktori : /usr/lib/python2.7/site-packages/salt/states/ |
Current File : //usr/lib/python2.7/site-packages/salt/states/azurearm_resource.py |
# -*- coding: utf-8 -*- ''' Azure (ARM) Resource State Module .. versionadded:: 2019.2.0 :maintainer: <devops@decisionlab.io> :maturity: new :depends: * `azure <https://pypi.python.org/pypi/azure>`_ >= 2.0.0 * `azure-common <https://pypi.python.org/pypi/azure-common>`_ >= 1.1.8 * `azure-mgmt <https://pypi.python.org/pypi/azure-mgmt>`_ >= 1.0.0 * `azure-mgmt-compute <https://pypi.python.org/pypi/azure-mgmt-compute>`_ >= 1.0.0 * `azure-mgmt-network <https://pypi.python.org/pypi/azure-mgmt-network>`_ >= 1.7.1 * `azure-mgmt-resource <https://pypi.python.org/pypi/azure-mgmt-resource>`_ >= 1.1.0 * `azure-mgmt-storage <https://pypi.python.org/pypi/azure-mgmt-storage>`_ >= 1.0.0 * `azure-mgmt-web <https://pypi.python.org/pypi/azure-mgmt-web>`_ >= 0.32.0 * `azure-storage <https://pypi.python.org/pypi/azure-storage>`_ >= 0.34.3 * `msrestazure <https://pypi.python.org/pypi/msrestazure>`_ >= 0.4.21 :platform: linux :configuration: This module requires Azure Resource Manager credentials to be passed as a dictionary of keyword arguments to the ``connection_auth`` parameter in order to work properly. Since the authentication parameters are sensitive, it's recommended to pass them to the states via pillar. Required provider parameters: if using username and password: * ``subscription_id`` * ``username`` * ``password`` if using a service principal: * ``subscription_id`` * ``tenant`` * ``client_id`` * ``secret`` Optional provider parameters: **cloud_environment**: Used to point the cloud driver to different API endpoints, such as Azure GovCloud. Possible values: * ``AZURE_PUBLIC_CLOUD`` (default) * ``AZURE_CHINA_CLOUD`` * ``AZURE_US_GOV_CLOUD`` * ``AZURE_GERMAN_CLOUD`` Example Pillar for Azure Resource Manager authentication: .. code-block:: yaml azurearm: user_pass_auth: subscription_id: 3287abc8-f98a-c678-3bde-326766fd3617 username: fletch password: 123pass mysubscription: subscription_id: 3287abc8-f98a-c678-3bde-326766fd3617 tenant: ABCDEFAB-1234-ABCD-1234-ABCDEFABCDEF client_id: ABCDEFAB-1234-ABCD-1234-ABCDEFABCDEF secret: XXXXXXXXXXXXXXXXXXXXXXXX cloud_environment: AZURE_PUBLIC_CLOUD Example states using Azure Resource Manager authentication: .. code-block:: jinja {% set profile = salt['pillar.get']('azurearm:mysubscription') %} Ensure resource group exists: azurearm_resource.resource_group_present: - name: my_rg - location: westus - tags: how_awesome: very contact_name: Elmer Fudd Gantry - connection_auth: {{ profile }} Ensure resource group is absent: azurearm_resource.resource_group_absent: - name: other_rg - connection_auth: {{ profile }} ''' # Import Python libs from __future__ import absolute_import import json import logging # Import Salt libs import salt.utils.files __virtualname__ = 'azurearm_resource' log = logging.getLogger(__name__) def __virtual__(): ''' Only make this state available if the azurearm_resource module is available. ''' return __virtualname__ if 'azurearm_resource.resource_group_check_existence' in __salt__ else False def resource_group_present(name, location, managed_by=None, tags=None, connection_auth=None, **kwargs): ''' .. versionadded:: 2019.2.0 Ensure a resource group exists. :param name: Name of the resource group. :param location: The Azure location in which to create the resource group. This value cannot be updated once the resource group is created. :param managed_by: The ID of the resource that manages this resource group. This value cannot be updated once the resource group is created. :param tags: A dictionary of strings can be passed as tag metadata to the resource group object. :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. Example usage: .. code-block:: yaml Ensure resource group exists: azurearm_resource.resource_group_present: - name: group1 - location: eastus - tags: contact_name: Elmer Fudd Gantry - connection_auth: {{ profile }} ''' ret = { 'name': name, 'result': False, 'comment': '', 'changes': {} } if not isinstance(connection_auth, dict): ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' return ret group = {} present = __salt__['azurearm_resource.resource_group_check_existence'](name, **connection_auth) if present: group = __salt__['azurearm_resource.resource_group_get'](name, **connection_auth) ret['changes'] = __utils__['dictdiffer.deep_diff'](group.get('tags', {}), tags or {}) if not ret['changes']: ret['result'] = True ret['comment'] = 'Resource group {0} is already present.'.format(name) return ret if __opts__['test']: ret['comment'] = 'Resource group {0} tags would be updated.'.format(name) ret['result'] = None ret['changes'] = { 'old': group.get('tags', {}), 'new': tags } return ret elif __opts__['test']: ret['comment'] = 'Resource group {0} would be created.'.format(name) ret['result'] = None ret['changes'] = { 'old': {}, 'new': { 'name': name, 'location': location, 'managed_by': managed_by, 'tags': tags, } } return ret group_kwargs = kwargs.copy() group_kwargs.update(connection_auth) group = __salt__['azurearm_resource.resource_group_create_or_update']( name, location, managed_by=managed_by, tags=tags, **group_kwargs ) present = __salt__['azurearm_resource.resource_group_check_existence'](name, **connection_auth) if present: ret['result'] = True ret['comment'] = 'Resource group {0} has been created.'.format(name) ret['changes'] = { 'old': {}, 'new': group } return ret ret['comment'] = 'Failed to create resource group {0}! ({1})'.format(name, group.get('error')) return ret def resource_group_absent(name, connection_auth=None): ''' .. versionadded:: 2019.2.0 Ensure a resource group does not exist in the current subscription. :param name: Name of the resource group. :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. ''' ret = { 'name': name, 'result': False, 'comment': '', 'changes': {} } if not isinstance(connection_auth, dict): ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' return ret group = {} present = __salt__['azurearm_resource.resource_group_check_existence'](name, **connection_auth) if not present: ret['result'] = True ret['comment'] = 'Resource group {0} is already absent.'.format(name) return ret elif __opts__['test']: group = __salt__['azurearm_resource.resource_group_get'](name, **connection_auth) ret['comment'] = 'Resource group {0} would be deleted.'.format(name) ret['result'] = None ret['changes'] = { 'old': group, 'new': {}, } return ret group = __salt__['azurearm_resource.resource_group_get'](name, **connection_auth) deleted = __salt__['azurearm_resource.resource_group_delete'](name, **connection_auth) if deleted: present = False else: present = __salt__['azurearm_resource.resource_group_check_existence'](name, **connection_auth) if not present: ret['result'] = True ret['comment'] = 'Resource group {0} has been deleted.'.format(name) ret['changes'] = { 'old': group, 'new': {} } return ret ret['comment'] = 'Failed to delete resource group {0}!'.format(name) return ret def policy_definition_present(name, policy_rule=None, policy_type=None, mode=None, display_name=None, description=None, metadata=None, parameters=None, policy_rule_json=None, policy_rule_file=None, template='jinja', source_hash=None, source_hash_name=None, skip_verify=False, connection_auth=None, **kwargs): ''' .. versionadded:: 2019.2.0 Ensure a security policy definition exists. :param name: Name of the policy definition. :param policy_rule: A YAML dictionary defining the policy rule. See `Azure Policy Definition documentation <https://docs.microsoft.com/en-us/azure/azure-policy/policy-definition#policy-rule>`_ for details on the structure. One of ``policy_rule``, ``policy_rule_json``, or ``policy_rule_file`` is required, in that order of precedence for use if multiple parameters are used. :param policy_rule_json: A text field defining the entirety of a policy definition in JSON. See `Azure Policy Definition documentation <https://docs.microsoft.com/en-us/azure/azure-policy/policy-definition#policy-rule>`_ for details on the structure. One of ``policy_rule``, ``policy_rule_json``, or ``policy_rule_file`` is required, in that order of precedence for use if multiple parameters are used. Note that the `name` field in the JSON will override the ``name`` parameter in the state. :param policy_rule_file: The source of a JSON file defining the entirety of a policy definition. See `Azure Policy Definition documentation <https://docs.microsoft.com/en-us/azure/azure-policy/policy-definition#policy-rule>`_ for details on the structure. One of ``policy_rule``, ``policy_rule_json``, or ``policy_rule_file`` is required, in that order of precedence for use if multiple parameters are used. Note that the `name` field in the JSON will override the ``name`` parameter in the state. :param skip_verify: Used for the ``policy_rule_file`` parameter. If ``True``, hash verification of remote file sources (``http://``, ``https://``, ``ftp://``) will be skipped, and the ``source_hash`` argument will be ignored. :param source_hash: This can be a source hash string or the URI of a file that contains source hash strings. :param source_hash_name: When ``source_hash`` refers to a hash file, Salt will try to find the correct hash by matching the filename/URI associated with that hash. :param policy_type: The type of policy definition. Possible values are NotSpecified, BuiltIn, and Custom. Only used with the ``policy_rule`` parameter. :param mode: The policy definition mode. Possible values are NotSpecified, Indexed, and All. Only used with the ``policy_rule`` parameter. :param display_name: The display name of the policy definition. Only used with the ``policy_rule`` parameter. :param description: The policy definition description. Only used with the ``policy_rule`` parameter. :param metadata: The policy definition metadata defined as a dictionary. Only used with the ``policy_rule`` parameter. :param parameters: Required dictionary if a parameter is used in the policy rule. Only used with the ``policy_rule`` parameter. :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. Example usage: .. code-block:: yaml Ensure policy definition exists: azurearm_resource.policy_definition_present: - name: testpolicy - display_name: Test Policy - description: Test policy for testing policies. - policy_rule: if: allOf: - equals: Microsoft.Compute/virtualMachines/write source: action - field: location in: - eastus - eastus2 - centralus then: effect: deny - connection_auth: {{ profile }} ''' ret = { 'name': name, 'result': False, 'comment': '', 'changes': {} } if not isinstance(connection_auth, dict): ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' return ret if not policy_rule and not policy_rule_json and not policy_rule_file: ret['comment'] = 'One of "policy_rule", "policy_rule_json", or "policy_rule_file" is required!' return ret if sum(x is not None for x in [policy_rule, policy_rule_json, policy_rule_file]) > 1: ret['comment'] = 'Only one of "policy_rule", "policy_rule_json", or "policy_rule_file" is allowed!' return ret if ((policy_rule_json or policy_rule_file) and (policy_type or mode or display_name or description or metadata or parameters)): ret['comment'] = 'Policy definitions cannot be passed when "policy_rule_json" or "policy_rule_file" is defined!' return ret temp_rule = {} if policy_rule_json: try: temp_rule = json.loads(policy_rule_json) except Exception as exc: ret['comment'] = 'Unable to load policy rule json! ({0})'.format(exc) return ret elif policy_rule_file: try: # pylint: disable=unused-variable sfn, source_sum, comment_ = __salt__['file.get_managed']( None, template, policy_rule_file, source_hash, source_hash_name, None, None, None, __env__, None, None, skip_verify=skip_verify, **kwargs ) except Exception as exc: ret['comment'] = 'Unable to locate policy rule file "{0}"! ({1})'.format(policy_rule_file, exc) return ret if not sfn: ret['comment'] = 'Unable to locate policy rule file "{0}"!)'.format(policy_rule_file) return ret try: with salt.utils.files.fopen(sfn, 'r') as prf: temp_rule = json.load(prf) except Exception as exc: ret['comment'] = 'Unable to load policy rule file "{0}"! ({1})'.format(policy_rule_file, exc) return ret if sfn: salt.utils.files.remove(sfn) policy_name = name if policy_rule_json or policy_rule_file: if temp_rule.get('name'): policy_name = temp_rule.get('name') policy_rule = temp_rule.get('properties', {}).get('policyRule') policy_type = temp_rule.get('properties', {}).get('policyType') mode = temp_rule.get('properties', {}).get('mode') display_name = temp_rule.get('properties', {}).get('displayName') description = temp_rule.get('properties', {}).get('description') metadata = temp_rule.get('properties', {}).get('metadata') parameters = temp_rule.get('properties', {}).get('parameters') policy = __salt__['azurearm_resource.policy_definition_get'](name, azurearm_log_level='info', **connection_auth) if 'error' not in policy: if policy_type and policy_type.lower() != policy.get('policy_type', '').lower(): ret['changes']['policy_type'] = { 'old': policy.get('policy_type'), 'new': policy_type } if (mode or '').lower() != policy.get('mode', '').lower(): ret['changes']['mode'] = { 'old': policy.get('mode'), 'new': mode } if (display_name or '').lower() != policy.get('display_name', '').lower(): ret['changes']['display_name'] = { 'old': policy.get('display_name'), 'new': display_name } if (description or '').lower() != policy.get('description', '').lower(): ret['changes']['description'] = { 'old': policy.get('description'), 'new': description } rule_changes = __utils__['dictdiffer.deep_diff'](policy.get('policy_rule', {}), policy_rule or {}) if rule_changes: ret['changes']['policy_rule'] = rule_changes meta_changes = __utils__['dictdiffer.deep_diff'](policy.get('metadata', {}), metadata or {}) if meta_changes: ret['changes']['metadata'] = meta_changes param_changes = __utils__['dictdiffer.deep_diff'](policy.get('parameters', {}), parameters or {}) if param_changes: ret['changes']['parameters'] = param_changes if not ret['changes']: ret['result'] = True ret['comment'] = 'Policy definition {0} is already present.'.format(name) return ret if __opts__['test']: ret['comment'] = 'Policy definition {0} would be updated.'.format(name) ret['result'] = None return ret else: ret['changes'] = { 'old': {}, 'new': { 'name': policy_name, 'policy_type': policy_type, 'mode': mode, 'display_name': display_name, 'description': description, 'metadata': metadata, 'parameters': parameters, 'policy_rule': policy_rule, } } if __opts__['test']: ret['comment'] = 'Policy definition {0} would be created.'.format(name) ret['result'] = None return ret # Convert OrderedDict to dict if isinstance(metadata, dict): metadata = json.loads(json.dumps(metadata)) if isinstance(parameters, dict): parameters = json.loads(json.dumps(parameters)) policy_kwargs = kwargs.copy() policy_kwargs.update(connection_auth) policy = __salt__['azurearm_resource.policy_definition_create_or_update']( name=policy_name, policy_rule=policy_rule, policy_type=policy_type, mode=mode, display_name=display_name, description=description, metadata=metadata, parameters=parameters, **policy_kwargs ) if 'error' not in policy: ret['result'] = True ret['comment'] = 'Policy definition {0} has been created.'.format(name) return ret ret['comment'] = 'Failed to create policy definition {0}! ({1})'.format(name, policy.get('error')) return ret def policy_definition_absent(name, connection_auth=None): ''' .. versionadded:: 2019.2.0 Ensure a policy definition does not exist in the current subscription. :param name: Name of the policy definition. :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. ''' ret = { 'name': name, 'result': False, 'comment': '', 'changes': {} } if not isinstance(connection_auth, dict): ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' return ret policy = __salt__['azurearm_resource.policy_definition_get'](name, azurearm_log_level='info', **connection_auth) if 'error' in policy: ret['result'] = True ret['comment'] = 'Policy definition {0} is already absent.'.format(name) return ret elif __opts__['test']: ret['comment'] = 'Policy definition {0} would be deleted.'.format(name) ret['result'] = None ret['changes'] = { 'old': policy, 'new': {}, } return ret deleted = __salt__['azurearm_resource.policy_definition_delete'](name, **connection_auth) if deleted: ret['result'] = True ret['comment'] = 'Policy definition {0} has been deleted.'.format(name) ret['changes'] = { 'old': policy, 'new': {} } return ret ret['comment'] = 'Failed to delete policy definition {0}!'.format(name) return ret def policy_assignment_present(name, scope, definition_name, display_name=None, description=None, assignment_type=None, parameters=None, connection_auth=None, **kwargs): ''' .. versionadded:: 2019.2.0 Ensure a security policy assignment exists. :param name: Name of the policy assignment. :param scope: The scope of the policy assignment. :param definition_name: The name of the policy definition to assign. :param display_name: The display name of the policy assignment. :param description: The policy assignment description. :param assignment_type: The type of policy assignment. :param parameters: Required dictionary if a parameter is used in the policy rule. :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. Example usage: .. code-block:: yaml Ensure policy assignment exists: azurearm_resource.policy_assignment_present: - name: testassign - scope: /subscriptions/bc75htn-a0fhsi-349b-56gh-4fghti-f84852 - definition_name: testpolicy - display_name: Test Assignment - description: Test assignment for testing assignments. - connection_auth: {{ profile }} ''' ret = { 'name': name, 'result': False, 'comment': '', 'changes': {} } if not isinstance(connection_auth, dict): ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' return ret policy = __salt__['azurearm_resource.policy_assignment_get']( name, scope, azurearm_log_level='info', **connection_auth ) if 'error' not in policy: if assignment_type and assignment_type.lower() != policy.get('type', '').lower(): ret['changes']['type'] = { 'old': policy.get('type'), 'new': assignment_type } if scope.lower() != policy['scope'].lower(): ret['changes']['scope'] = { 'old': policy['scope'], 'new': scope } pa_name = policy['policy_definition_id'].split('/')[-1] if definition_name.lower() != pa_name.lower(): ret['changes']['definition_name'] = { 'old': pa_name, 'new': definition_name } if (display_name or '').lower() != policy.get('display_name', '').lower(): ret['changes']['display_name'] = { 'old': policy.get('display_name'), 'new': display_name } if (description or '').lower() != policy.get('description', '').lower(): ret['changes']['description'] = { 'old': policy.get('description'), 'new': description } param_changes = __utils__['dictdiffer.deep_diff'](policy.get('parameters', {}), parameters or {}) if param_changes: ret['changes']['parameters'] = param_changes if not ret['changes']: ret['result'] = True ret['comment'] = 'Policy assignment {0} is already present.'.format(name) return ret if __opts__['test']: ret['comment'] = 'Policy assignment {0} would be updated.'.format(name) ret['result'] = None return ret else: ret['changes'] = { 'old': {}, 'new': { 'name': name, 'scope': scope, 'definition_name': definition_name, 'type': assignment_type, 'display_name': display_name, 'description': description, 'parameters': parameters, } } if __opts__['test']: ret['comment'] = 'Policy assignment {0} would be created.'.format(name) ret['result'] = None return ret if isinstance(parameters, dict): parameters = json.loads(json.dumps(parameters)) policy_kwargs = kwargs.copy() policy_kwargs.update(connection_auth) policy = __salt__['azurearm_resource.policy_assignment_create']( name=name, scope=scope, definition_name=definition_name, type=assignment_type, display_name=display_name, description=description, parameters=parameters, **policy_kwargs ) if 'error' not in policy: ret['result'] = True ret['comment'] = 'Policy assignment {0} has been created.'.format(name) return ret ret['comment'] = 'Failed to create policy assignment {0}! ({1})'.format(name, policy.get('error')) return ret def policy_assignment_absent(name, scope, connection_auth=None): ''' .. versionadded:: 2019.2.0 Ensure a policy assignment does not exist in the provided scope. :param name: Name of the policy assignment. :param scope: The scope of the policy assignment. connection_auth A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. ''' ret = { 'name': name, 'result': False, 'comment': '', 'changes': {} } if not isinstance(connection_auth, dict): ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' return ret policy = __salt__['azurearm_resource.policy_assignment_get']( name, scope, azurearm_log_level='info', **connection_auth ) if 'error' in policy: ret['result'] = True ret['comment'] = 'Policy assignment {0} is already absent.'.format(name) return ret elif __opts__['test']: ret['comment'] = 'Policy assignment {0} would be deleted.'.format(name) ret['result'] = None ret['changes'] = { 'old': policy, 'new': {}, } return ret deleted = __salt__['azurearm_resource.policy_assignment_delete'](name, scope, **connection_auth) if deleted: ret['result'] = True ret['comment'] = 'Policy assignment {0} has been deleted.'.format(name) ret['changes'] = { 'old': policy, 'new': {} } return ret ret['comment'] = 'Failed to delete policy assignment {0}!'.format(name) return ret