Skip to content

Commit

Permalink
Documentation Enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
apybar committed Jul 17, 2024
1 parent 32c6bab commit c2406d3
Show file tree
Hide file tree
Showing 11 changed files with 494 additions and 56 deletions.
100 changes: 98 additions & 2 deletions Docs/operational-scripts-documenting-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,79 @@ To utilize the schema add a ```$schema``` tag to the JSON file.

This schema is new in v7.4.x and may not be complete. Please let us know if we missed anything.

## Example Documentation Specification File using 'documentAllAssignments'

Each file must contain one or both documentation topics. This example file in the StarterKit has both topics. Element `pacEnvironment` references the Policy as Code environment in `global-settings.jsonc` defining the tenant and root scope where the custom Policies and Policy Sets are deployed.

## Example Documentation Specification File
* [`documentAssignments`](#assignment-documentation)
* [`documentPolicySets`](#policy-set-documentation)

```json
{
"documentAssignments": {
"documentAllAssignments": [
{
"enabled": true,
"pacEnvironment": "EPAC-Prod",
"skipPolicyAssignments": [],
"skipPolicyDefinitions": [
"/providers/microsoft.authorization/policysetdefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8" // Azure Security Benchmark v3
],
"overrideEnvironmentCategory": {
"DEV": [ // Any name will suffice - will be header of column, grouping the scopes within the array
"/providers/Microsoft.Management/managementGroups/DEV-1",
"/providers/Microsoft.Management/managementGroups/DEV-2"
],
"PROD": [
"/providers/Microsoft.Management/managementGroups/PROD-1",
"/providers/Microsoft.Management/managementGroups/PROD-2"
]
}
}
],
"documentationSpecifications": [
{
"fileNameStem": "contoso-policy-effects-across-environments",
"environmentCategories": [], // when using 'documentAllAssignments', this value will be overwritten
"title": "Contoso Policy effects"
}
]
},
"documentPolicySets": [
{
"pacEnvironment": "tenant",
"fileNameStem": "contoso-compliance-policy-sets",
"title": "Document interesting Policy Sets",
"policySets": [
{
"shortName": "ASB",
"id": "/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8" // Azure Security Benchmark v3
},
{
"shortName": "NIST 800-171",
"id": "/providers/Microsoft.Authorization/policySetDefinitions/03055927-78bd-4236-86c0-f36125a10dc9" // NIST SP 800-171 Rev. 2
},
{
"shortName": "NIST 800-53",
"id": "/providers/Microsoft.Authorization/policySetDefinitions/179d1daa-458f-4e47-8086-2a68d0d6c38f" // NIST SP 800-53 Rev. 5
},
{
"shortName": "ORG",
"id": "/providers/Microsoft.Management/managementGroups/Contoso-Root/providers/Microsoft.Authorization/policySetDefinitions/org-security-benchmark" // Organization Security Benchmark for Custom Policies
}
],
"environmentColumnsInCsv": [
"prod",
"test",
"dev",
"lab"
]
}
]
}
```

## Example Documentation Specification File using 'environmentCategories'

Each file must contain one or both documentation topics. This example file in the StarterKit has both topics. Element `pacEnvironment` references the Policy as Code environment in `global-settings.jsonc` defining the tenant and root scope where the custom Policies and Policy Sets are deployed.

Expand Down Expand Up @@ -179,7 +249,31 @@ Alternatively, you can set `markdownMaxParameterLength` to a maximum length. EPA

## Assignment Documentation

### Element `environmentCategories`
### OPTION 1: Element `documentAllAssignments`

Best used when **all** Policy Assignments need to be documented.

When enabled, this section lists all Policy Assignments across all scopes where Policy is directly assigned. In many organizations, the same Policies and effects are applied to multiple Management Groups and even Azure tenants with the parameters consistent, therefore there is an option to group scopes by environment category.

`documentAllAssignments` entry specifies:

* `enabled`: setting this value to "true" will enable the use of 'documentAllAssignments' and will overwrite 'environmentCategories' if the section exists within the file.
* `pacEnvironment`: references the Policy as Code environment in `global-settings.jsonc` defining the tenant and root scope where the Policies and Policy Sets are deployed.
* `skipPolicyAssignments`: list of Policy Assignment ID's used to define Policy Assinments that do not want to included in the output.
* `skipPolicyDefinitions`: list of Policy Definition and Policy Set ID's used to define Policy Assinments that do not want to included in the output.
* `overrideEnvironmentCategory`: list of custom defined Environment Categories that will overwrite the auto-generated values. By default, all Policy Assignment scopes are treated as an individual "Environment Category", therefore leverage this section to override these Environemnt Categories and create custom groupings. (For an example see [`Example Documentation Specification File using 'documentAllAssignments'`](#Example-Documentation-Specification-File-using-documentAllAssignments))

### Element `documentationSpecifications`

Each entry in the array defines a set of outputs:

* `fileNameStem`: the file name stem used to construct the filenames.
* `environmentCategories` listed as effect columns.
* `title`: Heading 1 text for Markdown.

### OPTION 2: Element `environmentCategories`

Best used when specific Policy Assignments need to be documented.

For any given environment category, such as `prod`, `test`, `dev`, this section lists Policy Assignments which are representative for those environments. In many organizations, the same Policies and effects are applied to multiple Management Groups and even Azure tenants with the parameters consistent by environment category.

Expand Down Expand Up @@ -243,6 +337,8 @@ Each entry in the array defines a set of outputs:
* Group Names
* Effects per `environmentCategory` and Policy Set with additional details on the origin of the effect.

* `Folder: services`: Individual Markdown files generated off of the main Policy Assignment Markdown file. These files are based on each "Service Category" and can be used as sub-pages with Azure DevOps Wiki pages.

## Policy Set Documentation

Compares multiple Policy Set definitions for Policy and effect overlap as Markdown and Excel (`.csv`) files.
Expand Down
3 changes: 2 additions & 1 deletion Docs/policy-exemptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ To utilize the schema add a ```$schema``` tag to the JSON file.
## 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.
> 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` or `Mitigated`.
Expand Down
67 changes: 40 additions & 27 deletions Schemas/policy-documentation-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,58 @@
"documentAssignments": {
"type": "object",
"properties": {
"environmentCategories": {
"documentAllAssignments": {
"type": "array",
"minItems": 1,
"maxItems": 1,
"items": [
{
"type": "object",
"properties": {
"pacEnvironment": {
"type": "string"
"enabled": {
"type": "boolean"
},
"environmentCategory": {
"pacEnvironment": {
"type": "string"
},
"scopes": {
"skipPolicyAssignments": {
"type": "array",
"minItems": 1,
"minItems": 0,
"items": [
{
"type": "string"
}
]
},
"representativeAssignments": {
"skipPolicyDefinitions": {
"type": "array",
"minItems": 1,
"minItems": 0,
"items": [
{
"type": "object",
"properties": {
"shortName": {
"type": "string"
},
"id": {
"type": "string"
}
]
},
"overrideEnvironmentCategory": {
"type": "object",
"minItems": 0,
"properties": {
"*": {
"type": "array",
"maxItems": 0,
"items": [
{
"type": "string"
}
},
"required": [
"shortName",
"id"
]
}
]
},
"required": []
}
}
},
"required": [
"enabled",
"pacEnvironment",
"environmentCategory",
"scopes",
"representativeAssignments"
"title"
]
}
]
Expand Down Expand Up @@ -108,9 +111,19 @@
]
}
},
"required": [
"environmentCategories",
"documentationSpecifications"
"anyOf": [
{
"required": [
"environmentCategories",
"documentationSpecifications"
]
},
{
"required": [
"documentAllAssignments",
"documentPolicySets"
]
}
]
},
"documentPolicySets": {
Expand Down
2 changes: 1 addition & 1 deletion Scripts/Helpers/Find-AzNonCompliantResources.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function Find-AzNonCompliantResources {
$query = "policyresources | where type == `"microsoft.policyinsights/policystates`" and properties.complianceState == `"NonCompliant`"$($effectFilter)"
}
else {
$query = "policyresources | where type == `"microsoft.policyinsights/policystates`" and properties.complianceState <> `"Compliant`"$($effectFilter)"
$query = "policyresources | where type == `"microsoft.policyinsights/policystates`""
}
Write-Information "Az Graph Query: '$query'"
$result = @() + (Search-AzGraphAllItems -Query $query -ProgressItemName "Policy compliance records")
Expand Down
5 changes: 5 additions & 0 deletions Scripts/Helpers/Get-AzPolicyAssignments.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ function Get-AzPolicyAssignments {
$scope = $resourceIdParts.scope
$policyResource.resourceIdParts = $resourceIdParts
$policyResource.scope = $scope
$policyResource.scopeType = $resourceIdParts.scopeType
$policyResource.scopeDisplayName = $ScopeTable.$scope.displayName
if ($policyResource.scopeDisplayName -eq $policyResource.tenantId) {
$policyResource.scopeDisplayName = "Tenant Root Group"
}
$policyResource.pacOwner = Confirm-PacOwner -ThisPacOwnerId $thisPacOwnerId -PolicyResource $policyResource -Scope $scope -ManagedByCounters $policyResourcesTable.counters.managedBy
if ($policyResource.identity -and $policyResource.identity.type -ne "None") {
$principalId = ""
Expand Down
23 changes: 17 additions & 6 deletions Scripts/Helpers/Get-AzPolicyResourcesDetails.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ function Get-AzPolicyResourcesDetails {
param (
[string] $PacEnvironmentSelector,
[hashtable] $PacEnvironment,
[hashtable] $CachedPolicyResourceDetails
[hashtable] $CachedPolicyResourceDetails,
[switch] $CollectAllPolicies
)

$policyResourceDetails = $null
Expand All @@ -14,11 +15,21 @@ function Get-AzPolicyResourcesDetails {
# New root scope found
$scopeTable = Build-ScopeTableForDeploymentRootScope -PacEnvironment $PacEnvironment
# $NoParallelProcessing = $true
$deployed = Get-AzPolicyResources `
-PacEnvironment $PacEnvironment `
-ScopeTable $scopeTable `
-SkipRoleAssignments `
-SkipExemptions
if ($CollectAllPolicies) {
$deployed = Get-AzPolicyResources `
-PacEnvironment $PacEnvironment `
-ScopeTable $scopeTable `
-SkipRoleAssignments `
-SkipExemptions `
-CollectAllPolicies
}
else {
$deployed = Get-AzPolicyResources `
-PacEnvironment $PacEnvironment `
-ScopeTable $scopeTable `
-SkipRoleAssignments `
-SkipExemptions
}

$policyResourceDetails = Convert-PolicyResourcesToDetails `
-AllPolicyDefinitions $deployed.policydefinitions.all `
Expand Down
30 changes: 23 additions & 7 deletions Scripts/Helpers/Get-PolicyAssignmentsDetails.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function Get-PolicyAssignmentsDetails {
else {
$allAssignments = $PolicyResourceDetails.policyassignments
$policySetsDetails = $PolicyResourceDetails.policySets
$policiesDetails = $PolicyResourceDetails.policies
if (!$allAssignments.ContainsKey($assignmentId)) {
Write-Error "Assignment '$assignmentId' does not exist or is not managed by EPAC." -ErrorAction Stop
}
Expand All @@ -45,17 +46,32 @@ function Get-PolicyAssignmentsDetails {
else {
Write-Error "Assignment '$assignmentId' uses an unknown Policy Set '$($policySetId)'. This should not be possible!" -ErrorAction Stop
}

$entry = @{
shortName = $shortName
itemId = $assignmentId
assignmentId = $assignmentId
policySetId = $policySetId
}
}
elseif ($policySetId.Contains("policyDefinitions", [StringComparison]::InvariantCultureIgnoreCase)) {
$combinedDetail = Get-DeepCloneAsOrderedHashtable $policiesDetails.$policySetId
$combinedDetail.assignmentId = $assignmentId
$combinedDetail.assignment = $assignment
$combinedDetail.policyDefinitionId = $policySetId
$null = $assignmentsDetailsHt.Add($assignmentId, $combinedDetail)
$entry = @{
shortName = $shortName
itemId = $assignmentId
assignmentId = $assignmentId
policyDefinitionId = $policySetId
policySetId = "N/A"
}
}
else {
Write-Error "Assignment '$assignmentId' must be an Policy Set assignment (not a Policy assignment)." -ErrorAction Stop
Write-Error "Assignment '$assignmentId' is not a Policy Set or Policy Definition. This should not be possible!" -ErrorAction Stop
}
}
$entry = @{
shortName = $shortName
itemId = $assignmentId
assignmentId = $assignmentId
policySetId = $policySetId
}
$null = $assignmentPolicySetArray.Add($entry)


Expand Down
Loading

0 comments on commit c2406d3

Please sign in to comment.