Skip to content

Commit

Permalink
Merge pull request #62 from Azure/feature/heinrich/autodoc
Browse files Browse the repository at this point in the history
Policy autodoc feature refinement
  • Loading branch information
techlake authored Aug 30, 2022
2 parents 937f29d + 1c9155b commit 6c6cc67
Show file tree
Hide file tree
Showing 24 changed files with 234 additions and 285 deletions.
6 changes: 3 additions & 3 deletions Definitions/Assignments/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ The components required for **creating / updating / deleting Policy assignments

| Component | What is it used for? | Where can it be found? |
|--|--|--|
| **Assignment Json files** | The assignments Json file follows the management group hierarchy (optionally including subscriptions and resource groups) and defines all policy and initiative assignments on these scopes. | `Definitions/Assignments` folder |
| **Assignment JSON files** | The assignments JSON file follows the management group hierarchy (optionally including subscriptions and resource groups) and defines all policy and initiative assignments on these scopes. | `Definitions/Assignments` folder |
| **Global Settings File** | The `global-settings.jsonc` file specifies common values for Policy Assignments | `Definitions` folder |

<br/>

## Assignment structure

Assignment Json is hierarchical for efficient definitions, avoiding duplication of Json with copy/paste
Assignment JSON is hierarchical for efficient definitions, avoiding duplication of JSON with copy/paste

**Note:** the tee is not required to be balanced. The number of levels is not restricted; however, anything beyond 5 levels is unnecessary in real scenarios and would be difficult to read and manage.

Expand All @@ -52,7 +52,7 @@ Assignment Json is hierarchical for efficient definitions, avoiding duplication
| `ignoreBranch` | Ignore the rest of the tee staring at this node. Can be used to define future assignments without deploying the assignments. | Any node: overrides are ignored. |
| `enforcementMode` | Similar to `ignoreBranch`, it deploys the assignment and sets the assignment to `Default` or `DoNotEnforce`. `DoNotEnforce` allows a what if analysis. | Any node: overrides previous setting |
| `additionalRoleAssignments` | `roleDefinitionIds` are calculated from the included (direct or indirect via Initiative) Policy definition(s). Fo some Policies, such as DINE `diagnosticsSettings` the monitor destination might be in a different branch of the Management Group tree from the Assignment. This field specifies any roles requiring assignments in that MG branch. The value is an array, each element containing two items: `roleDefinitionId` and `scope` | Union of all the `additionalRoleAssignments` defined in this branch |
| Option 1: `definitionEntry` | Specify the `policyName` or `initiativeName` for the assignment. The name should not be a fully qualified `id`. `friendlyNameToDocumentIfGuid` is purely used as a comment to make the Json more readable if the name is a GUID (optional). | Either option 1 or option 2 must exist exactly once in each branch of the tree. |
| Option 1: `definitionEntry` | Specify the `policyName` or `initiativeName` for the assignment. The name should not be a fully qualified `id`. `friendlyNameToDocumentIfGuid` is purely used as a comment to make the JSON more readable if the name is a GUID (optional). | Either option 1 or option 2 must exist exactly once in each branch of the tree. |
| Option 2: `definitionEntryList` | List of definitions to assign - creates one assignment per list entry for each tree branch. Each entry must specify a `policyName` or `initiativeName` and may specify `friendlyNameToDocumentIfGuid`. A nested `assignment` must be included to differentiate the multiple assignments being created from a `definitionEntryList`. This `assignment` structure may include an `append` boolean field to indicate that the fields should be appended instead of (default) concatenated first. | Either option 1 or option 2 must exist exactly once in each branch of the tree. |

## Details for `scope` and `notScope`
Expand Down
158 changes: 102 additions & 56 deletions Definitions/Documentation/README.md

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions Definitions/Exemptions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

## Exemption Files

Exemptions can be defined as Json or CSV files. The names of the definition files don't matter.
Exemptions can be defined as JSON or CSV files. The names of the definition files don't matter.

Additionally, through the use of a third-party PowerShell module from the PowerShell Gallery `ImportExcel` (https://www.powershellgallery.com/packages/ImportExcel, https://github.com/dfinke/ImportExcel/tree/master/Public). The contributors to this project are not responsible for any issues with that module. To mitigate the risk, the StarterKit has commented out the use of the conversion to protect your system from any vulnerabilities and executes the script without an Azure login.

The pacEnvironment (see global-settings.jsonc) is represented with a folder, such as dev, test, tenant, ... A missing folder indicates that the pacEnvironment's Exemptions are managed by this solution. To extract existing extension, the operations script Get-AzExemptions.ps1 can be used to generate Json and CSV files. The output should be used to start the Exemption definitions.
The pacEnvironment (see global-settings.jsonc) is represented with a folder, such as dev, test, tenant, ... A missing folder indicates that the pacEnvironment's Exemptions are managed by this solution. To extract existing extension, the operations script Get-AzExemptions.ps1 can be used to generate JSON and CSV files. The output should be used to start the Exemption definitions.

### JSON Format

Expand Down Expand Up @@ -41,9 +41,9 @@ The pacEnvironment (see global-settings.jsonc) is represented with a folder, suc

### CSV/XLSX Format
If you use spreadsheets (.csv or .xlsx):
- Column headers must be exactly as the Json labels above.
- Column headers must be exactly as the JSON labels above.
- `policyDefinitionReferenceIds` use comma separated list within each cell.
- `metadata` cells must contain valid Json.
- `metadata` cells must contain valid JSON.

<br/>

Expand Down
2 changes: 1 addition & 1 deletion Definitions/Initiatives/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

## Initiative (Policy Set) Definition Files

The names of the definition Json files don't matter, the Initiative definitions are registered based on the `name` attribute. It is recommended that you use a `GUID` as the `name`. The solution also allows the use of Json with comments by using `.jsonc` instead of `.json` for the file extension.
The names of the definition JSON files don't matter, the Initiative definitions are registered based on the `name` attribute. It is recommended that you use a `GUID` as the `name`. The solution also allows the use of JSON with comments by using `.jsonc` instead of `.json` for the file extension.

> **NOTE**:
> When authoring policy/initiative definitions, check out the [Maximum count of Azure Policy objects](https://docs.microsoft.com/en-us/azure/governance/policy/overview#maximum-count-of-azure-policy-objects)
Expand Down
2 changes: 1 addition & 1 deletion Definitions/Policies/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

## Policy Definition Files

The names of the definition Json files don't matter, the Policy and Initiative definitions are registered based on the `name` attribute. It is recommended that you use a `GUID` as the `name`. The solution also allows the use of Json with comments by using `.jsonc` instead of `.json` for the file extension.
The names of the definition JSON files don't matter, the Policy and Initiative definitions are registered based on the `name` attribute. It is recommended that you use a `GUID` as the `name`. The solution also allows the use of JSON with comments by using `.jsonc` instead of `.json` for the file extension.

> **NOTE**:
> When authoring policy/initiative definitions, check out the [Maximum count of Azure Policy objects](https://docs.microsoft.com/en-us/azure/governance/policy/overview#maximum-count-of-azure-policy-objects)
Expand Down
4 changes: 2 additions & 2 deletions Pipeline/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ Create service connections for each of your environments and require minimum rol

### Build-AzPoliciesInitiativesAssignmentsPlan.ps1

Analyzes changes in policy, initiative, and assignment files. It calculates a plan to apply deltas. The deployment scripts are **declarative** and **idempotent**: this means, that regardless how many times they are run, they always push all changes that were implemented in the Json files to the Azure environment, i.e. if a Json file is newly created/updated/deleted, the pipeline will create/update/delete the Policy and/or Initiative definition in Azure. If there are no changes, the pipeline can be run any number of times, as it won't make any changes to Azure.
Analyzes changes in policy, initiative, and assignment files. It calculates a plan to apply deltas. The deployment scripts are **declarative** and **idempotent**: this means, that regardless how many times they are run, they always push all changes that were implemented in the JSON files to the Azure environment, i.e. if a JSON file is newly created/updated/deleted, the pipeline will create/update/delete the Policy and/or Initiative definition in Azure. If there are no changes, the pipeline can be run any number of times, as it won't make any changes to Azure.

In addition to the [common parameters](#common-parameters-for-flexible-and-unified-definitions), these parameters are defined:

Expand Down Expand Up @@ -146,7 +146,7 @@ Creates the role assignments for the Managed Identities required for `DeployIfNo

## Consuming Excel Files

Exemptions and assignments can use Json, CSV and Excel (.xlsx) files. Support for Excel files uses a third-party PowerShell module from the PowerShell Gallery. However, the StarterKit pipeline disables the use of .xslx files module (`Convert-XlsToCSV.ps1`) to mitigate potential vulnerability risks in a third-party utility (this does not imply any such vulnerabilities exist). You can enable it at your own risk by uncommenting the sections in each planning stage. The pipeline further mitigates the risk by executing this step without Azure credentials.
Exemptions and assignments can use JSON, CSV and Excel (.xlsx) files. Support for Excel files uses a third-party PowerShell module from the PowerShell Gallery. However, the StarterKit pipeline disables the use of .xslx files module (`Convert-XlsToCSV.ps1`) to mitigate potential vulnerability risks in a third-party utility (this does not imply any such vulnerabilities exist). You can enable it at your own risk by uncommenting the sections in each planning stage. The pipeline further mitigates the risk by executing this step without Azure credentials.

<br/>

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ The Policy as Code framework supports the following Policy and Initiative assign
> **NOTE**: Distributed teams must only include those scopes in their version of the assignments.json that is not covered by another team.
- **Mixed approach**: A centralized team manages policy and initiative assignments to a certain level (top-down approach), e.g. on the Tenant Root Group level, and top level Management group, and all assignments on lower levels (i.e. lower level Management Groups, Subscriptions and Resource Groups) are managed by multiple teams, in a distributed manner.

**NOTE**: This solution enforces a centralized approach. It is recommended that you follow a centralized approach however, when using the mixed approach, scopes that will not be managed by the central team should be excluded from the assignments Json file - therefore the assignment configuration script will ignore these scopes (it won't add/remove/update anything in there). Conversely, the distributed teams must only include those scopes in their version of the assignments.json that is not covered by the central team.
**NOTE**: This solution enforces a centralized approach. It is recommended that you follow a centralized approach however, when using the mixed approach, scopes that will not be managed by the central team should be excluded from the assignments JSON file - therefore the assignment configuration script will ignore these scopes (it won't add/remove/update anything in there). Conversely, the distributed teams must only include those scopes in their version of the assignments.json that is not covered by the central team.

<br/>

Expand Down Expand Up @@ -213,7 +213,7 @@ Pipelines can customized to fit your needs:

### Edit and create Policies, Initiatives and Assignments

Using the starter kit edit the directories in the `Definitions` folder. To simplify entering parameters, you can use the [Initiative documenting feature](Definitions/Documentation/README.md#documenting-assignments-and-initiatives) which creates Markdown, CSV and a Json parameter file. You need to specify your initiatives to be documented (folder [`Definitions\Documentation`](Definitions/Documentation/README.md#specifying-initiative-documentation)) and execute script [`./Scripts/Operations/Build-PolicyAssignmentDocumentation.ps1`](Scripts/Operations/README.md#build-policyassignmentdocumentationps1)
Using the starter kit edit the directories in the `Definitions` folder. To simplify entering parameters, you can use the [Initiative documenting feature](Definitions/Documentation/README.md#documenting-assignments-and-initiatives) which creates Markdown, CSV and a JSON parameter file. You need to specify your initiatives to be documented (folder [`Definitions\Documentation`](Definitions/Documentation/README.md#specifying-initiative-documentation)) and execute script [`./Scripts/Operations/Build-PolicyAssignmentDocumentation.ps1`](Scripts/Operations/README.md#build-policyassignmentdocumentationps1)

<br/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Build-AzInitiativeDefinitionsPlan `
-policyNeededRoleDefinitionIds $policyNeededRoleDefinitionIds `
-initiativeNeededRoleDefinitionIds $initiativeNeededRoleDefinitionIds

# Process Assignment Json files
# Process Assignment JSON files
$allAssignments = @{}
$newAssignments = @{}
$updatedAssignments = @{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<#
.SYNOPSIS
This script deploys the component as defined in the plan Json:
This script deploys the component as defined in the plan JSON:
.NOTES
This script is designed to be run in Azure DevOps pipelines.
Expand Down
2 changes: 1 addition & 1 deletion Scripts/Deploy/Set-AzPolicyRolesFromPlan.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<#
.SYNOPSIS
This script deploys the component as defined in the plan Json:
This script deploys the component as defined in the plan JSON:
.NOTES
This script is designed to be run in Azure DevOps pipelines.
Expand Down
12 changes: 6 additions & 6 deletions Scripts/Helpers/Build-AzInitiativeDefinitionsPlan.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function Build-AzInitiativeDefinitionsPlan {
)

Write-Information "==================================================================================================="
Write-Information "Processing Initiative definitions Json files in folder '$initiativeDefinitionsRootFolder'"
Write-Information "Processing Initiative definitions JSON files in folder '$initiativeDefinitionsRootFolder'"
Write-Information "==================================================================================================="
$initiativeFiles = @()
$initiativeFiles += Get-ChildItem -Path $initiativeDefinitionsRootFolder -Recurse -File -Filter "*.json"
Expand Down Expand Up @@ -56,22 +56,22 @@ function Build-AzInitiativeDefinitionsPlan {
}


# Getting Initiative definitions from the Json files
# Getting Initiative definitions from the JSON files
$obsoleteInitiativeDefinitions = $existingCustomInitiativeDefinitions.Clone()
foreach ($initiativeFile in $initiativeFiles) {
$Json = Get-Content -Path $initiativeFile.FullName -Raw -ErrorAction Stop
if (!(Test-Json $Json)) {
Write-Error "Initiative Json file '$($initiativeFile.Name)' is not valid = $Json" -ErrorAction Stop
Write-Error "Initiative JSON file '$($initiativeFile.Name)' is not valid = $Json" -ErrorAction Stop
}
$initiativeObject = $Json | ConvertFrom-Json -Depth 100

$name = $initiativeObject.name
$displayName = $initiativeObject.properties.displayName
if ($null -eq $name) {
Write-Error "Initiative Json file '$($policyFile.FullName)' is missing an Initiative name" -ErrorAction Stop
Write-Error "Initiative JSON file '$($policyFile.FullName)' is missing an Initiative name" -ErrorAction Stop
}
elseif ($null -eq $displayName) {
Write-Error "Initiative Json file '$($policyFile.FullName)' is missing a Initiative displayName" -ErrorAction Stop
Write-Error "Initiative JSON file '$($policyFile.FullName)' is missing a Initiative displayName" -ErrorAction Stop
}
if ($customInitiativeDefinitions.ContainsKey($name)) {
Write-Error "Duplicate Initiative definition '$($name)' in '$($customInitiativeDefinitions[$name].FullName)' and '$($initiativeFile.FullName)'" -ErrorAction Stop
Expand Down Expand Up @@ -209,7 +209,7 @@ function Build-AzInitiativeDefinitionsPlan {
$replacedInitiativeDefinitions.Add($name, $initiativeDefinitionConfig)
}
else {
# Check if Initiative definition in Azure is the same as in the Json file
# Check if Initiative definition in Azure is the same as in the JSON file
$displayNameMatches = $matchingCustomDefinition.displayName -eq $initiativeDefinitionConfig.DisplayName
$descriptionMatches = $matchingCustomDefinition.description -eq $initiativeDefinitionConfig.Description
$metadataMatches = Confirm-MetadataMatches `
Expand Down
7 changes: 4 additions & 3 deletions Scripts/Helpers/Build-AzPolicyAssignmentsPlan.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function Build-AzPolicyAssignmentsPlan {
)

Write-Information "==================================================================================================="
Write-Information "Processing Policy Assignments Json files in folder '$assignmentsRootFolder'"
Write-Information "Processing Policy Assignments JSON files in folder '$assignmentsRootFolder'"
Write-Information "==================================================================================================="
$assignmentFiles = @()
$assignmentFiles += Get-ChildItem -Path $assignmentsRootFolder -Recurse -File -Filter "*.json"
Expand Down Expand Up @@ -59,7 +59,7 @@ function Build-AzPolicyAssignmentsPlan {
Write-Information "Process '$($assignmentFile.FullName)'"
}
else {
Write-Error "Assignment Json file '$($assignmentFile.FullName)' is not valid." -ErrorAction Stop
Write-Error "Assignment JSON file '$($assignmentFile.FullName)' is not valid." -ErrorAction Stop
}
$assignmentObject = $Json | ConvertFrom-Json -AsHashtable

Expand Down Expand Up @@ -425,7 +425,8 @@ function Build-AzPolicyAssignmentsPlan {
}
}

} }
}
}
Write-Information ""
Write-Information ""

Expand Down
2 changes: 1 addition & 1 deletion Scripts/Helpers/Build-AzPolicyDefinitionsForInitiative.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function Build-AzPolicyDefinitionsForInitiative {
$roleDefinitionIdsInInitiative = @{}
$initiativeName = $initiativeObject.name
foreach ($policyDefinition in $policyDefinitionsInJson) {
# check desired state defined in Json
# check desired state defined in JSON
$policyName = $policyDefinition.policyDefinitionName
$result = Confirm-PolicyDefinitionUsedExists `
-allPolicyDefinitions $allPolicyDefinitions `
Expand Down
Loading

0 comments on commit 6c6cc67

Please sign in to comment.