mirror of
https://github.com/saltstack/salt.git
synced 2025-04-16 09:40:20 +00:00
880 lines
28 KiB
Python
880 lines
28 KiB
Python
"""
|
|
Azure (ARM) Resource State Module
|
|
|
|
.. versionadded:: 2019.2.0
|
|
|
|
.. warning::
|
|
|
|
This cloud provider will be removed from Salt in version 3007 in favor of
|
|
the `saltext.azurerm Salt Extension
|
|
<https://github.com/salt-extensions/saltext-azurerm>`_
|
|
|
|
:maintainer: <devops@eitr.tech>
|
|
: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 json
|
|
import logging
|
|
from functools import wraps
|
|
|
|
import salt.utils.azurearm
|
|
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.
|
|
"""
|
|
if "azurearm_resource.resource_group_check_existence" in __salt__:
|
|
return __virtualname__
|
|
return (False, "azurearm_resource module could not be loaded")
|
|
|
|
|
|
def _deprecation_message(function):
|
|
"""
|
|
Decorator wrapper to warn about azurearm deprecation
|
|
"""
|
|
|
|
@wraps(function)
|
|
def wrapped(*args, **kwargs):
|
|
salt.utils.versions.warn_until(
|
|
"Chlorine",
|
|
"The 'azurearm' functionality in Salt has been deprecated and its "
|
|
"functionality will be removed in version 3007 in favor of the "
|
|
"saltext.azurerm Salt Extension. "
|
|
"(https://github.com/salt-extensions/saltext-azurerm)",
|
|
category=FutureWarning,
|
|
)
|
|
ret = function(*args, **salt.utils.args.clean_kwargs(**kwargs))
|
|
return ret
|
|
|
|
return wrapped
|
|
|
|
|
|
@_deprecation_message
|
|
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 {} is already present.".format(name)
|
|
return ret
|
|
|
|
if __opts__["test"]:
|
|
ret["comment"] = "Resource group {} 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 {} 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 {} has been created.".format(name)
|
|
ret["changes"] = {"old": {}, "new": group}
|
|
return ret
|
|
|
|
ret["comment"] = "Failed to create resource group {}! ({})".format(
|
|
name, group.get("error")
|
|
)
|
|
return ret
|
|
|
|
|
|
@_deprecation_message
|
|
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 {} is already absent.".format(name)
|
|
return ret
|
|
|
|
elif __opts__["test"]:
|
|
group = __salt__["azurearm_resource.resource_group_get"](
|
|
name, **connection_auth
|
|
)
|
|
|
|
ret["comment"] = "Resource group {} 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 {} has been deleted.".format(name)
|
|
ret["changes"] = {"old": group, "new": {}}
|
|
return ret
|
|
|
|
ret["comment"] = "Failed to delete resource group {}!".format(name)
|
|
return ret
|
|
|
|
|
|
@_deprecation_message
|
|
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: # pylint: disable=broad-except
|
|
ret["comment"] = "Unable to load policy rule json! ({})".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: # pylint: disable=broad-except
|
|
ret["comment"] = 'Unable to locate policy rule file "{}"! ({})'.format(
|
|
policy_rule_file, exc
|
|
)
|
|
return ret
|
|
|
|
if not sfn:
|
|
ret["comment"] = 'Unable to locate policy rule file "{}"!)'.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: # pylint: disable=broad-except
|
|
ret["comment"] = 'Unable to load policy rule file "{}"! ({})'.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 {} is already present.".format(name)
|
|
return ret
|
|
|
|
if __opts__["test"]:
|
|
ret["comment"] = "Policy definition {} 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 {} 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 {} has been created.".format(name)
|
|
return ret
|
|
|
|
ret["comment"] = "Failed to create policy definition {}! ({})".format(
|
|
name, policy.get("error")
|
|
)
|
|
return ret
|
|
|
|
|
|
@_deprecation_message
|
|
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 {} is already absent.".format(name)
|
|
return ret
|
|
|
|
elif __opts__["test"]:
|
|
ret["comment"] = "Policy definition {} 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 {} has been deleted.".format(name)
|
|
ret["changes"] = {"old": policy, "new": {}}
|
|
return ret
|
|
|
|
ret["comment"] = "Failed to delete policy definition {}!".format(name)
|
|
return ret
|
|
|
|
|
|
@_deprecation_message
|
|
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 {} is already present.".format(name)
|
|
return ret
|
|
|
|
if __opts__["test"]:
|
|
ret["comment"] = "Policy assignment {} 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 {} 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 {} has been created.".format(name)
|
|
return ret
|
|
|
|
ret["comment"] = "Failed to create policy assignment {}! ({})".format(
|
|
name, policy.get("error")
|
|
)
|
|
return ret
|
|
|
|
|
|
@_deprecation_message
|
|
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 {} is already absent.".format(name)
|
|
return ret
|
|
|
|
elif __opts__["test"]:
|
|
ret["comment"] = "Policy assignment {} 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 {} has been deleted.".format(name)
|
|
ret["changes"] = {"old": policy, "new": {}}
|
|
return ret
|
|
|
|
ret["comment"] = "Failed to delete policy assignment {}!".format(name)
|
|
return ret
|