From 7867c8c7759bfc2032b08fbeae5d6a6adf06952b Mon Sep 17 00:00:00 2001 From: Anthony Watherston Date: Thu, 6 Jun 2024 10:29:31 +1000 Subject: [PATCH] Various fixes (#657) Co-authored-by: Anthony Watherston --- .../operational-scripts-documenting-policy.md | 35 +++- Docs/policy-exemptions.md | 6 + Schemas/policy-documentation-schema.json | 33 +++- Scripts/Deploy/Build-DeploymentPlans.ps1 | 42 +++-- .../Build-AssignmentDefinitionNode.ps1 | 13 +- Scripts/Helpers/Build-ExemptionsPlan.ps1 | 14 +- .../Out-DocumentationForPolicyAssignments.ps1 | 150 +++++++++------- .../Out-DocumentationForPolicySets.ps1 | 164 ++++++++++-------- 8 files changed, 298 insertions(+), 159 deletions(-) diff --git a/Docs/operational-scripts-documenting-policy.md b/Docs/operational-scripts-documenting-policy.md index 1bb0bff5..29575577 100644 --- a/Docs/operational-scripts-documenting-policy.md +++ b/Docs/operational-scripts-documenting-policy.md @@ -136,22 +136,45 @@ Each file must contain one or both documentation topics. This example file in th Markdown processors vary slightly. This shipt has settings to tune the output to match the Markdown processor you are using. -Azure DevOps Wikis (and maybe others) recognize `[[_TOC_]]` to insert a table of contents. Setting to `addMarkdownAdoWikiToc` to true enables generating the table of content. +### Azure DevOps Wiki Markdown + +- Some Markdown processors (including Azure DevOps Wikis) recognize `[[_TOC_]]` to insert a table of contents. Setting to `markdownAddToc` to `true` enables generating the table of content. +- Azure DevOps Wikis do not need a heading (title) at level 1. It needs the subheadings at level 1 instead. Setting `markdownAdoWiki` to true enables formatting the headings for Azure DevOps Wiki and generating the table of content (implicitly sets `markdownAddToc` to `true`). + +```jsonc +"markdownAddToc": true, // default is false, set to true to add a table of contents +``` + +```jsonc +"markdownAdoWiki": true, // default is false, set to true to format headings for Azure DevOps Wiki and generate a table of contents +``` + +### Embedded HTML in Markdown Tables + +EPAC uses embedded HTML to format Markdown tables. Some Markdown processors, such as SharePoint, do not recognize embedded HTML. Setting `markdownNoEmbeddedHtml` to `true` emits commas `, ` instead of the HTML tag `
`. + +```jsonc +"markdownNoEmbeddedHtml": true, // default is false, set to true to remove embedded HTML in Markdown tables +``` + +### Improve Markdown Readability + +Policy definition group names are not included in Markdown to reduce clutter. You can include a column by setting `markdownIncludeComplianceGroupNames` to `true`, ```jsonc -"addMarkdownAdoWikiToc": true, // default is false, set to true to add markdown ADO Wiki TOC +"markdownIncludeComplianceGroupNames": true, // default is false, set to true to include compliance group names ``` -SharePoint (and maybe others) do not recognize embedded HTML, such as line braeks (`
`) within a Markdown table. Setting `noMarkdownInTableLineBreaks` to true emits commas instead of the HTML tag. +In some markdown processors very long parameter name break the display. You can set `markdownSuppressParameterSection` to true to completely suppress the parameter section in the Markdown output. ```jsonc -"noMarkdownInTableLineBreaks": true, // default is false, set to true to remove markdown in table line breaks +"markdownSuppressParameterSection": true, // default is false, set to true to suppress the parameter section in the Markdown output ``` -Policy definition group names are not included in Markdown to reduce clutter. You can include a column by setting `includeComplianceGroupNamesInMarkdown` to true, +Alternatively, you can set `markdownMaxParameterLength` to a maximum length. EPAC will truncate the name at that length and append an ellipsis. The default is 40 characters. The minimum is 16 characters. ```jsonc -"includeComplianceGroupNamesInMarkdown": true, // default is false, set to true to include compliance group names +"markdownMaxParameterLength": 42, // default is 42 ``` ## Assignment Documentation diff --git a/Docs/policy-exemptions.md b/Docs/policy-exemptions.md index f9b0ff07..90917aec 100644 --- a/Docs/policy-exemptions.md +++ b/Docs/policy-exemptions.md @@ -361,3 +361,9 @@ Legacy column `policyAssignmentId` is still supported for backward compatibility - 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](#specifying-policydefinitionreferenceids) + +## 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```. diff --git a/Schemas/policy-documentation-schema.json b/Schemas/policy-documentation-schema.json index 6cb610c5..7a226759 100644 --- a/Schemas/policy-documentation-schema.json +++ b/Schemas/policy-documentation-schema.json @@ -80,14 +80,23 @@ "title": { "type": "string" }, - "addMarkdownAdoWikiToc": { + "markdownAddToc": { "type": "boolean" }, - "noMarkdownInTableLineBreaks": { + "markdownAdoWiki": { "type": "boolean" }, - "includeComplianceGroupNamesInMarkdown": { + "markdownNoEmbeddedHtml": { "type": "boolean" + }, + "markdownIncludeComplianceGroupNames": { + "type": "boolean" + }, + "markdownSuppressParameterSection": { + "type": "boolean" + }, + "markdownMaxParameterLength": { + "type": "integer" } }, "required": [ @@ -149,6 +158,24 @@ "type": "string" } ] + }, + "markdownAddToc": { + "type": "boolean" + }, + "markdownAdoWiki": { + "type": "boolean" + }, + "markdownNoEmbeddedHtml": { + "type": "boolean" + }, + "markdownIncludeComplianceGroupNames": { + "type": "boolean" + }, + "markdownSuppressParameterSection": { + "type": "boolean" + }, + "markdownMaxParameterLength": { + "type": "integer" } }, "required": [ diff --git a/Scripts/Deploy/Build-DeploymentPlans.ps1 b/Scripts/Deploy/Build-DeploymentPlans.ps1 index 70ef49c1..fcfbdead 100644 --- a/Scripts/Deploy/Build-DeploymentPlans.ps1 +++ b/Scripts/Deploy/Build-DeploymentPlans.ps1 @@ -53,7 +53,9 @@ param ( [Parameter(HelpMessage = "If set, outputs variables consumable by conditions in a DevOps pipeline.")] [ValidateSet("ado", "gitlab", "")] - [string] $DevOpsType = "" + [string] $DevOpsType = "", + + [switch]$SkipNotScopedExemptions ) $PSDefaultParameterValues = @{ @@ -332,17 +334,33 @@ if ($buildSelections.buildAny) { if ($buildSelections.buildPolicyExemptions) { # Process Exemption JSON files - Build-ExemptionsPlan ` - -ExemptionsRootFolder $policyExemptionsFolderForPacEnvironment ` - -ExemptionsAreNotManagedMessage $exemptionsAreNotManagedMessage ` - -PacEnvironment $pacEnvironment ` - -ScopeTable $scopeTable ` - -AllDefinitions $allDefinitions ` - -AllAssignments $allAssignments ` - -CombinedPolicyDetails $combinedPolicyDetails ` - -Assignments $assignments ` - -DeployedExemptions $deployedPolicyResources.policyExemptions ` - -Exemptions $exemptions + if ($SkipNotScopedExemptions) { + Build-ExemptionsPlan ` + -ExemptionsRootFolder $policyExemptionsFolderForPacEnvironment ` + -ExemptionsAreNotManagedMessage $exemptionsAreNotManagedMessage ` + -PacEnvironment $pacEnvironment ` + -ScopeTable $scopeTable ` + -AllDefinitions $allDefinitions ` + -AllAssignments $allAssignments ` + -CombinedPolicyDetails $combinedPolicyDetails ` + -Assignments $assignments ` + -DeployedExemptions $deployedPolicyResources.policyExemptions ` + -Exemptions $exemptions ` + -SkipNotScopedExemptions + } + else { + Build-ExemptionsPlan ` + -ExemptionsRootFolder $policyExemptionsFolderForPacEnvironment ` + -ExemptionsAreNotManagedMessage $exemptionsAreNotManagedMessage ` + -PacEnvironment $pacEnvironment ` + -ScopeTable $scopeTable ` + -AllDefinitions $allDefinitions ` + -AllAssignments $allAssignments ` + -CombinedPolicyDetails $combinedPolicyDetails ` + -Assignments $assignments ` + -DeployedExemptions $deployedPolicyResources.policyExemptions ` + -Exemptions $exemptions + } } Write-Information "===================================================================================================" diff --git a/Scripts/Helpers/Build-AssignmentDefinitionNode.ps1 b/Scripts/Helpers/Build-AssignmentDefinitionNode.ps1 index db151933..8d5c2b45 100644 --- a/Scripts/Helpers/Build-AssignmentDefinitionNode.ps1 +++ b/Scripts/Helpers/Build-AssignmentDefinitionNode.ps1 @@ -344,11 +344,18 @@ function Build-AssignmentDefinitionNode { $thisScopeGlobalNotScopeList = $thisScopeDetails.notScopesList $thisScopeGlobalNotScopeTable = $thisScopeDetails.notScopesTable foreach ($notScope in $definition.notScopesList) { - if (-not $thisScopeGlobalNotScopeTable.ContainsKey($notScope)) { - if ($thisScopeChildren.ContainsKey($notScope)) { + $individualResource = $false + $notScopeTrimmed = $notScope + $splits = $notScope -split "/" + if ($splits.Count -gt 5) { + $individualResource = $true + $notScopeTrimmed = $splits[0..4] -join "/" + } + if (-not $thisScopeGlobalNotScopeTable.ContainsKey($notScopeTrimmed)) { + if ($thisScopeChildren.ContainsKey($notScopeTrimmed)) { $null = $thisNotScopeList.Add($notScope) } - elseif ($notScope.Contains("*")) { + elseif (!$individualResource -and $notScope.Contains("*")) { foreach ($scopeChildId in $thisScopeChildren.Keys) { if ($scopeChildId -like $notScope) { $null = $thisNotScopeList.Add($scopeChildId) diff --git a/Scripts/Helpers/Build-ExemptionsPlan.ps1 b/Scripts/Helpers/Build-ExemptionsPlan.ps1 index 87215e1d..b8d1d3c3 100644 --- a/Scripts/Helpers/Build-ExemptionsPlan.ps1 +++ b/Scripts/Helpers/Build-ExemptionsPlan.ps1 @@ -10,7 +10,8 @@ function Build-ExemptionsPlan { $CombinedPolicyDetails, $Assignments, $DeployedExemptions, - $Exemptions + $Exemptions, + [switch]$SkipNotScopedExemptions ) Write-Information "===================================================================================================" @@ -618,8 +619,15 @@ function Build-ExemptionsPlan { if ($includeAssignment) { foreach ($notScope in $calculatedPolicyAssignment.notScopes) { if ($trimmedScope -eq $notScope -or $parentTable.ContainsKey($notScope)) { - $includeAssignment = $false - break + if ($SkipNotScopedExemptions) { + $includeAssignment = $true + break + } + else { + $includeAssignment = $false + break + } + } } if ($includeAssignment) { diff --git a/Scripts/Helpers/Out-DocumentationForPolicyAssignments.ps1 b/Scripts/Helpers/Out-DocumentationForPolicyAssignments.ps1 index e74d0c7a..4276cdeb 100644 --- a/Scripts/Helpers/Out-DocumentationForPolicyAssignments.ps1 +++ b/Scripts/Helpers/Out-DocumentationForPolicyAssignments.ps1 @@ -154,12 +154,28 @@ function Out-DocumentationForPolicyAssignments { #region Markdown [System.Collections.Generic.List[string]] $allLines = [System.Collections.Generic.List[string]]::new() - $null = $allLines.Add("# $title`n") + $leadingHeadingHashtag = "#" + if ($DocumentationSpecification.markdownAdoWiki) { + $leadingHeadingHashtag = "" + $null = $allLines.Add("[[_TOC_]]`n") + } + else { + $null = $allLines.Add("# $title`n") + if ($DocumentationSpecification.markdownAddToc) { + $null = $allLines.Add("[[_TOC_]]`n") + } + } $null = $allLines.Add("Auto-generated Policy effect documentation across environments '$($environmentCategories -join "', '")' sorted by Policy category and Policy display name.") - if ($DocumentationSpecification.addMarkdownAdoWikiToc) { - $null = $allLines.Add("`n[[_TOC_]]") + + $inTableAfterDisplayNameBreak = "
" + $inTableBreak = "
" + if ($DocumentationSpecification.markdownNoEmbeddedHtml) { + $inTableAfterDisplayNameBreak = ": " + $inTableBreak = ", " } + + #region Environment Categories foreach ($environmentCategory in $environmentCategories) { @@ -167,9 +183,9 @@ function Out-DocumentationForPolicyAssignments { $itemList = $perEnvironment.itemList $assignmentsDetails = $perEnvironment.assignmentsDetails $scopes = $perEnvironment.scopes - $null = $allLines.Add("`n## Environment Category ``$environmentCategory``") + $null = $allLines.Add("`n$leadingHeadingHashtag# Environment Category ``$environmentCategory``") - $null = $allLines.Add("`n### Scopes`n") + $null = $allLines.Add("`n$leadingHeadingHashtag## Scopes`n") foreach ($scope in $scopes) { $null = $allLines.Add("- $scope") } @@ -179,7 +195,7 @@ function Out-DocumentationForPolicyAssignments { if ($assignmentsDetails.ContainsKey($assignmentId)) { # should always be true $assignmentsDetail = $assignmentsDetails.$assignmentId - $null = $allLines.Add("`n### Assignment: ``$($assignmentsDetail.assignment.properties.displayName)```n") + $null = $allLines.Add("`n$leadingHeadingHashtag## Assignment: ``$($assignmentsDetail.assignment.properties.displayName)```n") $null = $allLines.Add("| Property | Value |") $null = $allLines.Add("| :------- | :---- |") $null = $allLines.Add("| Assignment Id | $($assignmentId) |") @@ -204,24 +220,17 @@ function Out-DocumentationForPolicyAssignments { $addedTableDivider += " :-----: |" } - if ($DocumentationSpecification.includeComplianceGroupNamesInMarkdown) { - $null = $allLines.Add("`n## Policy Effects by Policy`n") - $null = $allLines.Add("| Category | Policy | Compliance |$addedTableHeader") - $null = $allLines.Add("| :------- | :----- | :--------- |$addedTableDivider") + if ($DocumentationSpecification.markdownIncludeComplianceGroupNames) { + $null = $allLines.Add("`n$leadingHeadingHashtag# Policy Effects by Policy`n") + $null = $allLines.Add("| Category | Policy | Group Names |$addedTableHeader") + $null = $allLines.Add("| :------- | :----- | :---------- |$addedTableDivider") } else { - $null = $allLines.Add("`n## Policy Effects by Policy`n") + $null = $allLines.Add("`n$leadingHeadingHashtag# Policy Effects by Policy`n") $null = $allLines.Add("| Category | Policy |$addedTableHeader") $null = $allLines.Add("| :------- | :----- |$addedTableDivider") } - $inTableAfterDisplayNameBreak = "
" - $inTableBreak = "
" - if ($DocumentationSpecification.noMarkdownInTableLineBreaks) { - $inTableAfterDisplayNameBreak = ": " - $inTableBreak = ", " - } - $flatPolicyListAcrossEnvironments.Values | Sort-Object -Property { $_.category }, { $_.displayName } | ForEach-Object -Process { # Build additional columns $addedEffectColumns = "" @@ -243,7 +252,7 @@ function Out-DocumentationForPolicyAssignments { } $groupNamesText = "" - if ($DocumentationSpecification.includeComplianceGroupNamesInMarkdown) { + if ($DocumentationSpecification.markdownIncludeComplianceGroupNames) { $groupNames = $_.groupNames if ($groupNames.Count -gt 0) { $sortedGroupNames = $groupNames | Sort-Object -Unique @@ -260,60 +269,75 @@ function Out-DocumentationForPolicyAssignments { #region Parameters - $null = $allLines.Add("`n## Policy Parameters by Policy`n") - $null = $allLines.Add("| Category | Policy |$addedTableHeader") - $null = $allLines.Add("| :------- | :----- |$addedTableDivider") + if ($DocumentationSpecification.markdownSuppressParameterSection) { + Write-Verbose "Suppressing Parameters section in Markdown" + } + else { + $null = $allLines.Add("`n$leadingHeadingHashtag# Policy Parameters by Policy`n") + $null = $allLines.Add("| Category | Policy |$addedTableHeader") + $null = $allLines.Add("| :------- | :----- |$addedTableDivider") - $flatPolicyListAcrossEnvironments.Values | Sort-Object -Property { $_.category }, { $_.displayName } | ForEach-Object -Process { - # Build additional columns - $addedParametersColumns = "" - $environmentList = $_.environmentList - $hasParameters = $false - foreach ($environmentCategory in $environmentCategories) { - if ($environmentList.ContainsKey($environmentCategory)) { - $environmentCategoryValues = $environmentList.$environmentCategory - $text = "" - $parameters = $environmentCategoryValues.parameters - $notFirst = $false - foreach ($parameterName in $parameters.Keys) { - $parameter = $parameters.$parameterName - if (-not $parameter.isEffect) { - $hasParameters = $true - $value = $parameter.value - if ($notFirst) { - $text += $inTableBreak - } - else { - $notFirst = $true - } - if ($null -eq $value) { - $value = $parameter.defaultValue + $flatPolicyListAcrossEnvironments.Values | Sort-Object -Property { $_.category }, { $_.displayName } | ForEach-Object -Process { + # Build additional columns + $addedParametersColumns = "" + $environmentList = $_.environmentList + $hasParameters = $false + foreach ($environmentCategory in $environmentCategories) { + if ($environmentList.ContainsKey($environmentCategory)) { + $environmentCategoryValues = $environmentList.$environmentCategory + $text = "" + $parameters = $environmentCategoryValues.parameters + $notFirst = $false + foreach ($parameterName in $parameters.Keys) { + $parameter = $parameters.$parameterName + if (-not $parameter.isEffect) { + $hasParameters = $true + $markdownMaxParameterLength = 42 + if ($DocumentationSpecification.markdownMaxParameterLength) { + $markdownMaxParameterLength = $DocumentationSpecification.markdownMaxParameterLength + if ($markdownMaxParameterLength -lt 16) { + Write-Error "markdownMaxParameterLength must be at least 16; it is $markdownMaxParameterLength" -ErrorAction Stop + } + } + if ($parameterName.length -gt $markdownMaxParameterLength) { + $parameterName = $parameterName.substring(0, $markdownMaxParameterLength - 3) + "..." + } + $value = $parameter.value + if ($notFirst) { + $text += $inTableBreak + } + else { + $notFirst = $true + } if ($null -eq $value) { - $value = "null" + $value = $parameter.defaultValue + if ($null -eq $value) { + $value = "null" + } } - } - if ($value -is [string]) { - $text += "$($parameterName) = **```"$value`"``**" - } - else { - $json = ConvertTo-Json $value -Depth 100 -Compress - $jsonTruncated = $json - if ($json.length -gt 40) { - $jsonTruncated = $json.substring(0, 37) + "..." + $valueString = "" + if ($value -is [string]) { + $valueString = $value + } + else { + $valueString = ConvertTo-Json $value -Depth 100 -Compress } - $text += "$($parameterName) = **``$jsonTruncated``**" + if ($valueString.length -gt $markdownMaxParameterLength) { + $valueString = $valueString.substring(0, $markdownMaxParameterLength - 3) + "..." + } + $text += "$($parameterName) = **``$valueString``**" } } + $addedParametersColumns += " $text |" + } + else { + $addedParametersColumns += " |" } - $addedParametersColumns += " $text |" } - else { - $addedParametersColumns += " |" + if ($hasParameters) { + $null = $allLines.Add("| $($_.category) | **$($_.displayName)**$($inTableAfterDisplayNameBreak)$($_.description) |$($addedParametersColumns)") } } - if ($hasParameters) { - $null = $allLines.Add("| $($_.category) | **$($_.displayName)**$($inTableAfterDisplayNameBreak)$($_.description) |$($addedParametersColumns)") - } } #endregion Parameters diff --git a/Scripts/Helpers/Out-DocumentationForPolicySets.ps1 b/Scripts/Helpers/Out-DocumentationForPolicySets.ps1 index 8510bcde..7599c3fc 100644 --- a/Scripts/Helpers/Out-DocumentationForPolicySets.ps1 +++ b/Scripts/Helpers/Out-DocumentationForPolicySets.ps1 @@ -21,24 +21,36 @@ function Out-DocumentationForPolicySets { #region Markdown [System.Collections.Generic.List[string]] $allLines = [System.Collections.Generic.List[string]]::new() - - $null = $allLines.Add("# $title`n") + $leadingHeadingHashtag = "#" + if ($DocumentationSpecification.markdownAdoWiki) { + $leadingHeadingHashtag = "" + $null = $allLines.Add("[[_TOC_]]`n") + } + else { + $null = $allLines.Add("# $title`n") + if ($DocumentationSpecification.markdownAddToc) { + $null = $allLines.Add("[[_TOC_]]`n") + } + } $null = $allLines.Add("Auto-generated Policy effect documentation for PolicySets grouped by Effect and sorted by Policy category and Policy display name.") - if ($DocumentationSpecification.addMarkdownAdoWikiToc) { - $null = $allLines.Add("`n[[_TOC_]]") + $inTableAfterDisplayNameBreak = "
" + $inTableBreak = "
" + if ($DocumentationSpecification.markdownNoEmbeddedHtml) { + $inTableAfterDisplayNameBreak = ": " + $inTableBreak = ", " } #region Policy Set List $addedTableHeader = "" $addedTableDivider = "" - $null = $allLines.Add("`n## Policy Set (Initiative) List`n") + $null = $allLines.Add("`n$leadingHeadingHashtag# Policy Set (Initiative) List`n") foreach ($item in $ItemList) { $shortName = $item.shortName $policySetId = $item.policySetId $policySetDetail = $PolicySetDetails.$policySetId $policySetDisplayName = $policySetDetail.displayName -replace "\n\r", " " -replace "\n", " " -replace "\r", " " $policySetDescription = $policySetDetail.description -replace "\n\r", " " -replace "\n", " " -replace "\r", " " - $null = $allLines.Add("### $($shortName)`n") + $null = $allLines.Add("$leadingHeadingHashtag## $($shortName)`n") $null = $allLines.Add("- Display name: $($policySetDisplayName)`n") $null = $allLines.Add("- Type: $($policySetDetail.policyType)") $null = $allLines.Add("- Category: $($policySetDetail.category)`n") @@ -49,21 +61,14 @@ function Out-DocumentationForPolicySets { } #endregion Policy Set List - $inTableAfterDisplayNameBreak = "
" - $inTableBreak = "
" - if ($DocumentationSpecification.noMarkdownInTableLineBreaks) { - $inTableAfterDisplayNameBreak = ": " - $inTableBreak = ", " - } - #region Policy Effects - if ($DocumentationSpecification.includeComplianceGroupNamesInMarkdown) { - $null = $allLines.Add("`n## Policy Effects by Policy`n") + if ($DocumentationSpecification.markdownIncludeComplianceGroupNames) { + $null = $allLines.Add("`n$leadingHeadingHashtag# Policy Effects by Policy`n") $null = $allLines.Add("| Category | Policy | Compliance |$addedTableHeader") $null = $allLines.Add("| :------- | :----- | :----------|$addedTableDivider") } else { - $null = $allLines.Add("`n## Policy Effects`n") + $null = $allLines.Add("`n$leadingHeadingHashtag# Policy Effects`n") $null = $allLines.Add("| Category | Policy |$addedTableHeader") $null = $allLines.Add("| :------- | :----- |$addedTableDivider") } @@ -103,7 +108,7 @@ function Out-DocumentationForPolicySets { } } $complianceText = "" - if ($DocumentationSpecification.includeComplianceGroupNamesInMarkdown) { + if ($DocumentationSpecification.markdownIncludeComplianceGroupNames) { if ($groupNamesList.Count -gt 0) { $groupNamesList = $groupNamesList | Sort-Object -Unique $complianceText = "| $($groupNamesList -join $inTableBreak) " @@ -123,69 +128,90 @@ function Out-DocumentationForPolicySets { #endregion Policy Effects #region Policy Parameters - $null = $allLines.Add("`n## Policy Parameters by Policy`n") - $null = $allLines.Add("| Category | Policy |$addedTableHeader") - $null = $allLines.Add("| :------- | :----- |$addedTableDivider") + if ($DocumentationSpecification.markdownSuppressParameterSection) { + Write-Verbose "Suppressing Parameters section in Markdown" + } + else { + $null = $allLines.Add("`n$leadingHeadingHashtag# Policy Parameters by Policy`n") + $null = $allLines.Add("| Category | Policy |$addedTableHeader") + $null = $allLines.Add("| :------- | :----- |$addedTableDivider") - $FlatPolicyList.Values | Sort-Object -Property { $_.category }, { $_.displayName } | ForEach-Object -Process { - $policySetList = $_.policySetList - $addedParametersColumns = "" - $effectValue = "Unknown" - if ($null -ne $_.effectValue) { - $effectValue = $_.effectValue - } - else { - $effectValue = $_.effectDefault - } + $FlatPolicyList.Values | Sort-Object -Property { $_.category }, { $_.displayName } | ForEach-Object -Process { + $policySetList = $_.policySetList + $addedParametersColumns = "" + $effectValue = "Unknown" + if ($null -ne $_.effectValue) { + $effectValue = $_.effectValue + } + else { + $effectValue = $_.effectDefault + } - if ($effectValue -ne "Manual" -or $IncludeManualPolicies) { - $hasParameters = $false - foreach ($item in $ItemList) { - $shortName = $item.shortName - if ($policySetList.ContainsKey($shortName)) { - $perPolicySet = $policySetList.$shortName - $parameters = $perPolicySet.parameters - $text = "" - $notFirst = $false - foreach ($parameterName in $parameters.Keys) { - $parameter = $parameters.$parameterName - if (-not $parameter.isEffect) { - $hasParameters = $true - $value = $parameter.defaultValue - if ($notFirst) { - $text += $inTableBreak - } - else { - $notFirst = $true - } - if ($value -is [string]) { - $text += "$($parameterName) = **```"$value`"``**" - } - else { - $json = ConvertTo-Json $value -Depth 100 -Compress - $jsonTruncated = $json - if ($json.length -gt 40) { - $jsonTruncated = $json.substring(0, 37) + "..." + if ($effectValue -ne "Manual" -or $IncludeManualPolicies) { + $hasParameters = $false + foreach ($item in $ItemList) { + $shortName = $item.shortName + if ($policySetList.ContainsKey($shortName)) { + $perPolicySet = $policySetList.$shortName + $parameters = $perPolicySet.parameters + $text = "" + $notFirst = $false + foreach ($parameterName in $parameters.Keys) { + $parameter = $parameters.$parameterName + if (-not $parameter.isEffect) { + $hasParameters = $true + $markdownMaxParameterLength = 42 + if ($DocumentationSpecification.markdownMaxParameterLength) { + $markdownMaxParameterLength = $DocumentationSpecification.markdownMaxParameterLength + if ($markdownMaxParameterLength -lt 16) { + Write-Error "markdownMaxParameterLength must be at least 16; it is $markdownMaxParameterLength" -ErrorAction Stop + } } - $text += "$($parameterName) = **``$jsonTruncated``**" + if ($parameterName.length -gt $markdownMaxParameterLength) { + $parameterName = $parameterName.substring(0, $markdownMaxParameterLength - 3) + "..." + } + $value = $parameter.value + if ($notFirst) { + $text += $inTableBreak + } + else { + $notFirst = $true + } + if ($null -eq $value) { + $value = $parameter.defaultValue + if ($null -eq $value) { + $value = "null" + } + } + $valueString = "" + if ($value -is [string]) { + $valueString = $value + } + else { + $valueString = ConvertTo-Json $value -Depth 100 -Compress + } + if ($valueString.length -gt $markdownMaxParameterLength) { + $valueString = $valueString.substring(0, $markdownMaxParameterLength - 3) + "..." + } + $text += "$($parameterName) = **``$valueString``**" } } + $addedParametersColumns += " $text |" + } + else { + $addedParametersColumns += " |" } - $addedParametersColumns += " $text |" } - else { - $addedParametersColumns += " |" + if ($hasParameters) { + $policyDisplayName = $_.displayName -replace "\n\r", " " -replace "\n", " " -replace "\r", " " + $policyDescription = $_.description -replace "\n\r", " " -replace "\n", " " -replace "\r", " " + $null = $allLines.Add("| $($_.category) | **$($policyDisplayName)**$($inTableAfterDisplayNameBreak)$($policyDescription) |$addedParametersColumns") } } - if ($hasParameters) { - $policyDisplayName = $_.displayName -replace "\n\r", " " -replace "\n", " " -replace "\r", " " - $policyDescription = $_.description -replace "\n\r", " " -replace "\n", " " -replace "\r", " " - $null = $allLines.Add("| $($_.category) | **$($policyDisplayName)**$($inTableAfterDisplayNameBreak)$($policyDescription) |$addedParametersColumns") + else { + Write-Verbose "Skipping manual policy: $($_.name)" } } - else { - Write-Verbose "Skipping manual policy: $($_.name)" - } } #endregion Policy Parameters