Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/azure-cli-core/azure/cli/core/profiles/_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def default_api_version(self):
ResourceType.MGMT_RESOURCE_FEATURES: '2015-12-01',
ResourceType.MGMT_RESOURCE_LINKS: '2016-09-01',
ResourceType.MGMT_RESOURCE_LOCKS: '2016-09-01',
ResourceType.MGMT_RESOURCE_POLICY: '2019-09-01',
ResourceType.MGMT_RESOURCE_POLICY: '2020-09-01',
ResourceType.MGMT_RESOURCE_RESOURCES: '2020-10-01',
ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2019-11-01',
ResourceType.MGMT_RESOURCE_DEPLOYMENTSCRIPTS: '2020-10-01',
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ def test_policy_insights_remediation_management_group(self):
@AllowLargeResponse()
def test_policy_insights_remediation_complete(self):
self.kwargs.update({
'pan': '09d18943ace14946aff83c21',
'pan': '98904c39668a4f70804aef09',
Comment thread
robga marked this conversation as resolved.
'rg': 'az-cli-policy-insights-test',
'rn': self.create_random_name('azurecli-test-remediation', 40)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ def cf_policy_set_definitions(cli_ctx, _):
return _resource_policy_client_factory(cli_ctx).policy_set_definitions


def cf_policy_exemptions(cli_ctx, _):
return _resource_policy_client_factory(cli_ctx).policy_exemptions


def cf_management_locks(cli_ctx, _):
return _resource_lock_client_factory(cli_ctx).management_locks

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ def get_policy_assignment_completion_list(cmd, prefix, namespace, **kwargs): #
return [i.name for i in result]


@Completer
def get_policy_exemption_completion_list(cmd, prefix, namespace, **kwargs): # pylint: disable=unused-argument
policy_client = _resource_policy_client_factory(cmd.cli_ctx)
result = policy_client.policy_exemptions.list()
return [i.name for i in result]


@Completer
def get_providers_completion_list(cmd, prefix, namespace, **kwargs): # pylint: disable=unused-argument
rcf = _resource_client_factory(cmd.cli_ctx)
Expand Down
73 changes: 73 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 @@ -1760,6 +1760,79 @@
--definition-groups "[{ \\"name\\": \\"CostSaving\\" }, { \\"name\\": \\"Organizational\\" } ]"
"""

helps['policy exemption'] = """
type: group
short-summary: Manage resource policy exemptions.
"""

helps['policy exemption create'] = """
type: command
short-summary: Create a policy exemption.
examples:
- name: Create a policy exemption in default subscription.
text: |
az policy exemption create -n exemptTestVM \\
--policy-assignment "/subscriptions/mySubId/providers/Microsoft.Authorization/policyAssignments/limitVMSku" \\
--exemption-category "Waiver"
- name: Create a policy exemption in the resource group.
text: |
az policy exemption create -n exemptTestVM \\
--policy-assignment "/subscriptions/mySubId/providers/Microsoft.Authorization/policyAssignments/limitVMSku" \\
--exemption-category "Waiver" \\
--resource-group "myResourceGroup"
- name: Create a policy exemption in a management group.
text: |
az policy exemption create -n exemptTestVM \\
--policy-assignment "/providers/Microsoft.Management/managementGroups/myMG/providers/Microsoft.Authorization/policyAssignments/limitVMSku" \\
--exemption-category "Waiver" \\
--scope "/providers/Microsoft.Management/managementGroups/myMG"
"""

helps['policy exemption delete'] = """
type: command
short-summary: Delete a policy exemption.
examples:
- name: Delete a policy exemption.
text: |
az policy exemption delete --name MyPolicyExemption --resource-group "myResourceGroup"
crafted: true
"""

helps['policy exemption list'] = """
type: command
short-summary: List policy exemptions.
"""

helps['policy exemption show'] = """
type: command
short-summary: Show a policy exemption.
examples:
- name: Show a policy exemption.
text: |
az policy exemption show --name MyPolicyExemption --resource-group "myResourceGroup"
crafted: true
"""

helps['policy exemption update'] = """
type: command
short-summary: Update a policy exemption.
examples:
- name: Update a policy exemption.
text: |
az policy exemption update -n exemptTestVM \\
--exemption-category "Mitigated"
- name: Update a policy exemption in the resource group.
text: |
az policy exemption update -n exemptTestVM \\
--exemption-category "Mitigated" \\
--resource-group "myResourceGroup"
- name: Update a policy exemption in a management group.
text: |
az policy exemption update -n exemptTestVM \\
--exemption-category "Mitigated" \\
--scope "/providers/Microsoft.Management/managementGroups/myMG"
"""
Comment thread
robga marked this conversation as resolved.

helps['provider'] = """
type: group
short-summary: Manage resource providers.
Expand Down
20 changes: 18 additions & 2 deletions src/azure-cli/azure/cli/command_modules/resource/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def load_arguments(self, _):
from knack.arguments import ignore_type, CLIArgumentType

from azure.cli.command_modules.resource._completers import (
get_policy_completion_list, get_policy_set_completion_list, get_policy_assignment_completion_list,
get_policy_completion_list, get_policy_set_completion_list, get_policy_assignment_completion_list, get_policy_exemption_completion_list,
get_resource_types_completion_list, get_providers_completion_list)
from azure.cli.command_modules.resource._validators import (
validate_lock_parameters, validate_resource_lock, validate_group_lock, validate_subscription_lock, validate_metadata, RollbackAction,
Expand Down Expand Up @@ -187,7 +187,7 @@ def load_arguments(self, _):

with self.argument_context('policy assignment create', 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']))
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:
Expand Down Expand Up @@ -220,6 +220,22 @@ def load_arguments(self, _):
with self.argument_context('policy set-definition create', min_api='2017-06-01-preview', resource_type=ResourceType.MGMT_RESOURCE_POLICY) as c:
c.argument('name', options_list=['--name', '-n'], help='Name of the new policy set definition.')

with self.argument_context('policy exemption', min_api='2020-09-01', resource_type=ResourceType.MGMT_RESOURCE_POLICY) as c:
Comment thread
robga marked this conversation as resolved.
c.ignore('_subscription')
Comment thread
robga marked this conversation as resolved.
c.argument('name', options_list=['--name', '-n'], completer=get_policy_exemption_completion_list, help='Name of the policy exemption.')
c.argument('scope', help='Scope to which this policy exemption applies.')
c.argument('disable_scope_strict_match', options_list=['--disable-scope-strict-match', '-i'], action='store_true', help='Include policy exemptions either inherited from parent scope or at child scope.')
c.argument('display_name', help='Display name of the policy exemption.')
c.argument('description', help='Description of policy exemption.')
c.argument('exemption_category', options_list=['--exemption-category', '-e'], help='The policy exemption category of the policy exemption. Possible values are Waiver and Mitigated.')
Comment thread
robga marked this conversation as resolved.
Outdated
c.argument('policy_definition_reference_ids', nargs='+', options_list=['--policy-definition-reference-ids', '-r'], help='The policy definition reference ids to exempt in the initiative (policy set).')
Comment thread
robga marked this conversation as resolved.
c.argument('expires_on', help='"The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption.')
Comment thread
robga marked this conversation as resolved.
Outdated
c.argument('metadata', nargs='+', validator=validate_metadata, help='Metadata in space-separated key=value pairs.')

with self.argument_context('policy exemption create', min_api='2020-09-01', resource_type=ResourceType.MGMT_RESOURCE_POLICY) as c:
c.argument('name', options_list=['--name', '-n'], help='Name of the new policy exemption.')
c.argument('policy_assignment', options_list=['--policy-assignment', '-a'], help='The referenced policy assignment Id for the policy exemption.')

with self.argument_context('group') as c:
c.argument('tag', tag_type)
c.argument('tags', tags_type)
Expand Down
15 changes: 14 additions & 1 deletion src/azure-cli/azure/cli/command_modules/resource/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from azure.cli.core.commands.arm import handle_template_based_exception
from azure.cli.command_modules.resource._client_factory import (
cf_resource_groups, cf_providers, cf_features, cf_tags, cf_deployments,
cf_deployment_operations, cf_policy_definitions, cf_policy_set_definitions, cf_resource_links,
cf_deployment_operations, cf_policy_definitions, cf_policy_set_definitions, cf_policy_exemptions, cf_resource_links,
cf_resource_deploymentscripts, cf_resource_managedapplications, cf_resource_managedappdefinitions, cf_management_groups, cf_management_group_subscriptions, cf_resource_templatespecs)
from azure.cli.command_modules.resource._validators import process_deployment_create_namespace, process_ts_create_or_update_namespace, _validate_template_spec, _validate_template_spec_out

Expand Down Expand Up @@ -112,6 +112,12 @@ def load_command_table(self, _):
resource_type=ResourceType.MGMT_RESOURCE_POLICY
)

resource_policy_exemptions_sdk = CliCommandType(
operations_tmpl='azure.mgmt.resource.policy.operations#PolicyExemptionsOperations.{}',
client_factory=cf_policy_exemptions,
resource_type=ResourceType.MGMT_RESOURCE_POLICY
)

resource_lock_sdk = CliCommandType(
operations_tmpl='azure.mgmt.resource.locks.operations#ManagementLocksOperations.{}',
resource_type=ResourceType.MGMT_RESOURCE_LOCKS
Expand Down Expand Up @@ -387,6 +393,13 @@ def load_command_table(self, _):
g.custom_show_command('show', 'get_policy_setdefinition')
g.custom_command('update', 'update_policy_setdefinition')

with self.command_group('policy exemption', resource_policy_exemptions_sdk, resource_type=ResourceType.MGMT_RESOURCE_POLICY, min_api='2020-09-01') as g:
g.custom_command('create', 'create_policy_exemption')
g.custom_command('delete', 'delete_policy_exemption')
Comment thread
robga marked this conversation as resolved.
g.custom_command('list', 'list_policy_exemption')
g.custom_show_command('show', 'get_policy_exemption')
g.custom_command('update', 'update_policy_exemption')
Comment thread
robga marked this conversation as resolved.

with self.command_group('lock', resource_type=ResourceType.MGMT_RESOURCE_LOCKS) as g:
g.custom_command('create', 'create_lock')
g.custom_command('delete', 'delete_lock')
Expand Down
95 changes: 90 additions & 5 deletions src/azure-cli/azure/cli/command_modules/resource/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -2138,11 +2138,6 @@ def create_policy_assignment(cmd, policy=None, policy_set_definition=None,
invalid notscopes value: \'%s\'', id_arg)
return
assignment.not_scopes = kwargs_list
PolicySku = cmd.get_models('PolicySku')
policySku = PolicySku(name='A0', tier='Free')
if sku:
policySku = policySku if sku.lower() == 'free' else PolicySku(name='A1', tier='Standard')
assignment.sku = policySku

if cmd.supported_api_version(min_api='2018-05-01'):
if location:
Expand Down Expand Up @@ -2437,6 +2432,96 @@ def update_policy_setdefinition(cmd, policy_set_definition_name, definitions=Non
return policy_client.policy_set_definitions.create_or_update(policy_set_definition_name, parameters)


def create_policy_exemption(cmd, name, policy_assignment=None, exemption_category=None,
policy_definition_reference_ids=None, expires_on=None,
display_name=None, description=None, resource_group_name=None, scope=None,
metadata=None):
if policy_assignment is None:
raise CLIError('--policy_assignment is required')
if exemption_category is None:
raise CLIError('--exemption_category is required')
Comment thread
robga marked this conversation as resolved.
Outdated

policy_client = _resource_policy_client_factory(cmd.cli_ctx)
Comment thread
robga marked this conversation as resolved.
scope = _build_policy_scope(policy_client.config.subscription_id,
resource_group_name, scope)
PolicyExemption = cmd.get_models('PolicyExemption')
exemption = PolicyExemption(policy_assignment_id=policy_assignment, policy_definition_reference_ids=policy_definition_reference_ids,
exemption_category=exemption_category, expires_on=expires_on,
display_name=display_name, description=description, metadata=metadata)
createdExemption = policy_client.policy_exemptions.create_or_update(scope, name, exemption)
return createdExemption


def update_policy_exemption(cmd, name, exemption_category=None,
policy_definition_reference_ids=None, expires_on=None,
display_name=None, description=None, resource_group_name=None, scope=None,
metadata=None):
policy_client = _resource_policy_client_factory(cmd.cli_ctx)
Comment thread
robga marked this conversation as resolved.
scope = _build_policy_scope(policy_client.config.subscription_id,
resource_group_name, scope)
PolicyExemption = cmd.get_models('PolicyExemption')
exemption = policy_client.policy_exemptions.get(scope, name)
parameters = PolicyExemption(
policy_assignment_id=exemption.policy_assignment_id,
policy_definition_reference_ids=policy_definition_reference_ids if policy_definition_reference_ids is not None else exemption.policy_definition_reference_ids,
exemption_category=exemption_category if exemption_category is not None else exemption.exemption_category,
expires_on=expires_on if expires_on is not None else exemption.expires_on,
display_name=display_name if display_name is not None else exemption.display_name,
description=description if description is not None else exemption.description,
metadata=metadata if metadata is not None else exemption.metadata)
updatedExemption = policy_client.policy_exemptions.create_or_update(scope, name, parameters)
return updatedExemption


def delete_policy_exemption(cmd, name, resource_group_name=None, scope=None):
policy_client = _resource_policy_client_factory(cmd.cli_ctx)
Comment thread
robga marked this conversation as resolved.
scope = _build_policy_scope(policy_client.config.subscription_id,
resource_group_name, scope)
policy_client.policy_exemptions.delete(scope, name)


def get_policy_exemption(cmd, name, resource_group_name=None, scope=None):
policy_client = _resource_policy_client_factory(cmd.cli_ctx)
Comment thread
robga marked this conversation as resolved.
scope = _build_policy_scope(policy_client.config.subscription_id,
resource_group_name, scope)
return policy_client.policy_exemptions.get(scope, name)


def list_policy_exemption(cmd, disable_scope_strict_match=None, resource_group_name=None, scope=None):
from azure.cli.core.commands.client_factory import get_subscription_id
policy_client = _resource_policy_client_factory(cmd.cli_ctx)
Comment thread
robga marked this conversation as resolved.
_scope = _build_policy_scope(get_subscription_id(cmd.cli_ctx),
resource_group_name, scope)
id_parts = parse_resource_id(_scope)
subscription = id_parts.get('subscription')
resource_group = id_parts.get('resource_group')
resource_type = id_parts.get('child_type_1') or id_parts.get('type')
resource_name = id_parts.get('child_name_1') or id_parts.get('name')
management_group = _parse_management_group_id(scope)

if management_group:
result = policy_client.policy_exemptions.list_for_management_group(management_group_id=management_group, filter='atScope()')
elif all([resource_type, resource_group, subscription]):
namespace = id_parts.get('namespace')
parent_resource_path = '' if not id_parts.get('child_name_1') else (id_parts['type'] + '/' + id_parts['name'])
result = policy_client.policy_exemptions.list_for_resource(
resource_group, namespace,
parent_resource_path, resource_type, resource_name)
elif resource_group:
result = policy_client.policy_exemptions.list_for_resource_group(resource_group)
elif subscription:
result = policy_client.policy_exemptions.list()
elif scope:
raise CLIError('usage error `--scope`: must be a fully qualified ARM ID.')
else:
raise CLIError('usage error: --scope ARM_ID | --resource-group NAME')
Comment thread
robga marked this conversation as resolved.
Outdated

if not disable_scope_strict_match:
result = [i for i in result if i.id.lower().strip('/').startswith(_scope.lower().strip('/') + "/providers/microsoft.authorization/policyexemptions")]

return result


def _register_rp(cli_ctx, subscription_id=None):
rp = "Microsoft.Management"
import time
Expand Down
Loading