diff --git a/Docs/ci-cd-ado-pipelines.md b/Docs/ci-cd-ado-pipelines.md index 94e88e2c..f338e838 100644 --- a/Docs/ci-cd-ado-pipelines.md +++ b/Docs/ci-cd-ado-pipelines.md @@ -3,11 +3,13 @@ This page covers the specifics for the Azure DevOps (ADO) pipelines created by using the Starter Kit. Pipelines can be further customized based on requirements. Guidance provided is for the simplified GitHub Flow as documented in the [branching flows](ci-cd-branching-flows.md). Documentation on the Release Flow pipelines will be made available in a future release. > [!Note] +> To find all examples of Azure DevOps Pipelines, please visit [StarterKit/Pipelines/AzureDevOps](https://github.com/Azure/enterprise-azure-policy-as-code/tree/main/StarterKit/Pipelines/AzureDevOps). + > [App Registration Setup](ci-cd-app-registrations.md) is a pre-requisite. ## Service connections for the Service Principals -Create ADO service connections for each of the previously created [App Registrations](ci-cd-app-registrations.md). You will need to retrieve the credential for the Service Principal that Azure Devops will use for Authentication. This can be either a Client Secret, a X509 certificate, or a Federated Credential. For more information on these options, refer to the [Application Credentials](ci-cd-app-registrations.md/#application-credentials) +Create ADO service connections for each of the previously created [App Registrations](ci-cd-app-registrations.md). You will need to retrieve the credential for the Service Principal that Azure Devops will use for Authentication. This can be either a Client Secret, a X509 certificate, or a Federated Credential. For more information on these options, refer to the [Application Credentials](ci-cd-app-registrations.md/#application-credentials). ## Pipeline Templates @@ -15,10 +17,11 @@ The provided Azure DevOps pipelines utilize the template functionality to create ## GitHub Flow Pipeline -If utilizing the GitHub flow branching strategy, three pipeline files are created: -- epac-dev-pipeline -- epac-tenant-pipeline -- epac-remediation-pipeline +If utilizing the GitHub flow branching strategy, three pipeline files are created + + - [epac-dev-pipeline](https://github.com/Azure/enterprise-azure-policy-as-code/blob/main/StarterKit/Pipelines/AzureDevOps/GitHub-Flow/epac-dev-pipeline.yml) + - [epac-tenant-pipeline](https://github.com/Azure/enterprise-azure-policy-as-code/blob/main/StarterKit/Pipelines/AzureDevOps/GitHub-Flow/epac-tenant-pipeline.yml) + - [epac-remediation-pipeline](https://github.com/Azure/enterprise-azure-policy-as-code/blob/main/StarterKit/Pipelines/AzureDevOps/GitHub-Flow/epac-remediation-pipeline.yml) ### epac-dev-pipeline This represents the Develop Policy Resources in a Feature Branch flow as described in [Branching Flows](ci-cd-branching-flows.md/#develop-policy-resources-in-a-feature-branch). In general, The EPAC-Dev pipeline is configured to run when any change is pushed to a `feature/*` branch. It runs across three (3) stages: Plan, Deploy & Tenant Plan. diff --git a/Docs/ci-cd-app-registrations.md b/Docs/ci-cd-app-registrations.md index 003e697a..46510336 100644 --- a/Docs/ci-cd-app-registrations.md +++ b/Docs/ci-cd-app-registrations.md @@ -72,7 +72,7 @@ The following Service Principals & Role assignments would be created to support Credentials will need to be created for each Service Principal to be used in the CI/CD process. Traditionally, this is accomplished by creating a Client Secret on the associated Entra ID Application, and providing the CI/CD tool with the Application's ID and Secret. Secrets present an automation challenge as they need to be managed, secured, and rotated as they eventually expire. To solve this, some tools, including Azure DevOps, now support the use of Federated Credentials as described below. -### Alternative: `Azure Federated Identity Credentials` +### Azure Federated Identity Credentials Federated identity credentials are a new type of credential that enables workload identity federation for software workloads. Workload identity federation allows you to access Microsoft Entra protected resources without needing to manage secrets (for supported scenarios). diff --git a/Docs/ci-cd-github-actions.md b/Docs/ci-cd-github-actions.md index f6bfca66..e4745c66 100644 --- a/Docs/ci-cd-github-actions.md +++ b/Docs/ci-cd-github-actions.md @@ -1,8 +1,9 @@ # Github Actions -The starter kit contains a sample pipeline to use GitHub Actions to deploy Enterprise Policy as Code. It features a review process and is driven by pull requests and approvals. +This page covers the specifics for the GitHub Actions pipelines created by using the Starter Kit. Pipelines can be further customized based on requirements. We have revised our approach to GitHub Actions simplifying the process and make it easier to understand. The new approach is documented below and is included in the starter kit with v8.5 and later. -We have revised our approach to GitHub Actions simplifying the process and make it easier to understand. The new approach is documented below and is included in the starter kit with v8.5 and later. +> [!Note] +> To find all examples of GitHub Actions Pipelines, please visit [StarterKit/Pipelines/GitHubActions](https://github.com/Azure/enterprise-azure-policy-as-code/tree/main/StarterKit/Pipelines/AzureDevOps). The previous version is still available in the starter kit in folder `Legacy` and the [documentation is retained](#legacy-github-cicd-workflows) at the end of this page. diff --git a/Docs/operational-scripts-documenting-policy.md b/Docs/operational-scripts-documenting-policy.md index 9e77ec4d..54658451 100644 --- a/Docs/operational-scripts-documenting-policy.md +++ b/Docs/operational-scripts-documenting-policy.md @@ -33,8 +33,8 @@ Each file must contain one or both documentation topics. This example file in th "documentAssignments": { "documentAllAssignments": [ { - "enabled": true, "pacEnvironment": "EPAC-Prod", + "fileNameStemPrefix": "Production", "skipPolicyAssignments": [], "skipPolicyDefinitions": [ "/providers/microsoft.authorization/policysetdefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8" // Azure Security Benchmark v3 @@ -298,8 +298,8 @@ When enabled, this section lists all Policy Assignments across all scopes where `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. +* `fileNameStemPrefix`: add a prefix to the fileNameStem set in "documentationSpecifications". Usefull when needing to avoid overwriting of files. * `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)) diff --git a/Docs/start-implementing.md b/Docs/start-implementing.md index 99def0f0..37d461dc 100644 --- a/Docs/start-implementing.md +++ b/Docs/start-implementing.md @@ -14,7 +14,7 @@ The following steps are required to implement Enterprise Policy as Code (EPAC) i 4. Install [Powershell and EPAC](#install-powershell-and-epac). 5. Create your [`Definitions` folder and subfolders](#create-the-definitions-folder). 6. Populate `global-settings.jsonc` with your [environment settings](settings-global-setting-file.md) and [desired state strategy](settings-dfc-assignments.md). -7. Populate your Definitions folder with Policy resources. +7. Populate your Definitions folder with Policy resources. (For a folder structure example, please see [StarterKit/Definitions-Common](https://github.com/Azure/enterprise-azure-policy-as-code/tree/main/StarterKit/Definitions-Common)) - [Option A:] [Extract existing Policy resources](start-extracting-policy-resources.md) from your Azure environment. - [Option B:] [Integrate Azure Landing Zones (ALZ)](integrating-with-alz.md). - [Option C:] Utilize the [hydration kit](operational-scripts-hydration-kit.md) and `StarterKit` content. @@ -113,18 +113,6 @@ The simplest `global-settings.jsonc` for the above structure is: } ``` -## Cloud Environment with Unsupported/Missing Policy Definitions - -In some multi-tenant implementations, not all policies, policy sets, and/or assignments will function in all tenants, usually due to either built-in policies that don't exist in some tenant types or unavailable resource providers. In order to facilitate multi-tenant deployments in these scenarios, utilize the `epacCloudEnvironments` property to specify which cloud type a specific file should be considered in. For example in order to have a policy definition deployed only to epacEnvironments that are China cloud tenants, add a metadata property like this to that definition (or definitionSet) file: - -```json -"metadata": { - "epacCloudEnvironments": [ - "AzureChinaCloud" - ] -}, -``` - For assignment files, this is a top level property on the assignment's root node: ```json @@ -166,12 +154,27 @@ Many scripts use parameters for input and output folders. They default to the cu ### Create the Definitions folder -Create a new EPAC `Definitions` folder with a number of subfolder and a `global-settings.jsonc` file +Create a new EPAC `Definitions` folder with a number of subfolder and a `global-settings.jsonc` file. + +> [!TIP] +> For a folder structure example, please see [StarterKit/Definitions-Common](https://github.com/Azure/enterprise-azure-policy-as-code/tree/main/StarterKit/Definitions-Common). ```ps1 New-HydrationDefinitionFolder -DefinitionsRootFolder Definitions ``` +## Cloud Environment with Unsupported/Missing Policy Definitions + +In some multi-tenant implementations, not all policies, policy sets, and/or assignments will function in all tenants, usually due to either built-in policies that don't exist in some tenant types or unavailable resource providers. In order to facilitate multi-tenant deployments in these scenarios, utilize the `epacCloudEnvironments` property to specify which cloud type a specific file should be considered in. For example in order to have a policy definition deployed only to epacEnvironments that are China cloud tenants, add a metadata property like this to that definition (or definitionSet) file: + +```json +"metadata": { + "epacCloudEnvironments": [ + "AzureChinaCloud" + ] +}, +``` + ## Debug EPAC issues Should you encounter issues with the expected behavior of EPAC, try the following: diff --git a/Scripts/Helpers/Out-DocumentationForPolicyAssignments.ps1 b/Scripts/Helpers/Out-DocumentationForPolicyAssignments.ps1 index 633ce2df..9ab5a12c 100644 --- a/Scripts/Helpers/Out-DocumentationForPolicyAssignments.ps1 +++ b/Scripts/Helpers/Out-DocumentationForPolicyAssignments.ps1 @@ -462,7 +462,7 @@ function Out-DocumentationForPolicyAssignments { # Output file $outputFilePath = "$($OutputPath -replace '[/\\]$', '')/$($fileNameStem).md" - $allLines | Out-File $outputFilePath -Force + $allLines | Out-File "$outputFilePath" -Force # Output file foreach ($key in $assignmentsByCategory.keys | Sort-Object) { @@ -590,11 +590,7 @@ function Out-DocumentationForPolicyAssignments { #endregion csv #region PushToWiki - if ($DocumentationSpecification.markdownAdoWikiConfig) { - if ($WikiClonePat -eq "") { - Write-Error "PAT Token not found! Please pass as parameter 'WikiClonePat'!" - Exit 1 - } + if ($WikiClonePat) { Write-Information "Attempting push to Azure DevOps Wiki" # Clone down wiki git clone "https://$($WikiClonePat):x-oauth-basic@$($DocumentationSpecification.markdownAdoWikiConfig.adoOrganization).visualstudio.com/$($DocumentationSpecification.markdownAdoWikiConfig.adoProject)/_git/$($DocumentationSpecification.markdownAdoWikiConfig.adoWiki).wiki" diff --git a/Scripts/Operations/Build-PolicyDocumentation.ps1 b/Scripts/Operations/Build-PolicyDocumentation.ps1 index de1a0af2..0b37b9ac 100644 --- a/Scripts/Operations/Build-PolicyDocumentation.ps1 +++ b/Scripts/Operations/Build-PolicyDocumentation.ps1 @@ -58,7 +58,10 @@ param ( [switch] $IncludeManualPolicies, [Parameter(Mandatory = $false, HelpMessage = "Include if using a PAT token for pushing to ADO Wiki.")] - [string] $WikiClonePat + [string] $WikiClonePat, + + [parameter(Mandatory = $false, HelpMessage = "Defines which Policy as Code (PAC) environment we are using, if omitted, the script prompts for a value. The values are read from `$DefinitionsRootFolder/global-settings.jsonc.", Position = 0)] + [string] $pacSelector ) # Dot Source Helper Scripts @@ -258,16 +261,8 @@ foreach ($file in $files) { } } - # Set documentAllEnabled to false if does not exist - if ($null -eq $documentationSpec.documentAssignments.documentAllAssignments.enabled) { - $documentAllEnabled = $false - } - else { - $documentAllEnabled = $documentationSpec.documentAssignments.documentAllAssignments.enabled - } - # Process instructions to document Assignments - if ($documentationSpec.documentAssignments -and !$documentAllEnabled) { + if ($documentationSpec.documentAssignments -and !$documentationSpec.documentAssignments.documentAllAssignments) { $documentAssignments = $documentationSpec.documentAssignments $environmentCategories = $documentAssignments.environmentCategories @@ -340,9 +335,25 @@ foreach ($file in $files) { } } - if ($documentationSpec.documentAssignments -and $documentAllEnabled) { - # Load pacEnvironment + if ($documentationSpec.documentAssignments -and $documentationSpec.documentAssignments.documentAllAssignments) { + # Load pacEnvironments from policy documentations folder $pacEnvironmentSelector = $documentationSpec.documentAssignments.documentAllAssignments.pacEnvironment + + # Check to see if PacSelector was passed as a parameter + if ($pacSelector) { + $pacEnvironmentSelector = $pacSelector + $pacSelectorDocumentAllAssignments = $documentationSpec.documentAssignments.documentAllAssignments | Where-Object { $_.pacEnvironment -eq "$pacSelector" } + $documentationSpec.documentAssignments.documentAllAssignments = $pacSelectorDocumentAllAssignments + + if ($null -eq $documentationSpec.documentAssignments.documentAllAssignments) { + Write-Error "Provided PacSelector '$pacSelector' not found in $($file.Name)!" -ErrorAction Stop + } + } + + # Check to see if PacSelector was not passed as a parameter but there are multiple pacEnvironments configured within documentAllAssignments + if ($pacEnvironmentSelector.count -gt 1 -and $pacSelector -eq "") { + Write-Error "Multiple 'pacEnvironments' found in $($file.Name) - Please provide parameter -PacSelector to specify the documentation needed to be created" -ErrorAction Stop + } $pacEnvironment = Switch-PacEnvironment ` -PacEnvironmentSelector $pacEnvironmentSelector ` -PacEnvironments $pacEnvironments ` @@ -493,6 +504,11 @@ foreach ($file in $files) { # Build documents $documentationSpecifications = $documentAssignments.documentationSpecifications foreach ($documentationSpecification in $documentationSpecifications) { + # Check to see if naming contains prefix for file name + if ($documentationSpec.documentAssignments.documentAllAssignments.fileNameStemPrefix) { + $documentationSpecification.fileNameStem = $documentationSpec.documentAssignments.documentAllAssignments.fileNameStemPrefix + "-" + $documentationSpecification.fileNameStem + } + $documentationType = $documentationSpecification.type if ($null -ne $documentationType) { Write-Information "Field documentationType ($($documentationType)) is deprecated" diff --git a/StarterKit/Definitions-Common/global-settings.jsonc b/StarterKit/Definitions-Common/global-settings.jsonc new file mode 100644 index 00000000..3abc1b18 --- /dev/null +++ b/StarterKit/Definitions-Common/global-settings.jsonc @@ -0,0 +1,32 @@ +{ + "$schema": "https://raw.githubusercontent.com/Azure/enterprise-azure-policy-as-code/main/Schemas/global-settings-schema.json", + "pacOwnerId": "bd715eb8-36d2-4d33-8db0-505e04c0e4aa", + "pacEnvironments": [ + { + "pacSelector": "EPAC-DEV", + "cloud": "AzureCloud", + "tenantId": "4df7a0a2-1b2f-4a43-b48f-bed4b04a4f91", + "deploymentRootScope": "/providers/Microsoft.Management/managementGroups/4df7a0a2-1b2f-4a43-b48f-bed4b04a4f91", + "desiredState": { // [optional] + "strategy": "ownedOnly", // default full + "keepDfcSecurityAssignments": true, // default false + "doNotDisableDeprecatedPolicies": false + }, + "globalNotScopes": [], + "managedIdentityLocation": "eastus2" + }, + { + "pacSelector": "EPAC-PROD", + "cloud": "AzureCloud", + "tenantId": "3385800d-0197-4981-92e4-12a04898f862", + "deploymentRootScope": "/providers/Microsoft.Management/managementGroups/3385800d-0197-4981-92e4-12a04898f862", + "desiredState": { // [optional] + "strategy": "full", // default full + "keepDfcSecurityAssignments": true, // default false // default full + "doNotDisableDeprecatedPolicies": false + }, + "globalNotScopes": [], + "managedIdentityLocation": "eastus2" + } + ] +} \ No newline at end of file diff --git a/StarterKit/Definitions-Common/policyAssignments/allowed-locations-assignments.jsonc b/StarterKit/Definitions-Common/policyAssignments/allowed-locations-assignments.jsonc new file mode 100644 index 00000000..a2c68409 --- /dev/null +++ b/StarterKit/Definitions-Common/policyAssignments/allowed-locations-assignments.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "https://raw.githubusercontent.com/Azure/enterprise-azure-policy-as-code/main/Schemas/policy-assignment-schema.json", + "nodeName": "/Loc/", + "assignment": { + "name": "allowed-locations", + "displayName": "Allowed Locations", + "description": "Sets the allowed locations" + }, + "definitionEntry": { + "policySetName": "e14e5d7c-9551-4ae2-b8fa-b5d6b9b3c677", + "displayName": "Allowed Locations Initiative" + }, + "parameters": { + "AllowedLocations": [ + "eastus2" + ] + }, + "scope": { + "epac-dev": [ + "/providers/Microsoft.Management/managementGroups/mg-epac-dev" + ], + "tenant": [ + "/providers/Microsoft.Management/managementGroups/mg-enterprise" + ] + } +} \ No newline at end of file diff --git a/StarterKit/Definitions-Common/policyDocumentations/document-all-assignments.jsonc b/StarterKit/Definitions-Common/policyDocumentations/document-all-assignments.jsonc new file mode 100644 index 00000000..98ee4c52 --- /dev/null +++ b/StarterKit/Definitions-Common/policyDocumentations/document-all-assignments.jsonc @@ -0,0 +1,31 @@ +{ + "documentAssignments": { + "documentAllAssignments": [ + { + "enabled": true, + "pacEnvironment": "EPAC-PROD", + "fileNameStemPrefix": "Production", + "skipPolicyAssignments": [], + "skipPolicyDefinitions": [ + "/providers/microsoft.authorization/policysetdefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8" // Azure Security Benchmark v3 + ], + "overrideEnvironmentCategory": {} + } + ], + "documentationSpecifications": [ + { + "fileNameStem": "Azure-Policy-Assignments", + "environmentCategories": [], + "title": "Current list of Azure Policies deployed", + "markdownAdoWiki": true, + "markdownAdoWikiConfig": [ + { + "adoOrganization": "EPAC", + "adoProject": "EPAC", + "adoWiki": "EPAC" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/StarterKit/Definitions-Common/policyExemptions/active-exemptions.jsonc b/StarterKit/Definitions-Common/policyExemptions/active-exemptions.jsonc new file mode 100644 index 00000000..6826271d --- /dev/null +++ b/StarterKit/Definitions-Common/policyExemptions/active-exemptions.jsonc @@ -0,0 +1,15 @@ +{ + "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", + } + ] +} \ No newline at end of file