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

[ARM] Add 'az policy assignment non-compliance-message' #18158

Merged
merged 10 commits into from
Jun 11, 2021
47 changes: 47 additions & 0 deletions src/azure-cli/azure/cli/command_modules/resource/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -1521,6 +1521,15 @@
az policy assignment create --name myPolicy --policy {PolicyName} --enforcement-mode 'DoNotEnforce'
"""

helps['policy assignment update'] = """
type: command
short-summary: Update a resource policy assignment.
examples:
- name: Update a resource policy assignment's description.
text: |
az policy assignment update --name myPolicy --description 'My policy description'
"""

helps['policy assignment delete'] = """
type: command
short-summary: Delete a resource policy assignment.
Expand Down Expand Up @@ -1563,6 +1572,44 @@
crafted: true
"""

helps['policy assignment non-compliance-message'] = """
type: group
short-summary: Manage a policy assignment's non-compliance messages.
"""

helps['policy assignment non-compliance-message create'] = """
type: command
short-summary: Add a non-compliance message to a policy assignment.
examples:
- name: Add a non-compliance message to a policy assignment.
text: >
az policy assignment non-compliance-message create -g MyResourceGroup -n MyPolicyAssignment -m 'Resources must follow naming standards'
- name: Add a non-compliance message for a specific policy in an assigned policy set definition.
text: >
az policy assignment non-compliance-message create -g MyResourceGroup -n MyPolicySetAssignment -m 'Resources must use allowed SKUs' --policy-definition-reference-id SkuPolicyRefId
"""

helps['policy assignment non-compliance-message list'] = """
type: command
short-summary: List the non-compliance messages for a policy assignment.
examples:
- name: List the non-compliance messages for a policy assignment.
text: >
az policy assignment non-compliance-message list -g MyResourceGroup -n MyPolicyAssignment
"""

helps['policy assignment non-compliance-message delete'] = """
type: command
short-summary: Remove one or more non-compliance messages from a policy assignment.
examples:
- name: Remove non-compliance messages from a policy assignment that contain a specific message and no policy definition reference ID.
text: >
az policy assignment non-compliance-message delete -g MyResourceGroup -n MyPolicyAssignment -m 'Resources must follow naming standards'
- name: Remove non-compliance messages from a policy assignment that contain a specific message and a specific policy definition reference ID.
text: >
az policy assignment non-compliance-message delete -g MyResourceGroup -n MyPolicySetAssignment -m 'Resources must use allowed SKUs' --policy-definition-reference-id SkuPolicyRefId
"""

helps['policy assignment list'] = """
type: command
short-summary: List resource policy assignments.
Expand Down
23 changes: 14 additions & 9 deletions src/azure-cli/azure/cli/command_modules/resource/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,32 +183,37 @@ def load_arguments(self, _):
c.argument('scope', help='Scope to which this policy assignment applies.')
c.argument('disable_scope_strict_match', action='store_true', help='Include policy assignments either inherited from parent scope or at child scope.')
c.argument('display_name', help='Display name of the policy assignment.')
c.argument('description', help='Description of the policy assignment.', min_api='2016-12-01')
c.argument('policy', help='Name or id of the policy definition.', completer=get_policy_completion_list)

with self.argument_context('policy assignment create', resource_type=ResourceType.MGMT_RESOURCE_POLICY) as c:
c.argument('name', options_list=['--name', '-n'], help='Name of the new policy assignment.')
c.argument('params', options_list=['--params', '-p'], help='JSON formatted string or a path to a file or uri with parameter values of the policy rule.', type=file_type, completer=FilesCompleter(), min_api='2016-12-01')

with self.argument_context('policy assignment create', resource_type=ResourceType.MGMT_RESOURCE_POLICY, min_api='2017-06-01-preview') as c:
with self.argument_context('policy assignment', resource_type=ResourceType.MGMT_RESOURCE_POLICY, min_api='2017-06-01-preview') as c:
c.argument('policy_set_definition', options_list=['--policy-set-definition', '-d'], help='Name or id of the policy set definition.')
c.argument('sku', options_list=['--sku', '-s'], help='policy sku.', arg_type=get_enum_type(['free', 'standard']), deprecate_info=c.deprecate(hide=True))
c.argument('notscopes', options_list='--not-scopes', nargs='+')

with self.argument_context('policy assignment create', resource_type=ResourceType.MGMT_RESOURCE_POLICY, min_api='2018-05-01') as c:
c.argument('location', arg_type=get_location_type(self.cli_ctx), help='The location of the policy assignment. Only required when utilizing managed identity.')

with self.argument_context('policy assignment create', resource_type=ResourceType.MGMT_RESOURCE_POLICY, arg_group='Managed Identity', min_api='2018-05-01') as c:
with self.argument_context('policy assignment', resource_type=ResourceType.MGMT_RESOURCE_POLICY, arg_group='Managed Identity', min_api='2018-05-01') as c:
c.argument('assign_identity', nargs='*', validator=validate_msi, help="Assigns a system assigned identity to the policy assignment.")
c.argument('identity_scope', arg_type=identity_scope_type)
c.argument('identity_role', arg_type=identity_role_type)

with self.argument_context('policy assignment create', resource_type=ResourceType.MGMT_RESOURCE_POLICY, min_api='2019-06-01') as c:
with self.argument_context('policy assignment', resource_type=ResourceType.MGMT_RESOURCE_POLICY, min_api='2019-06-01') as c:
c.argument('enforcement_mode', options_list=['--enforcement-mode', '-e'], help='Enforcement mode of the policy assignment, e.g. Default, DoNotEnforce. Please visit https://aka.ms/azure-policyAssignment-enforcement-mode for more information.', arg_type=get_enum_type(EnforcementMode))

with self.argument_context('policy assignment create', resource_type=ResourceType.MGMT_RESOURCE_POLICY) as c:
c.argument('name', options_list=['--name', '-n'], help='Name of the new policy assignment.')

with self.argument_context('policy assignment create', resource_type=ResourceType.MGMT_RESOURCE_POLICY, min_api='2018-05-01') as c:
c.argument('location', arg_type=get_location_type(self.cli_ctx), help='The location of the policy assignment. Only required when utilizing managed identity.')

with self.argument_context('policy assignment identity', resource_type=ResourceType.MGMT_RESOURCE_POLICY, min_api='2018-05-01') as c:
c.argument('identity_scope', arg_type=identity_scope_type)
c.argument('identity_role', arg_type=identity_role_type)

with self.argument_context('policy assignment non-compliance-message', resource_type=ResourceType.MGMT_RESOURCE_POLICY, min_api='2020-09-01') as c:
c.argument('message', options_list=['--message', '-m'], help='Message that will be shown when a resource is denied by policy or evaluation details are inspected.')
c.argument('policy_definition_reference_id', options_list=['--policy-definition-reference-id', '-r'], help='Policy definition reference ID within the assigned initiative (policy set) that the message applies to.')

with self.argument_context('policy set-definition', min_api='2017-06-01-preview', resource_type=ResourceType.MGMT_RESOURCE_POLICY) as c:
c.argument('policy_set_definition_name', arg_type=existing_policy_set_definition_name_type)
c.argument('display_name', help='Display name of policy set definition.')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,12 +373,18 @@ def load_command_table(self, _):
g.custom_command('delete', 'delete_policy_assignment')
g.custom_command('list', 'list_policy_assignment')
g.custom_show_command('show', 'show_policy_assignment')
g.custom_command('update', 'update_policy_assignment')

with self.command_group('policy assignment identity', resource_type=ResourceType.MGMT_RESOURCE_POLICY, min_api='2018-05-01') as g:
g.custom_command('assign', 'set_identity')
g.custom_show_command('show', 'show_identity')
g.custom_command('remove', 'remove_identity')

with self.command_group('policy assignment non-compliance-message', resource_type=ResourceType.MGMT_RESOURCE_POLICY, min_api='2020-09-01') as g:
g.custom_command('create', 'create_policy_non_compliance_message')
g.custom_command('list', 'list_policy_non_compliance_message')
g.custom_command('delete', 'delete_policy_non_compliance_message')
pilor marked this conversation as resolved.
Show resolved Hide resolved

with self.command_group('policy definition', resource_policy_definitions_sdk, resource_type=ResourceType.MGMT_RESOURCE_POLICY) as g:
g.custom_command('create', 'create_policy_definition')
g.custom_command('delete', 'delete_policy_definition')
Expand Down
105 changes: 99 additions & 6 deletions src/azure-cli/azure/cli/command_modules/resource/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -2110,7 +2110,8 @@ def create_policy_assignment(cmd, policy=None, policy_set_definition=None,
name=None, display_name=None, params=None,
resource_group_name=None, scope=None, sku=None,
not_scopes=None, location=None, assign_identity=None,
identity_scope=None, identity_role='Contributor', enforcement_mode='Default'):
identity_scope=None, identity_role='Contributor', enforcement_mode='Default',
description=None):
"""Creates a policy assignment
:param not_scopes: Space-separated scopes where the policy assignment does not apply.
"""
Expand All @@ -2124,19 +2125,18 @@ def create_policy_assignment(cmd, policy=None, policy_set_definition=None,
params = _load_file_string_or_uri(params, 'params', False)

PolicyAssignment = cmd.get_models('PolicyAssignment')
assignment = PolicyAssignment(display_name=display_name, policy_definition_id=policy_id, scope=scope, enforcement_mode=enforcement_mode)
assignment = PolicyAssignment(display_name=display_name, policy_definition_id=policy_id, scope=scope, enforcement_mode=enforcement_mode, description=description)
assignment.parameters = params if params else None

if cmd.supported_api_version(min_api='2017-06-01-preview'):
if not_scopes:
kwargs_list = []
for id_arg in not_scopes.split(' '):
if parse_resource_id(id_arg):
id_parts = parse_resource_id(id_arg)
if id_parts.get('subscription') or _is_management_group_scope(id_arg):
kwargs_list.append(id_arg)
else:
logger.error('az policy assignment create error: argument --not-scopes: \
invalid notscopes value: \'%s\'', id_arg)
return
raise InvalidArgumentValueError("Invalid resource ID value in --not-scopes: '%s'" % id_arg)
assignment.not_scopes = kwargs_list

if cmd.supported_api_version(min_api='2018-05-01'):
Expand Down Expand Up @@ -2170,6 +2170,50 @@ def _build_identities_info(cmd, identities):
return ResourceIdentity(type=identity_type)


def update_policy_assignment(cmd, name=None, display_name=None, params=None,
resource_group_name=None, scope=None, sku=None,
not_scopes=None, enforcement_mode=None, description=None):
"""Updates a policy assignment
:param not_scopes: Space-separated scopes where the policy assignment does not apply.
"""
policy_client = _resource_policy_client_factory(cmd.cli_ctx)
subscription_id = get_subscription_id(cmd.cli_ctx)
scope = _build_policy_scope(subscription_id, resource_group_name, scope)
params = _load_file_string_or_uri(params, 'params', False)

existing_assignment = policy_client.policy_assignments.get(scope, name)
PolicyAssignment = cmd.get_models('PolicyAssignment')
assignment = PolicyAssignment(
display_name=display_name if display_name is not None else existing_assignment.display_name,
policy_definition_id=existing_assignment.policy_definition_id,
scope=existing_assignment.scope,
enforcement_mode=enforcement_mode if enforcement_mode is not None else existing_assignment.enforcement_mode,
metadata=existing_assignment.metadata,
parameters=params if params is not None else existing_assignment.parameters,
description=description if description is not None else existing_assignment.description)

if cmd.supported_api_version(min_api='2017-06-01-preview'):
kwargs_list = existing_assignment.not_scopes
if not_scopes:
kwargs_list = []
for id_arg in not_scopes.split(' '):
id_parts = parse_resource_id(id_arg)
if id_parts.get('subscription') or _is_management_group_scope(id_arg):
kwargs_list.append(id_arg)
else:
raise InvalidArgumentValueError("Invalid resource ID value in --not-scopes: '%s'" % id_arg)
assignment.not_scopes = kwargs_list

if cmd.supported_api_version(min_api='2018-05-01'):
assignment.location = existing_assignment.location
assignment.identity = existing_assignment.identity

if cmd.supported_api_version(min_api='2020-09-01'):
assignment.non_compliance_messages = existing_assignment.non_compliance_messages

return policy_client.policy_assignments.create(scope, name, assignment)


def delete_policy_assignment(cmd, name, resource_group_name=None, scope=None):
policy_client = _resource_policy_client_factory(cmd.cli_ctx)
subscription_id = get_subscription_id(cmd.cli_ctx)
Expand Down Expand Up @@ -2218,6 +2262,55 @@ def list_policy_assignment(cmd, disable_scope_strict_match=None, resource_group_
return result


def list_policy_non_compliance_message(cmd, name, scope=None, resource_group_name=None):
policy_client = _resource_policy_client_factory(cmd.cli_ctx)
subscription_id = get_subscription_id(cmd.cli_ctx)
scope = _build_policy_scope(subscription_id, resource_group_name, scope)
return policy_client.policy_assignments.get(scope, name).non_compliance_messages


def create_policy_non_compliance_message(cmd, name, message, scope=None, resource_group_name=None,
policy_definition_reference_id=None):
policy_client = _resource_policy_client_factory(cmd.cli_ctx)
subscription_id = get_subscription_id(cmd.cli_ctx)
scope = _build_policy_scope(subscription_id, resource_group_name, scope)

assignment = policy_client.policy_assignments.get(scope, name)

NonComplianceMessage = cmd.get_models('NonComplianceMessage')
created_message = NonComplianceMessage(message=message, policy_definition_reference_id=policy_definition_reference_id)
if not assignment.non_compliance_messages:
assignment.non_compliance_messages = []
assignment.non_compliance_messages.append(created_message)

return policy_client.policy_assignments.create(scope, name, assignment).non_compliance_messages


def delete_policy_non_compliance_message(cmd, name, message, scope=None, resource_group_name=None,
policy_definition_reference_id=None):
policy_client = _resource_policy_client_factory(cmd.cli_ctx)
subscription_id = get_subscription_id(cmd.cli_ctx)
scope = _build_policy_scope(subscription_id, resource_group_name, scope)

assignment = policy_client.policy_assignments.get(scope, name)

NonComplianceMessage = cmd.get_models('NonComplianceMessage')
message_to_remove = NonComplianceMessage(message=message, policy_definition_reference_id=policy_definition_reference_id)
if assignment.non_compliance_messages:
assignment.non_compliance_messages = [existingMessage for existingMessage in assignment.non_compliance_messages if not _is_non_compliance_message_equivalent(existingMessage, message_to_remove)]

return policy_client.policy_assignments.create(scope, name, assignment).non_compliance_messages


def _is_non_compliance_message_equivalent(first, second):
first_message = '' if first.message is None else first.message
seccond_message = '' if second.message is None else second.message
first_reference_id = '' if first.policy_definition_reference_id is None else first.policy_definition_reference_id
second_reference_id = '' if second.policy_definition_reference_id is None else second.policy_definition_reference_id

return first_message.lower() == seccond_message.lower() and first_reference_id.lower() == second_reference_id.lower()


def set_identity(cmd, name, scope=None, resource_group_name=None, identity_role='Contributor', identity_scope=None):
policy_client = _resource_policy_client_factory(cmd.cli_ctx)
subscription_id = get_subscription_id(cmd.cli_ctx)
Expand Down
Loading