Policy Exemptions
Tip
The changes implementing Option A below makes JSON files easier to read than CSV files. We recommend using Policy definition Ids or Names for new exemptions and JSON files instead of CSV files. Of course, CSV files are still supported. You may even mix and match the two formats in the same folder.
Exemption Folder Structure
Exemptions can be defined as JSON or CSV files (we recommend that you use CSV files). The names of the definition files don't matter. If multiple files exists in a folder, the lists from all the files are added together.
The pacEnvironment (see global-settings.jsonc) is represented with a folder structure under the folder policyExemptions, such as epac-dev, tenant, ... A missing folder indicates that the pacEnvironment's Exemptions are not managed by this solution. To extract existing exemptions, the operations script Get-AzExemptions.ps1 can be used to generate JSON and CSV files. The output may be used to start the Exemption definitions. This same output is also created when Extract existing Policy Resources from an Environment.
A typical folder structure might look like this:
Definitions
policyExemptions
epac-dev
<name>.csv of <name>.json
tenant
<name>.csv of <name>.json
JSON Schema
The GitHub repo contains a JSON schema which can be used in tools such as VS Code to provide code completion.
To utilize the schema add a $schema
tag to the JSON file.
{
"$schema": "https://raw.githubusercontent.com/Azure/enterprise-azure-policy-as-code/main/Schemas/policy-exemption-schema.json"
}
Defining Exemptions
Tip
In v10.0.0, exemptions can be defined by specifying the Policy definition Ids or Names instead of Policy Assignment Ids. This significantly reduces the complexity of defining exemptions for Policy Sets with overlapping Policy definitions.
Each exemption must define the following properties:
name
- unique name, we recommend a short human readable name.displayName
- descriptive name displayed on portal.exemptionCategory
-Waiver
orMitigated
.- Item to exempt; one of the following:
policyDefinitionId
, orpolicyDefinitionName
- exempts the Policy definition in all applicable Policy Assignments.policySetDefinitionId
, orpolicySetDefinitionName
- exempts all Policies in the Policy Set, or a subset ifpolicyDefinitionsReferenceIds
are specified.policyAssignmentId
to be exempted. For Assignments of a Policy Set, you may addpolicyDefinitionReferenceIds
to exempt specific Policies within the Policy Set.scope
orscopes
- Management Group, subscription, Resource Group or resource.- Optional
expiresOn
- empty or expiry date.assignmentScopeValidation
-Default
orDoNotValidate
resourceSelectors
- valid JSON (see JSON format below)metadata
- valid JSON (see JSON format below)
Metadata
You can use metadata
for additional information.
EPAC injects deployedBy
into the metadata
section. This is a string that identifies the deployment source. It defaults to epac/$pacOwnerId/$pacSelector
. You can override this value in global-settings.jsonc
Not recommended: Adding deployedBy
to the metadata
section in the Policy definition file will override the value for this Exemption only from global-settings.jsonc
or default value.
Specifying Policy or Policies to be Exempted
The following properties can be used to specify the Policy or Policies to be exempted.
Caution
assignmentScopeValidation
value DoNotValidate
only works for Option B. It may work for Option A and C in some cases, but it is not recommended. EPAC cannot gracefully handle this and display a specific error message. Instead, it will display a generic error message with the following text "Exemption entry $($entryNumber): No assignments found for scope $($currentScope), skipping entry."
.
Option A: Policy definition Ids or Names
Tip
We recommend using this option for new exemptions, except when exempting multiple Policies in a Policy Set.
It creates one exemption per Assignment containing the Policy definition (direct or indirect through a Policy Sets). policyDefinitionReferenceIds
must be empty (omitted). This is the simplest and most readable way to define exemptions. Specify one of the following:
policyDefinitionId
for built-in Policy definitions in the form"/providers/Microsoft.Authorization/policyDefinitions/00000000-0000-0000-0000-000000000000"
.policyDefinitionName
for custom Policy definitions. In CSV files specify the cell inassignmentReferenceId
as"policyDefinitions/{{policyDefinitionName}}"
.
Option B: Policy Assignment Id
It creates one exemption for the specified Policy Assignment. This is the traditional way of defining an Exemption. It is still useful for exempting multiple Policies in an assigned Policy Set with one exemption. Specify the following:
policyAssignmentId
for the Policy Assignment in the form/providers/Microsoft.Management/managementGroups/{{managementGroupId}}/providers/Microsoft.Authorization/policyAssignments/{{policyAssignmentName}}
.- Optionally, for Policy Set Assignments only,
policyDefinitionReferenceIds
containing an array of strings as detailed below.
In CSV files, the column policyAssignmentId
is still supported for backward compatibility for Option B only.
Tip
We recommend using the column assignmentReferenceId
for every options, including option B.
Option C: Policy Set definition Ids or Names
It creates one exemption per Assignment assigning the Policy Set definition. It is useful for exempting multiple Policies in a Policy Set with one exemption. Specify the following:
policySetDefinitionId
for built-in Policy Set definitions in the form"/providers/Microsoft.Authorization/policySetDefinitions/00000000-0000-0000-0000-000000000000"
.policySetDefinitionName
for custom Policy Set definitions. In CSV files specify the cell inassignmentReferenceId
as"policySetDefinitions/{{policySetDefinitionName}}"
.- Optionally, for Policy Set Assignments only,
policyDefinitionReferenceIds
containing an array of strings as detailed below.
In CSV files use the column assignmentReferenceId
, and optionally policyDefinitionReferenceIds
.
Specifying policyDefinitionReferenceIds
policyDefinitionReferenceIds
is used to exempt specific Policies within a Policy Set. It is only used explicitly with policyAssignmentId
and policySetDefinitionId
or policySetDefinitionName
. For policyDefinitionId
and policyDefinitionName
, it is calculated by EPAC and should be empty.
policyDefinitionReferenceIds
is an array of strings. Each string can be one of the following:
policyDefinitionReferenceId
as specified in the Policy Set definition.policyDefinitionId
for built-in Policy definitions in the form"/providers/Microsoft.Authorization/policyDefinitions/00000000-0000-0000-0000-000000000000"
.policyDefinitionName
for custom Policy definitions in the form"policyDefinitions/{{policyDefinitionName}}"
.
In CSV files, policyDefinitionReferenceIds
is a list of ampersand &
separated strings. In JSON files, it is an array of strings.
Defining the Scope with scope
or scopes
The scope
property is used to define a single scope. The scopes
property is used to define multiple scopes. scopes
was introduced in v10.1.0.
Tip
Using a scopes
array creates nicely concatenated values for displayName
and description
for single scope. We recommend to always use scopes
. You can suppress the concatenation by adding a colon :
at the beginning of each string before the scope.
scope
defines a single scope
It is unchanged from previous versions.
scopes
Defines multiple Scopes in a single Entry
A list of Management Groups, subscriptions, Resource Groups or resource Ids. In CSV files it is a list separated by an ampersand &
. In JSON files it is an array of strings.
The last part of the scope is used as a postfix in the exemption displayName
and description
to make it easier to identify the scope. This behavior can be overridden by:
- Adding a human readable name followed by a colon
:
before the scope:humanReadableName:/subscriptions/11111111-2222-3333-4444-555555555555
. - Adding just a colon
:
before the scope to suppress the concatenation::/subscriptions/11111111-2222-3333-4444-555555555555
.
In CSV files, the scope
column is still supported for backward compatibility. We recommend using the scopes
column for all new exemptions. scopes
is a list of ampersand &
separated strings.
In JSON files, scope
is a string and scopes
is an array of strings.
Combining Policy Definitions at multiple Scopes
When using Option A or Option C and/or scopes
, EPAC needs to generate concatenated values for name
, displayName
, and description
to ensure uniqueness and readability.
name
is generated by concatenating thename
with a dash-
and the Assignmentname
(the last part of thepolicyAssignmentId
).displayName
anddescription
are generated by concatenating thedisplayName
anddescription
with a a space dash space (-
), the last part of the scope, or the human readable name before the colon:
(if usingscopes
).
It is best to explain the details with examples. They are based on JSON files, but the same principles apply to CSV files.
Example with policyDefinition
and scopes
Definition file:
{
"exemptions": [
{
"name": "short-name",
"displayName": "Descriptive name displayed on portal",
"description": "More details",
"exemptionCategory": "Waiver",
"scopes": [
"/subscriptions/11111111-2222-3333-4444-555555555555",
"/subscriptions/11111111-2222-3333-4444-555555555556/resourceGroups/resourceGroupName1",
],
"policyDefinitionId": "/providers/microsoft.authorization/policyDefinitions/00000000-0000-0000-0000-000000000000",
}
]
}
Generated fields for each assignment with the Policy specified:
name
is the same for all the scopes: "short-name-assignmentName"displayName
: "Descriptive name displayed on portal - 11111111-2222-3333-4444-555555555555 - assignmentName"displayName
: "Descriptive name displayed on portal - resourceGroupName1 - assignmentName"description
: "More details - 11111111-2222-3333-4444-555555555555 - assignmentName"description
: "More details - resourceGroupName1 - assignmentName"
Example with policyDefinition
, scopes
and a human readable name
Definition file:
{
"exemptions": [
{
"name": "short-name",
"displayName": "Descriptive name displayed on portal",
"description": "More details",
"exemptionCategory": "Waiver",
"scopes": [
"humanReadableName:/subscriptions/11111111-2222-3333-4444-555555555555",
"/subscriptions/11111111-2222-3333-4444-555555555556/resourceGroups/resourceGroupName1",
],
"policyDefinitionId": "/providers/microsoft.authorization/policyDefinitions/00000000-0000-0000-0000-000000000000",
}
]
}
Generated fields for each assignment with the Policy specified:
name
is the same for all the scopes: "short-name-assignmentName"displayName
: "Descriptive name displayed on portal - humanReadableName - assignmentName - humanReadableName"displayName
: "Descriptive name displayed on portal - resourceGroupName1 - assignmentName "description
: "More details - humanReadableName - assignmentName"description
: "More details - resourceGroupName1 - assignmentName"
Example with policyDefinition
and a single scope
Definition file:
{
"exemptions": [
{
"name": "short-name",
"displayName": "Descriptive name displayed on portal",
"description": "More details",
"exemptionCategory": "Waiver",
"scope": "/subscriptions/11111111-2222-3333-4444-555555555555/resourceGroups/resourceGroupName1",
"policyDefinitionId": "/providers/microsoft.authorization/policyDefinitions/00000000-0000-0000-0000-000000000000",
}
]
}
Generated fields for the assignment with the Policy specified:
name
: "short-name-assignmentName"displayName
: "Descriptive name displayed on portal - assignmentName"description
: "More details - assignmentName"
Example with policyAssignmentId
and scopes
Definition file:
{
"exemptions": [
{
"name": "short-name",
"displayName": "Descriptive name displayed on portal",
"description": "More details",
"exemptionCategory": "Waiver",
"scopes": [
"/subscriptions/11111111-2222-3333-4444-555555555555",
"/subscriptions/11111111-2222-3333-4444-555555555556/resourceGroups/resourceGroupName1",
],
"policyAssignmentId": "/providers/Microsoft.Management/managementGroups/{{managementGroupId}}/providers/Microsoft.Authorization/policyAssignments/{{policyAssignmentName}}",
}
]
}
Generated fields for this assignment:
name
: "short-name"displayName
: "Descriptive name displayed on portal - 11111111-2222-3333-4444-555555555555displayName
: "Descriptive name displayed on portal - resourceGroupName1"description
: "More details - 11111111-2222-3333-4444-555555555555"description
: "More details - resourceGroupName1"
Example with policyAssignmentId
, scopes
and a human readable name
Definition file:
{
"exemptions": [
{
"name": "short-name",
"displayName": "Descriptive name displayed on portal",
"description": "More details",
"exemptionCategory": "Waiver",
"scopes": [
"humanReadableName:/subscriptions/11111111-2222-3333-4444-555555555555",
"/subscriptions/11111111-2222-3333-4444-555555555556/resourceGroups/resourceGroupName1",
],
"policyAssignmentId": "/providers/Microsoft.Management/managementGroups/{{managementGroupId}}/providers/Microsoft.Authorization/policyAssignments/{{policyAssignmentName}}",
}
]
}
Generated fields for this assignment:
name
: "short-name"displayName
: "Descriptive name displayed on portal - humanReadableName"displayName
: "Descriptive name displayed on portal - resourceGroupName1"description
: "More details - humanReadableName"description
: "More details - resourceGroupName1"
Example with policyAssignmentId
and a single scope
Definition file:
{
"exemptions": [
{
"name": "short-name",
"displayName": "Descriptive name displayed on portal",
"description": "More details",
"exemptionCategory": "Waiver",
"scope": "/subscriptions/11111111-2222-3333-4444-555555555555/resourceGroups/resourceGroupName1",
"policyAssignmentId": "/providers/Microsoft.Management/managementGroups/{{managementGroupId}}/providers/Microsoft.Authorization/policyAssignments/{{policyAssignmentName}}",
}
]
}
Generated fields for this assignment:
name
: "short-name"displayName
: "Descriptive name displayed on portal"description
: "More details"
CSV Format
The columns must have the headers as described below. The order of the columns is not important.
Regular Columns
name
- unique name, we recommend a GUID.displayName
- descriptive name displayed on portal.exemptionCategory
-Waiver
orMitigated
.scope
- individual Management Group, subscription, Resource Group or resource.scopes
- list of ampersand&
separated Management Groups, subscriptions, Resource Groups or resource Ids. Ampersand is used instead of commas since it is not a valid character in a scope name and therefore doesn't conflict.- Optional
expiresOn
- empty or expiry date.policyDefinitionReferenceIds
- list of ampersand&
separated strings as defined above.assignmentScopeValidation
-Default
orDoNotValidate
resourceSelectors
- valid JSON (see JSON format below)metadata
- valid JSON (see JSON format below)
Caution
Breaking change: v10.1.0 replaced the usual comma in policyDefinitionReferenceIds
with an ampersand &
to avoid conflicts with the scope Ids. You must replace in-cell commas with ampersands.
Option A columns: Policy definition Ids or Names
- Column
assignmentReferenceId
must be formatted: - For Built-in Policy definition:
/providers/Microsoft.Authorization/policyDefinitions/00000000-0000-0000-0000-000000000000
- For Custom Policy definition:
policyDefinitions/{{policyDefinitionName}}
- Column
policyDefinitionReferenceIds
must be empty
Option B columns: Policy Assignment Id
- Column
assignmentReferenceId
must be a Policy Assignment Id: /providers/Microsoft.Management/managementGroups/{{managementGroupId}}/providers/Microsoft.Authorization/policyAssignments/{{policyAssignmentName}}
- Column
policyDefinitionReferenceIds
must be an ampersand separated list containing any of the following: - Empty for Policy Assignment of a single Policy, or to exempt the scope from every Policy in the assigned Policy Set
- One of the options as detailed above
Legacy column policyAssignmentId
is still supported for backward compatibility for Option B only.
Option C columns: Policy Set definition Ids or Names
- Column
assignmentReferenceId
must be a Policy Set definition Id or Name - Built-in Policy Set definition:
/providers/Microsoft.Authorization/policySetDefinitions/00000000-0000-0000-0000-000000000000
- Custom Policy Set definition:
policySetDefinitions/{{policySetDefinitionName}}
- Column
policyDefinitionReferenceIds
must be an ampersand separated list containing any of the following: - Empty to exempt all Policies in the Policy Set
- One of the options as detailed above
Moving from Excluded Scopes to Exemptions
If you are moving from using excluded scopes to the use of exemptions the by default EPAC will not deploy new exemptions that are part of an assignment excluded scopes. As well as this - EPAC will delete any exemption if finds that is deployed to an excluded scope.
You can override this behavior by using the switch -SkipNotScopedExemptions
when you call Build-DeploymentPlans
.