From 173e72cc72a492b065176a3122b41bd97cb86c28 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 19 Sep 2022 13:05:21 +0200 Subject: [PATCH 001/133] linter --- .github/workflows/linter.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 683f0e1c4a..af409cf677 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -27,3 +27,11 @@ jobs: DEFAULT_BRANCH: ${{ github.base_ref }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} FILTER_REGEX_EXCLUDE: '[module.tests.ps1|Get\-ModulesAsMarkdownTable.ps1|.*yml]' + + # Analyze repository with PSRule + - name: Run PSRule analysis + uses: microsoft/ps-rule@v2.4.0 + continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project + with: + modules: PSRule.Rules.Azure + baseline: Azure.Default From a4b0c10ffcbc32ad26289511109be486fd0213fc Mon Sep 17 00:00:00 2001 From: Elena Batanero <46710322+elbatane@users.noreply.github.com> Date: Mon, 19 Sep 2022 14:51:12 +0200 Subject: [PATCH 002/133] Create ps-rule.yaml (#2066) --- ps-rule.yaml | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 ps-rule.yaml diff --git a/ps-rule.yaml b/ps-rule.yaml new file mode 100644 index 0000000000..0e744129d9 --- /dev/null +++ b/ps-rule.yaml @@ -0,0 +1,57 @@ +# +# PSRule for Azure configuration +# + +# Please see the documentation for all configuration options: +# https://aka.ms/ps-rule/options +# https://aka.ms/ps-rule-azure/options + +# Configure binding for local rules. +binding: + preferTargetInfo: true + targetType: + - type + - resourceType + +# Require minimum versions of modules. +requires: + PSRule: '@pre >=2.4.0' + PSRule.Rules.Azure: '@pre >=1.19.2' + +# Use PSRule for Azure. +include: + module: + - PSRule.Rules.Azure + +output: + culture: + - 'en-US' + +input: + pathIgnore: + + # Ignore other files in the repository. + - '.vscode/' + - '.github/' + - '*.md' + + # Exclude modules but not tests. + - 'modules/**/*.bicep' + - '!modules/**/*.test.bicep' + +configuration: + # Enable automatic expansion of Azure parameter files. + AZURE_PARAMETER_FILE_EXPANSION: true + + # Enable automatic expansion of Azure Bicep source files. + AZURE_BICEP_FILE_EXPANSION: true + + # Configures the number of seconds to wait for build Bicep files. + AZURE_BICEP_FILE_EXPANSION_TIMEOUT: 10 + +# Suppression ignores rules for a specific Azure resource by name. +# suppression: +# Azure.KeyVault.Logs: +# - kvtest001 +# Azure.Storage.BlobPublicAccess: +# - sttest001 From dc031fc9ca39380a47d875aea890bf85475e5bfe Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 19 Sep 2022 14:58:15 +0200 Subject: [PATCH 003/133] exclude parameters --- ps-rule.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ps-rule.yaml b/ps-rule.yaml index 0e744129d9..2b4b323671 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -41,7 +41,7 @@ input: configuration: # Enable automatic expansion of Azure parameter files. - AZURE_PARAMETER_FILE_EXPANSION: true + # AZURE_PARAMETER_FILE_EXPANSION: true # Enable automatic expansion of Azure Bicep source files. AZURE_BICEP_FILE_EXPANSION: true From b1533f18c8014d3aa3ad52a150ab94578ba0cb78 Mon Sep 17 00:00:00 2001 From: Karel De Winter <40666689+kareldewinter@users.noreply.github.com> Date: Mon, 19 Sep 2022 17:28:46 +0200 Subject: [PATCH 004/133] [Hackaton] First test for PSRule exclusions on KeyVault (#2067) * Changed KeyVault workflow * Changed inputPath for KeyVault workflow * Comment other steps * Added exclude modules for version.json * Enable custom rules exclusions * Suppression of Azure.Resource.UseTags * Added suppression with namePrefix * Uncomment validation steps --- .github/workflows/ms.keyvault.vaults.yml | 18 +++++++++++ ps-rule.yaml | 40 +++++++++++++----------- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index 248035cc5b..535903f37d 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -81,6 +81,24 @@ jobs: modulePath: '${{ env.modulePath }}' moduleTestFilePath: '${{ env.moduleTestFilePath }}' + job_psrule_test: + name: 'PsRule Analyze repository' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + + # Run analysis by using the PSRule GitHub action. + - name: Run PSRule analysis + uses: microsoft/ps-rule@v2.4.0 + with: + modules: 'PSRule.Rules.Azure' + inputPath: '${{ env.modulePath }}/' + ############################# # Deployment validation # ############################# diff --git a/ps-rule.yaml b/ps-rule.yaml index 0e744129d9..a56f01414c 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -10,8 +10,8 @@ binding: preferTargetInfo: true targetType: - - type - - resourceType + - type + - resourceType # Require minimum versions of modules. requires: @@ -21,27 +21,27 @@ requires: # Use PSRule for Azure. include: module: - - PSRule.Rules.Azure + - PSRule.Rules.Azure output: culture: - - 'en-US' + - 'en-US' input: pathIgnore: + # Ignore other files in the repository. + - '.vscode/' + - '.github/' + - '*.md' - # Ignore other files in the repository. - - '.vscode/' - - '.github/' - - '*.md' - - # Exclude modules but not tests. - - 'modules/**/*.bicep' - - '!modules/**/*.test.bicep' + # Exclude modules but not tests. + - 'modules/**/*.bicep' + - '!modules/**/*.test.bicep' + - 'modules/**/*version.json' configuration: # Enable automatic expansion of Azure parameter files. - AZURE_PARAMETER_FILE_EXPANSION: true + AZURE_PARAMETER_FILE_EXPANSION: false # Enable automatic expansion of Azure Bicep source files. AZURE_BICEP_FILE_EXPANSION: true @@ -49,9 +49,13 @@ configuration: # Configures the number of seconds to wait for build Bicep files. AZURE_BICEP_FILE_EXPANSION_TIMEOUT: 10 +rule: + # Enable custom rules that don't exist in the baseline + includeLocal: false + exclude: + # Ignore the following rules for all resources + - Azure.KeyVault.PurgeProtect # Suppression ignores rules for a specific Azure resource by name. -# suppression: -# Azure.KeyVault.Logs: -# - kvtest001 -# Azure.Storage.BlobPublicAccess: -# - sttest001 +suppression: + Azure.Resource.UseTags: + - <>kvvmin001 From c71f513721782077d2624997963b836dec4ddad2 Mon Sep 17 00:00:00 2001 From: Elena Batanero <46710322+elbatane@users.noreply.github.com> Date: Mon, 19 Sep 2022 17:45:24 +0200 Subject: [PATCH 005/133] [Hackathon] Token replacement for the resource group module (#2068) * Added ps-rule.yaml * testing psrule * fixing typo yml * fixed typo * testing input path * Added token replacement task * Adding Azure login task * checking context * adding write-output * Adding setEnvironmentVariables * Added matrix and needs * Testing inputPath /${{ matrix.moduleTestFilePaths }} * Uncommented workflow Co-authored-by: Elena Batanero Garcia --- .../workflows/ms.resources.resourcegroups.yml | 122 +++++++++++++++++- 1 file changed, 119 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 17916c688f..dd79505e8e 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -81,9 +81,125 @@ jobs: modulePath: '${{ env.modulePath }}' moduleTestFilePath: '${{ env.moduleTestFilePath }}' - ############################# - # Deployment validation # - ############################# + job_psrule_test: + name: 'PsRule Analyze repository' + runs-on: ubuntu-latest + needs: + - job_initialize_pipeline + strategy: + fail-fast: false + matrix: + moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + steps: + + - name: Checkout + uses: actions/checkout@v3 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: Azure Login + uses: Azure/login@v1 + with: + creds: ${{ env.AZURE_CREDENTIALS }} + enable-AzPSSession: true + + # [Token replacement] task(s) + # --------------------------- + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + $parameterFilePath = "" + $customParameterFileTokens = "" + # Grouping task logs + Write-Output '::group::Replace tokens in template file' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # Get target files + $targetFileList = @($templateFilePath) + if(-not [String]::IsNullOrEmpty($parameterFilePath)) { + $targetFileList += $parameterFilePath + } + + # Get Service Principal Object ID + $context = Get-AzContext + Write-Output 'Checking context' + $context + $servicePrincipalAppId = $context.Account.Id + $servicePrincipal = Get-AzADServicePrincipal -ApplicationId $servicePrincipalAppId + $servicePrincipalObjectId = $servicePrincipal.Id + + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = $targetFileList + Tokens = @{} + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' + } + + # Add enforced tokens + $ConvertTokensInputs.Tokens += @{ + resourceGroupName = '${{ env.resourceGroupName }}' + subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + tenantId = '${{ env.ARM_TENANT_ID }}' + } + + # Add local (source control) tokens + $tokenMap = @{} + foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + } + Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + $ConvertTokensInputs.Tokens += $tokenMap + + # Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($ConvertTokensInputs.Tokens['namePrefix'])){ + Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + $ConvertTokensInputs.Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + } + + # Add custom tokens (passed in via the pipeline) + if(-not [String]::IsNullOrEmpty($customParameterFileTokens)) { + $customTokens = $customParameterFileTokens| ConvertFrom-Json -AsHashTable + Write-Verbose ('Using custom parameter file tokens [{0}]' -f ($customTokens.Keys -join ', ')) -Verbose + $ConvertTokensInputs.Tokens += $customTokens + } + + Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Token Replacement Functionality [For Module] + $null = Convert-TokensInFileList @ConvertTokensInputs + + # Get target files for modules dependencies + $DependencyParameterFilePaths = [System.Collections.ArrayList]@() + $DependencyParameterFolders = Get-ChildItem -Path (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'dependencies') -Recurse -Filter 'parameters' -Directory + foreach ($FolderPath in $DependencyParameterFolders.FullName) { + $DependencyParameterFilePaths += Get-ChildItem -Path $FolderPath -Recurse -Filter '*.json' + } + $ConvertTokensInputs.FilePathList = $DependencyParameterFilePaths + + # Invoke Token Replacement Functionality [For Dependencies] + $null = Convert-TokensInFileList @ConvertTokensInputs + + Write-Output '::endgroup::' + + # Run analysis by using the PSRule GitHub action. + - name: Run PSRule analysis + uses: microsoft/ps-rule@v2.4.0 + with: + modules: 'PSRule.Rules.Azure' + inputPath: 'modules/Microsoft.Resources/resourceGroups/${{ matrix.moduleTestFilePaths }}' + + + ############################ + # Deployment validation # + ############################ job_module_deploy_validation: runs-on: ubuntu-20.04 name: 'Deployment validation' From 9a0a600b6b7811cb3e69694e83281db0201ab17e Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 19 Sep 2022 18:26:44 +0200 Subject: [PATCH 006/133] pathIgnore --- ps-rule.yaml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/ps-rule.yaml b/ps-rule.yaml index a56f01414c..92357e437d 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -30,14 +30,21 @@ output: input: pathIgnore: # Ignore other files in the repository. - - '.vscode/' - - '.github/' - - '*.md' + # - '.azuredevops/' + # - '.github/' + # - '.vscode/' + # - 'constructs/' + # - 'docs/' + # - 'utilities' + - '**/*' + # - '*.md' + # # - '!**/.tests/*.bicep' + # Exclude modules but not tests. - - 'modules/**/*.bicep' + # - 'modules/**/*.bicep' - '!modules/**/*.test.bicep' - - 'modules/**/*version.json' + # - 'modules/**/*version.json' configuration: # Enable automatic expansion of Azure parameter files. From 9cc1d1f3dc77befc1c2fbe1839da66b5fcc378dd Mon Sep 17 00:00:00 2001 From: Karthik Venkatraman <44262238+karthikvenkat17@users.noreply.github.com> Date: Tue, 20 Sep 2022 08:45:06 +0100 Subject: [PATCH 007/133] [Hackathon] PSRule addition for VNet resource (#2070) * setting json expansion to false * add psrule to vnet workflow * exclude tagging psrule * custom psrule.yaml per module * typo in psrule.yaml * updated typo in psrule.yaml * uncomment deployment job Co-authored-by: Karthik Venkatraman --- .../workflows/ms.network.virtualnetworks.yml | 34 ++++++++-- .../virtualNetworks/.test/vnet-ps-rule.yaml | 63 +++++++++++++++++++ ps-rule.yaml | 1 + 3 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index dcd2810438..f09c176e38 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -60,6 +60,26 @@ jobs: removeDeployment: ${{ steps.get-workflow-param.outputs.removeDeployment }} moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + job_psrule_validation: + runs-on: ubuntu-20.04 + name: 'psrule-validation' + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: Run PSRule analysis + uses: microsoft/ps-rule@main + with: + modules: 'PSRule.Rules.Azure' + inputPath: '${{ env.modulePath }}/' + outputFormat: Sarif + option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' + + + ######################### # Static validation # ######################### @@ -81,9 +101,11 @@ jobs: modulePath: '${{ env.modulePath }}' moduleTestFilePath: '${{ env.moduleTestFilePath }}' - ############################# - # Deployment validation # - ############################# + + + # ############################# + # # Deployment validation # + # ############################# job_module_deploy_validation: runs-on: ubuntu-20.04 name: 'Deployment validation' @@ -113,9 +135,9 @@ jobs: managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - ################## - # Publishing # - ################## + # ################## + # # Publishing # + # ################## job_publish_module: name: 'Publishing' if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' diff --git a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml new file mode 100644 index 0000000000..a82192e29f --- /dev/null +++ b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml @@ -0,0 +1,63 @@ +# +# PSRule for Azure configuration +# + +# Please see the documentation for all configuration options: +# https://aka.ms/ps-rule/options +# https://aka.ms/ps-rule-azure/options + +# Configure binding for local rules. +binding: + preferTargetInfo: true + targetType: + - type + - resourceType + +# Require minimum versions of modules. +requires: + PSRule: '@pre >=2.4.0' + PSRule.Rules.Azure: '@pre >=1.19.2' + +# Use PSRule for Azure. +include: + module: + - PSRule.Rules.Azure + +output: + culture: + - 'en-US' + +input: + pathIgnore: + # Ignore other files in the repository. + - '.vscode/' + - '.github/' + - '*.md' + + # Exclude modules but not tests. + - 'modules/**/*.bicep' + - '!modules/**/*.test.bicep' + - 'modules/**/*version.json' + +configuration: + # Enable automatic expansion of Azure parameter files. + AZURE_PARAMETER_FILE_EXPANSION: false + + # Enable automatic expansion of Azure Bicep source files. + AZURE_BICEP_FILE_EXPANSION: true + + # Configures the number of seconds to wait for build Bicep files. + AZURE_BICEP_FILE_EXPANSION_TIMEOUT: 10 + +rule: + # Enable custom rules that don't exist in the baseline + includeLocal: false + exclude: + # Ignore the following rules for all resources + - Azure.Resource.UseTags + +# Suppression ignores rules for a specific Azure resource by name. +#suppression: +# Azure.Resource.UseTags: +# - <>kvvmin001 + diff --git a/ps-rule.yaml b/ps-rule.yaml index a56f01414c..4b24175416 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -55,6 +55,7 @@ rule: exclude: # Ignore the following rules for all resources - Azure.KeyVault.PurgeProtect + # Suppression ignores rules for a specific Azure resource by name. suppression: Azure.Resource.UseTags: From 855f16172ecf8710f4113450a444b7886c2e0359 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 10:55:33 +0200 Subject: [PATCH 008/133] clean up token replacement --- .../workflows/ms.resources.resourcegroups.yml | 182 +++++++++--------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index dd79505e8e..ca5e073009 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -91,19 +91,17 @@ jobs: matrix: moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} steps: - - name: Checkout uses: actions/checkout@v3 - name: Set environment variables uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} - - name: Azure Login - uses: Azure/login@v1 - with: - creds: ${{ env.AZURE_CREDENTIALS }} - enable-AzPSSession: true - + # - name: Azure Login + # uses: Azure/login@v1 + # with: + # creds: ${{ env.AZURE_CREDENTIALS }} + # enable-AzPSSession: true # [Token replacement] task(s) # --------------------------- - name: 'Replace tokens in template file' @@ -112,8 +110,8 @@ jobs: azPSVersion: 'latest' inlineScript: | $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - $parameterFilePath = "" - $customParameterFileTokens = "" + # # $parameterFilePath = "" + # # $customParameterFileTokens = "" # Grouping task logs Write-Output '::group::Replace tokens in template file' @@ -121,22 +119,23 @@ jobs: . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') # Get target files - $targetFileList = @($templateFilePath) - if(-not [String]::IsNullOrEmpty($parameterFilePath)) { - $targetFileList += $parameterFilePath - } + # # $targetFileList = @($templateFilePath) + # # if(-not [String]::IsNullOrEmpty($parameterFilePath)) { + # # $targetFileList += $parameterFilePath + # # } - # Get Service Principal Object ID - $context = Get-AzContext - Write-Output 'Checking context' - $context - $servicePrincipalAppId = $context.Account.Id - $servicePrincipal = Get-AzADServicePrincipal -ApplicationId $servicePrincipalAppId - $servicePrincipalObjectId = $servicePrincipal.Id + # # Get Service Principal Object ID + # $context = Get-AzContext + # Write-Output 'Checking context' + # $context + # $servicePrincipalAppId = $context.Account.Id + # $servicePrincipal = Get-AzADServicePrincipal -ApplicationId $servicePrincipalAppId + # $servicePrincipalObjectId = $servicePrincipal.Id # Construct Token Function Input $ConvertTokensInputs = @{ - FilePathList = $targetFileList + # # FilePathList = $targetFileList + FilePathList = @($templateFilePath) Tokens = @{} TokenPrefix = '${{ env.tokenPrefix }}' TokenSuffix = '${{ env.tokenSuffix }}' @@ -176,16 +175,16 @@ jobs: # Invoke Token Replacement Functionality [For Module] $null = Convert-TokensInFileList @ConvertTokensInputs - # Get target files for modules dependencies - $DependencyParameterFilePaths = [System.Collections.ArrayList]@() - $DependencyParameterFolders = Get-ChildItem -Path (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'dependencies') -Recurse -Filter 'parameters' -Directory - foreach ($FolderPath in $DependencyParameterFolders.FullName) { - $DependencyParameterFilePaths += Get-ChildItem -Path $FolderPath -Recurse -Filter '*.json' - } - $ConvertTokensInputs.FilePathList = $DependencyParameterFilePaths + # # # Get target files for modules dependencies + # # $DependencyParameterFilePaths = [System.Collections.ArrayList]@() + # # $DependencyParameterFolders = Get-ChildItem -Path (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'dependencies') -Recurse -Filter 'parameters' -Directory + # # foreach ($FolderPath in $DependencyParameterFolders.FullName) { + # # $DependencyParameterFilePaths += Get-ChildItem -Path $FolderPath -Recurse -Filter '*.json' + # # } + # # $ConvertTokensInputs.FilePathList = $DependencyParameterFilePaths - # Invoke Token Replacement Functionality [For Dependencies] - $null = Convert-TokensInFileList @ConvertTokensInputs + # # # Invoke Token Replacement Functionality [For Dependencies] + # # $null = Convert-TokensInFileList @ConvertTokensInputs Write-Output '::endgroup::' @@ -197,65 +196,66 @@ jobs: inputPath: 'modules/Microsoft.Resources/resourceGroups/${{ matrix.moduleTestFilePaths }}' - ############################ - # Deployment validation # - ############################ - job_module_deploy_validation: - runs-on: ubuntu-20.04 - name: 'Deployment validation' - needs: - - job_initialize_pipeline - - job_module_pester_validation - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - uses: ./.github/actions/templates/validateModuleDeployment - with: - templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - location: '${{ env.location }}' - resourceGroupName: '${{ env.resourceGroupName }}' - subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + # ############################ + # # Deployment validation # + # ############################ + # job_module_deploy_validation: + # runs-on: ubuntu-20.04 + # name: 'Deployment validation' + # needs: + # - job_initialize_pipeline + # - job_module_pester_validation + # - job_psrule_test + # strategy: + # fail-fast: false + # matrix: + # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + # steps: + # - name: 'Checkout' + # uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' + # uses: ./.github/actions/templates/validateModuleDeployment + # with: + # templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # location: '${{ env.location }}' + # resourceGroupName: '${{ env.resourceGroupName }}' + # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - ################## - # Publishing # - ################## - job_publish_module: - name: 'Publishing' - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - runs-on: ubuntu-20.04 - needs: - - job_module_deploy_validation - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Publishing' - uses: ./.github/actions/templates/publishModule - with: - templateFilePath: '${{ env.modulePath }}/deploy.bicep' - templateSpecsRGName: '${{ env.templateSpecsRGName }}' - templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - templateSpecsDescription: '${{ env.templateSpecsDescription }}' - templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - bicepRegistryName: '${{ env.bicepRegistryName }}' - bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' + # ################## + # # Publishing # + # ################## + # job_publish_module: + # name: 'Publishing' + # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + # runs-on: ubuntu-20.04 + # needs: + # - job_module_deploy_validation + # steps: + # - name: 'Checkout' + # uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Publishing' + # uses: ./.github/actions/templates/publishModule + # with: + # templateFilePath: '${{ env.modulePath }}/deploy.bicep' + # templateSpecsRGName: '${{ env.templateSpecsRGName }}' + # templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + # templateSpecsDescription: '${{ env.templateSpecsDescription }}' + # templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + # bicepRegistryName: '${{ env.bicepRegistryName }}' + # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' From 81b5a9f872a9896e64df5c0f4526d2780c085187 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 11:44:08 +0200 Subject: [PATCH 009/133] clean up token replacement further --- .github/workflows/ms.resources.resourcegroups.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index ca5e073009..28afe1f969 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -163,12 +163,12 @@ jobs: $ConvertTokensInputs.Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' } - # Add custom tokens (passed in via the pipeline) - if(-not [String]::IsNullOrEmpty($customParameterFileTokens)) { - $customTokens = $customParameterFileTokens| ConvertFrom-Json -AsHashTable - Write-Verbose ('Using custom parameter file tokens [{0}]' -f ($customTokens.Keys -join ', ')) -Verbose - $ConvertTokensInputs.Tokens += $customTokens - } + # # Add custom tokens (passed in via the pipeline) + # if(-not [String]::IsNullOrEmpty($customParameterFileTokens)) { + # $customTokens = $customParameterFileTokens| ConvertFrom-Json -AsHashTable + # Write-Verbose ('Using custom parameter file tokens [{0}]' -f ($customTokens.Keys -join ', ')) -Verbose + # $ConvertTokensInputs.Tokens += $customTokens + # } Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose From 7aa8000c649722bf395596847b01d1d8a976894f Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 11:55:09 +0200 Subject: [PATCH 010/133] resize token replacement --- .../workflows/ms.resources.resourcegroups.yml | 55 +++++-------------- 1 file changed, 14 insertions(+), 41 deletions(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 28afe1f969..3119d8d7d6 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -110,20 +110,12 @@ jobs: azPSVersion: 'latest' inlineScript: | $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # # $parameterFilePath = "" - # # $customParameterFileTokens = "" # Grouping task logs Write-Output '::group::Replace tokens in template file' # Load used functions . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - # Get target files - # # $targetFileList = @($templateFilePath) - # # if(-not [String]::IsNullOrEmpty($parameterFilePath)) { - # # $targetFileList += $parameterFilePath - # # } - # # Get Service Principal Object ID # $context = Get-AzContext # Write-Output 'Checking context' @@ -132,60 +124,41 @@ jobs: # $servicePrincipal = Get-AzADServicePrincipal -ApplicationId $servicePrincipalAppId # $servicePrincipalObjectId = $servicePrincipal.Id - # Construct Token Function Input - $ConvertTokensInputs = @{ - # # FilePathList = $targetFileList - FilePathList = @($templateFilePath) - Tokens = @{} - TokenPrefix = '${{ env.tokenPrefix }}' - TokenSuffix = '${{ env.tokenSuffix }}' - } - - # Add enforced tokens - $ConvertTokensInputs.Tokens += @{ + # Polulate tokens + $Tokens = @{ resourceGroupName = '${{ env.resourceGroupName }}' subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' tenantId = '${{ env.ARM_TENANT_ID }}' } - # Add local (source control) tokens + ## Add local (source control) tokens $tokenMap = @{} foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } } Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - $ConvertTokensInputs.Tokens += $tokenMap + $Tokens += $tokenMap - # Swap 'namePrefix' token if empty and provided as a GitHub secret - if([String]::IsNullOrEmpty($ConvertTokensInputs.Tokens['namePrefix'])){ + ## Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - $ConvertTokensInputs.Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' } - # # Add custom tokens (passed in via the pipeline) - # if(-not [String]::IsNullOrEmpty($customParameterFileTokens)) { - # $customTokens = $customParameterFileTokens| ConvertFrom-Json -AsHashTable - # Write-Verbose ('Using custom parameter file tokens [{0}]' -f ($customTokens.Keys -join ', ')) -Verbose - # $ConvertTokensInputs.Tokens += $customTokens - # } + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = @($templateFilePath) + Tokens = $Tokens + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' + } Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose # Invoke Token Replacement Functionality [For Module] $null = Convert-TokensInFileList @ConvertTokensInputs - # # # Get target files for modules dependencies - # # $DependencyParameterFilePaths = [System.Collections.ArrayList]@() - # # $DependencyParameterFolders = Get-ChildItem -Path (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'dependencies') -Recurse -Filter 'parameters' -Directory - # # foreach ($FolderPath in $DependencyParameterFolders.FullName) { - # # $DependencyParameterFilePaths += Get-ChildItem -Path $FolderPath -Recurse -Filter '*.json' - # # } - # # $ConvertTokensInputs.FilePathList = $DependencyParameterFilePaths - - # # # Invoke Token Replacement Functionality [For Dependencies] - # # $null = Convert-TokensInFileList @ConvertTokensInputs - Write-Output '::endgroup::' # Run analysis by using the PSRule GitHub action. From f10a381bd9ea2fe6787e269f3dee5aa0ba7e08f3 Mon Sep 17 00:00:00 2001 From: Karthik Venkatraman <44262238+karthikvenkat17@users.noreply.github.com> Date: Tue, 20 Sep 2022 11:36:17 +0100 Subject: [PATCH 011/133] [Hackathon] Added suppression group for dependency resources (#2071) * suppress dependancy * comment deployment validation job * add supress yaml * supress yaml poc * rule filter * suppress rule filter * add baseline for suppression * baseline for suppression * filed suppress poc * suppress poc * psrule suppression * Updates to suppression yaml Co-authored-by: Karthik Venkatraman --- .ps-rule/dep-suppress.Rule.yaml | 14 ++++++++++++++ .../virtualNetworks/.test/vnet-ps-rule.yaml | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 .ps-rule/dep-suppress.Rule.yaml diff --git a/.ps-rule/dep-suppress.Rule.yaml b/.ps-rule/dep-suppress.Rule.yaml new file mode 100644 index 0000000000..04681c9def --- /dev/null +++ b/.ps-rule/dep-suppress.Rule.yaml @@ -0,0 +1,14 @@ +--- +# Synopsis: Suppress Rules for dependancies +apiVersion: github.com/microsoft/PSRule/v1 +kind: SuppressionGroup +metadata: + name: 'SuppressDependancy' +spec: + if: + name: '.' + startsWith: + - 'dep' + + + \ No newline at end of file diff --git a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml index a82192e29f..589d2ab3b7 100644 --- a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml +++ b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml @@ -58,6 +58,6 @@ rule: # Suppression ignores rules for a specific Azure resource by name. #suppression: -# Azure.Resource.UseTags: -# - <>kvvmin001 +# Azure.Identity.UserAssignedName: +# - 'dep*' From 15ceeaa37f87e954d0748439ce72bb03ad71ba42 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 12:38:46 +0200 Subject: [PATCH 012/133] typo --- .github/workflows/ms.resources.resourcegroups.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 3119d8d7d6..4b9c15d6dd 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -124,7 +124,7 @@ jobs: # $servicePrincipal = Get-AzADServicePrincipal -ApplicationId $servicePrincipalAppId # $servicePrincipalObjectId = $servicePrincipal.Id - # Polulate tokens + # Populate tokens $Tokens = @{ resourceGroupName = '${{ env.resourceGroupName }}' subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' From 153fad882e40c8064d5fc6c19b5a25c4d4a735d4 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 12:47:14 +0200 Subject: [PATCH 013/133] modulePath --- .../workflows/ms.resources.resourcegroups.yml | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 4b9c15d6dd..9714d72c00 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -82,7 +82,7 @@ jobs: moduleTestFilePath: '${{ env.moduleTestFilePath }}' job_psrule_test: - name: 'PsRule Analyze repository' + name: 'PsRule in-flight validation' runs-on: ubuntu-latest needs: - job_initialize_pipeline @@ -97,13 +97,6 @@ jobs: uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} - # - name: Azure Login - # uses: Azure/login@v1 - # with: - # creds: ${{ env.AZURE_CREDENTIALS }} - # enable-AzPSSession: true - # [Token replacement] task(s) - # --------------------------- - name: 'Replace tokens in template file' uses: azure/powershell@v1 with: @@ -116,14 +109,6 @@ jobs: # Load used functions . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - # # Get Service Principal Object ID - # $context = Get-AzContext - # Write-Output 'Checking context' - # $context - # $servicePrincipalAppId = $context.Account.Id - # $servicePrincipal = Get-AzADServicePrincipal -ApplicationId $servicePrincipalAppId - # $servicePrincipalObjectId = $servicePrincipal.Id - # Populate tokens $Tokens = @{ resourceGroupName = '${{ env.resourceGroupName }}' @@ -160,14 +145,13 @@ jobs: $null = Convert-TokensInFileList @ConvertTokensInputs Write-Output '::endgroup::' - # Run analysis by using the PSRule GitHub action. - name: Run PSRule analysis uses: microsoft/ps-rule@v2.4.0 + continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: 'modules/Microsoft.Resources/resourceGroups/${{ matrix.moduleTestFilePaths }}' - + inputPath: '${{modulePath}}/${{ matrix.moduleTestFilePaths }}' # ############################ # # Deployment validation # From c0ed856f073c0ffd3b5779103529227b372cfd0f Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 12:52:27 +0200 Subject: [PATCH 014/133] job name --- .github/workflows/ms.resources.resourcegroups.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 9714d72c00..8e6e3a73c2 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -82,7 +82,7 @@ jobs: moduleTestFilePath: '${{ env.moduleTestFilePath }}' job_psrule_test: - name: 'PsRule in-flight validation' + name: 'PsRule inflight validation' runs-on: ubuntu-latest needs: - job_initialize_pipeline From dea25f8088f8bd2ce2cafe4c7d243cb261ffdf4d Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 12:54:24 +0200 Subject: [PATCH 015/133] no psrule --- .../workflows/ms.resources.resourcegroups.yml | 126 +++++++++--------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 8e6e3a73c2..8a396eb1f5 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -81,77 +81,77 @@ jobs: modulePath: '${{ env.modulePath }}' moduleTestFilePath: '${{ env.moduleTestFilePath }}' - job_psrule_test: - name: 'PsRule inflight validation' - runs-on: ubuntu-latest - needs: - - job_initialize_pipeline - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Replace tokens in template file' - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # Grouping task logs - Write-Output '::group::Replace tokens in template file' + # job_psrule_test: + # name: 'PsRule inflight validation' + # runs-on: ubuntu-latest + # needs: + # - job_initialize_pipeline + # strategy: + # fail-fast: false + # matrix: + # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + # steps: + # - name: Checkout + # uses: actions/checkout@v3 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Replace tokens in template file' + # uses: azure/powershell@v1 + # with: + # azPSVersion: 'latest' + # inlineScript: | + # $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # # Grouping task logs + # Write-Output '::group::Replace tokens in template file' - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + # # Load used functions + # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - # Populate tokens - $Tokens = @{ - resourceGroupName = '${{ env.resourceGroupName }}' - subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - tenantId = '${{ env.ARM_TENANT_ID }}' - } + # # Populate tokens + # $Tokens = @{ + # resourceGroupName = '${{ env.resourceGroupName }}' + # subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + # managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + # tenantId = '${{ env.ARM_TENANT_ID }}' + # } - ## Add local (source control) tokens - $tokenMap = @{} - foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - } - Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - $Tokens += $tokenMap + # ## Add local (source control) tokens + # $tokenMap = @{} + # foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + # $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + # } + # Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + # $Tokens += $tokenMap - ## Swap 'namePrefix' token if empty and provided as a GitHub secret - if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - } + # ## Swap 'namePrefix' token if empty and provided as a GitHub secret + # if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + # Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + # $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + # } - # Construct Token Function Input - $ConvertTokensInputs = @{ - FilePathList = @($templateFilePath) - Tokens = $Tokens - TokenPrefix = '${{ env.tokenPrefix }}' - TokenSuffix = '${{ env.tokenSuffix }}' - } + # # Construct Token Function Input + # $ConvertTokensInputs = @{ + # FilePathList = @($templateFilePath) + # Tokens = $Tokens + # TokenPrefix = '${{ env.tokenPrefix }}' + # TokenSuffix = '${{ env.tokenSuffix }}' + # } - Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + # Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - # Invoke Token Replacement Functionality [For Module] - $null = Convert-TokensInFileList @ConvertTokensInputs + # # Invoke Token Replacement Functionality [For Module] + # $null = Convert-TokensInFileList @ConvertTokensInputs - Write-Output '::endgroup::' - # Run analysis by using the PSRule GitHub action. - - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.4.0 - continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project - with: - modules: 'PSRule.Rules.Azure' - inputPath: '${{modulePath}}/${{ matrix.moduleTestFilePaths }}' + # Write-Output '::endgroup::' + # # Run analysis by using the PSRule GitHub action. + # - name: Run PSRule analysis + # uses: microsoft/ps-rule@v2.4.0 + # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project + # with: + # modules: 'PSRule.Rules.Azure' + # inputPath: '${{modulePath}}/${{ matrix.moduleTestFilePaths }}' # ############################ # # Deployment validation # From d2a89d85666ad8215ea7f0beea212dd298e1ee21 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 12:55:56 +0200 Subject: [PATCH 016/133] no psrule step --- .../workflows/ms.resources.resourcegroups.yml | 143 +++++++++--------- 1 file changed, 72 insertions(+), 71 deletions(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 8a396eb1f5..274d7753e0 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -81,77 +81,78 @@ jobs: modulePath: '${{ env.modulePath }}' moduleTestFilePath: '${{ env.moduleTestFilePath }}' - # job_psrule_test: - # name: 'PsRule inflight validation' - # runs-on: ubuntu-latest - # needs: - # - job_initialize_pipeline - # strategy: - # fail-fast: false - # matrix: - # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - # steps: - # - name: Checkout - # uses: actions/checkout@v3 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Replace tokens in template file' - # uses: azure/powershell@v1 - # with: - # azPSVersion: 'latest' - # inlineScript: | - # $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # # Grouping task logs - # Write-Output '::group::Replace tokens in template file' - - # # Load used functions - # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # # Populate tokens - # $Tokens = @{ - # resourceGroupName = '${{ env.resourceGroupName }}' - # subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - # managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - # tenantId = '${{ env.ARM_TENANT_ID }}' - # } - - # ## Add local (source control) tokens - # $tokenMap = @{} - # foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - # $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - # } - # Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - # $Tokens += $tokenMap - - # ## Swap 'namePrefix' token if empty and provided as a GitHub secret - # if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - # Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - # $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - # } - - # # Construct Token Function Input - # $ConvertTokensInputs = @{ - # FilePathList = @($templateFilePath) - # Tokens = $Tokens - # TokenPrefix = '${{ env.tokenPrefix }}' - # TokenSuffix = '${{ env.tokenSuffix }}' - # } - - # Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # # Invoke Token Replacement Functionality [For Module] - # $null = Convert-TokensInFileList @ConvertTokensInputs - - # Write-Output '::endgroup::' - # # Run analysis by using the PSRule GitHub action. - # - name: Run PSRule analysis - # uses: microsoft/ps-rule@v2.4.0 - # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project - # with: - # modules: 'PSRule.Rules.Azure' - # inputPath: '${{modulePath}}/${{ matrix.moduleTestFilePaths }}' + job_psrule_test: + name: 'PsRule inflight validation' + runs-on: ubuntu-latest + needs: + - job_initialize_pipeline + strategy: + fail-fast: false + matrix: + moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # Grouping task logs + Write-Output '::group::Replace tokens in template file' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # Populate tokens + $Tokens = @{ + resourceGroupName = '${{ env.resourceGroupName }}' + subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + tenantId = '${{ env.ARM_TENANT_ID }}' + } + + ## Add local (source control) tokens + $tokenMap = @{} + foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + } + Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + $Tokens += $tokenMap + + ## Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + } + + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = @($templateFilePath) + Tokens = $Tokens + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' + } + + Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Token Replacement Functionality [For Module] + $null = Convert-TokensInFileList @ConvertTokensInputs + + Write-Output '::endgroup::' + + # # Run analysis by using the PSRule GitHub action. + # - name: Run PSRule analysis + # uses: microsoft/ps-rule@v2.4.0 + # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project + # with: + # modules: 'PSRule.Rules.Azure' + # inputPath: '${{modulePath}}/${{ matrix.moduleTestFilePaths }}' # ############################ # # Deployment validation # From 19cfe714f962aefda8918417dc427b1f7f1155e1 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 12:57:33 +0200 Subject: [PATCH 017/133] fix inputpath --- .github/workflows/ms.resources.resourcegroups.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 274d7753e0..c24891d422 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -146,13 +146,13 @@ jobs: Write-Output '::endgroup::' - # # Run analysis by using the PSRule GitHub action. - # - name: Run PSRule analysis - # uses: microsoft/ps-rule@v2.4.0 - # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project - # with: - # modules: 'PSRule.Rules.Azure' - # inputPath: '${{modulePath}}/${{ matrix.moduleTestFilePaths }}' + # Run analysis by using the PSRule GitHub action. + - name: Run PSRule analysis + uses: microsoft/ps-rule@v2.4.0 + continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project + with: + modules: 'PSRule.Rules.Azure' + inputPath: '${{env.modulePath}}/${{ matrix.moduleTestFilePaths }}' # ############################ # # Deployment validation # From 20e405bea37d67321cac2278189faaacbbd7ca4b Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 13:03:32 +0200 Subject: [PATCH 018/133] comment out continue on error option --- .github/workflows/ms.resources.resourcegroups.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index c24891d422..7d399272ec 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -149,7 +149,7 @@ jobs: # Run analysis by using the PSRule GitHub action. - name: Run PSRule analysis uses: microsoft/ps-rule@v2.4.0 - continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project + # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' inputPath: '${{env.modulePath}}/${{ matrix.moduleTestFilePaths }}' From 4a07c2a078df3235fa04fc85a1e57e87370c22ba Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 13:11:13 +0200 Subject: [PATCH 019/133] Align KV --- .github/workflows/ms.keyvault.vaults.yml | 184 ++++++++++++------ .../workflows/ms.resources.resourcegroups.yml | 8 +- 2 files changed, 124 insertions(+), 68 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index 535903f37d..4af837dbcb 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -82,8 +82,14 @@ jobs: moduleTestFilePath: '${{ env.moduleTestFilePath }}' job_psrule_test: - name: 'PsRule Analyze repository' + name: 'PsRule inflight validation' runs-on: ubuntu-latest + needs: + - job_initialize_pipeline + strategy: + fail-fast: false + matrix: + moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} steps: - name: Checkout uses: actions/checkout@v3 @@ -91,73 +97,123 @@ jobs: uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # Grouping task logs + Write-Output '::group::Replace tokens in template file' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # Populate tokens + $Tokens = @{ + resourceGroupName = '${{ env.resourceGroupName }}' + subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + tenantId = '${{ env.ARM_TENANT_ID }}' + } + + ## Add local (source control) tokens + $tokenMap = @{} + foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + } + Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + $Tokens += $tokenMap + + ## Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + } + + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = @($templateFilePath) + Tokens = $Tokens + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' + } + + Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Token Replacement Functionality [For Module] + $null = Convert-TokensInFileList @ConvertTokensInputs + + Write-Output '::endgroup::' # Run analysis by using the PSRule GitHub action. - name: Run PSRule analysis uses: microsoft/ps-rule@v2.4.0 + # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulePath }}/' + inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - ############################# - # Deployment validation # - ############################# - job_module_deploy_validation: - runs-on: ubuntu-20.04 - name: 'Deployment validation' - needs: - - job_initialize_pipeline - - job_module_pester_validation - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - uses: ./.github/actions/templates/validateModuleDeployment - with: - templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - location: '${{ env.location }}' - resourceGroupName: '${{ env.resourceGroupName }}' - subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - - ################## - # Publishing # - ################## - job_publish_module: - name: 'Publishing' - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - runs-on: ubuntu-20.04 - needs: - - job_module_deploy_validation - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Publishing' - uses: ./.github/actions/templates/publishModule - with: - templateFilePath: '${{ env.modulePath }}/deploy.bicep' - templateSpecsRGName: '${{ env.templateSpecsRGName }}' - templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - templateSpecsDescription: '${{ env.templateSpecsDescription }}' - templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - bicepRegistryName: '${{ env.bicepRegistryName }}' - bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' + # ############################# + # # Deployment validation # + # ############################# + # job_module_deploy_validation: + # runs-on: ubuntu-20.04 + # name: 'Deployment validation' + # needs: + # - job_initialize_pipeline + # - job_module_pester_validation + # - job_psrule_test + # strategy: + # fail-fast: false + # matrix: + # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + # steps: + # - name: 'Checkout' + # uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' + # uses: ./.github/actions/templates/validateModuleDeployment + # with: + # templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # location: '${{ env.location }}' + # resourceGroupName: '${{ env.resourceGroupName }}' + # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + + # ################## + # # Publishing # + # ################## + # job_publish_module: + # name: 'Publishing' + # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + # runs-on: ubuntu-20.04 + # needs: + # - job_module_deploy_validation + # steps: + # - name: 'Checkout' + # uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Publishing' + # uses: ./.github/actions/templates/publishModule + # with: + # templateFilePath: '${{ env.modulePath }}/deploy.bicep' + # templateSpecsRGName: '${{ env.templateSpecsRGName }}' + # templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + # templateSpecsDescription: '${{ env.templateSpecsDescription }}' + # templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + # bicepRegistryName: '${{ env.bicepRegistryName }}' + # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 7d399272ec..9f7125e256 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -152,11 +152,11 @@ jobs: # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: '${{env.modulePath}}/${{ matrix.moduleTestFilePaths }}' + inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # ############################ - # # Deployment validation # - # ############################ + # ############################# + # # Deployment validation # + # ############################# # job_module_deploy_validation: # runs-on: ubuntu-20.04 # name: 'Deployment validation' From 589d70464205f297159b2193b73a0d3a039b4615 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 13:14:47 +0200 Subject: [PATCH 020/133] kv no psrule --- .github/workflows/ms.keyvault.vaults.yml | 120 +++++++++++------------ 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index 4af837dbcb..5c182dad91 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -93,66 +93,66 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Replace tokens in template file' - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # Grouping task logs - Write-Output '::group::Replace tokens in template file' - - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # Populate tokens - $Tokens = @{ - resourceGroupName = '${{ env.resourceGroupName }}' - subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - tenantId = '${{ env.ARM_TENANT_ID }}' - } - - ## Add local (source control) tokens - $tokenMap = @{} - foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - } - Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - $Tokens += $tokenMap - - ## Swap 'namePrefix' token if empty and provided as a GitHub secret - if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - } - - # Construct Token Function Input - $ConvertTokensInputs = @{ - FilePathList = @($templateFilePath) - Tokens = $Tokens - TokenPrefix = '${{ env.tokenPrefix }}' - TokenSuffix = '${{ env.tokenSuffix }}' - } - - Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # Invoke Token Replacement Functionality [For Module] - $null = Convert-TokensInFileList @ConvertTokensInputs - - Write-Output '::endgroup::' - - # Run analysis by using the PSRule GitHub action. - - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.4.0 - # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project - with: - modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Replace tokens in template file' + # uses: azure/powershell@v1 + # with: + # azPSVersion: 'latest' + # inlineScript: | + # $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # # Grouping task logs + # Write-Output '::group::Replace tokens in template file' + + # # Load used functions + # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # # Populate tokens + # $Tokens = @{ + # resourceGroupName = '${{ env.resourceGroupName }}' + # subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + # managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + # tenantId = '${{ env.ARM_TENANT_ID }}' + # } + + # ## Add local (source control) tokens + # $tokenMap = @{} + # foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + # $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + # } + # Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + # $Tokens += $tokenMap + + # ## Swap 'namePrefix' token if empty and provided as a GitHub secret + # if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + # Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + # $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + # } + + # # Construct Token Function Input + # $ConvertTokensInputs = @{ + # FilePathList = @($templateFilePath) + # Tokens = $Tokens + # TokenPrefix = '${{ env.tokenPrefix }}' + # TokenSuffix = '${{ env.tokenSuffix }}' + # } + + # Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # # Invoke Token Replacement Functionality [For Module] + # $null = Convert-TokensInFileList @ConvertTokensInputs + + # Write-Output '::endgroup::' + + # # Run analysis by using the PSRule GitHub action. + # - name: Run PSRule analysis + # uses: microsoft/ps-rule@v2.4.0 + # # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project + # with: + # modules: 'PSRule.Rules.Azure' + # inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' # ############################# # # Deployment validation # From 0b6c7ad4b2c0cb8dcaac02237ea35ea2b42fa519 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 13:16:23 +0200 Subject: [PATCH 021/133] replace --- .github/workflows/ms.keyvault.vaults.yml | 104 +++++++++++------------ 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index 5c182dad91..88ff8748f5 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -93,58 +93,58 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Replace tokens in template file' - # uses: azure/powershell@v1 - # with: - # azPSVersion: 'latest' - # inlineScript: | - # $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # # Grouping task logs - # Write-Output '::group::Replace tokens in template file' - - # # Load used functions - # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # # Populate tokens - # $Tokens = @{ - # resourceGroupName = '${{ env.resourceGroupName }}' - # subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - # managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - # tenantId = '${{ env.ARM_TENANT_ID }}' - # } - - # ## Add local (source control) tokens - # $tokenMap = @{} - # foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - # $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - # } - # Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - # $Tokens += $tokenMap - - # ## Swap 'namePrefix' token if empty and provided as a GitHub secret - # if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - # Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - # $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - # } - - # # Construct Token Function Input - # $ConvertTokensInputs = @{ - # FilePathList = @($templateFilePath) - # Tokens = $Tokens - # TokenPrefix = '${{ env.tokenPrefix }}' - # TokenSuffix = '${{ env.tokenSuffix }}' - # } - - # Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # # Invoke Token Replacement Functionality [For Module] - # $null = Convert-TokensInFileList @ConvertTokensInputs - - # Write-Output '::endgroup::' + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # Grouping task logs + Write-Output '::group::Replace tokens in template file' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # Populate tokens + $Tokens = @{ + resourceGroupName = '${{ env.resourceGroupName }}' + subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + tenantId = '${{ env.ARM_TENANT_ID }}' + } + + ## Add local (source control) tokens + $tokenMap = @{} + foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + } + Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + $Tokens += $tokenMap + + ## Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + } + + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = @($templateFilePath) + Tokens = $Tokens + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' + } + + Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Token Replacement Functionality [For Module] + $null = Convert-TokensInFileList @ConvertTokensInputs + + Write-Output '::endgroup::' # # Run analysis by using the PSRule GitHub action. # - name: Run PSRule analysis From 393bfb54dd0ed9c81a74ea734c925eb228f0b952 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 13:18:22 +0200 Subject: [PATCH 022/133] no replace --- .github/workflows/ms.keyvault.vaults.yml | 96 ++++++++++++------------ 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index 88ff8748f5..ee7cb005dd 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -97,54 +97,54 @@ jobs: uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} - - name: 'Replace tokens in template file' - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # Grouping task logs - Write-Output '::group::Replace tokens in template file' - - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # Populate tokens - $Tokens = @{ - resourceGroupName = '${{ env.resourceGroupName }}' - subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - tenantId = '${{ env.ARM_TENANT_ID }}' - } - - ## Add local (source control) tokens - $tokenMap = @{} - foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - } - Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - $Tokens += $tokenMap - - ## Swap 'namePrefix' token if empty and provided as a GitHub secret - if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - } - - # Construct Token Function Input - $ConvertTokensInputs = @{ - FilePathList = @($templateFilePath) - Tokens = $Tokens - TokenPrefix = '${{ env.tokenPrefix }}' - TokenSuffix = '${{ env.tokenSuffix }}' - } - - Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # Invoke Token Replacement Functionality [For Module] - $null = Convert-TokensInFileList @ConvertTokensInputs - - Write-Output '::endgroup::' + # - name: 'Replace tokens in template file' + # uses: azure/powershell@v1 + # with: + # azPSVersion: 'latest' + # inlineScript: | + # $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # # Grouping task logs + # Write-Output '::group::Replace tokens in template file' + + # # Load used functions + # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # # Populate tokens + # $Tokens = @{ + # resourceGroupName = '${{ env.resourceGroupName }}' + # subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + # managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + # tenantId = '${{ env.ARM_TENANT_ID }}' + # } + + # ## Add local (source control) tokens + # $tokenMap = @{} + # foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + # $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + # } + # Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + # $Tokens += $tokenMap + + # ## Swap 'namePrefix' token if empty and provided as a GitHub secret + # if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + # Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + # $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + # } + + # # Construct Token Function Input + # $ConvertTokensInputs = @{ + # FilePathList = @($templateFilePath) + # Tokens = $Tokens + # TokenPrefix = '${{ env.tokenPrefix }}' + # TokenSuffix = '${{ env.tokenSuffix }}' + # } + + # Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # # Invoke Token Replacement Functionality [For Module] + # $null = Convert-TokensInFileList @ConvertTokensInputs + + # Write-Output '::endgroup::' # # Run analysis by using the PSRule GitHub action. # - name: Run PSRule analysis From 16fab3698a20d0aa55a16135c743adf67d859cff Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 13:19:05 +0200 Subject: [PATCH 023/133] replace 1 --- .github/workflows/ms.keyvault.vaults.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index ee7cb005dd..1d2f5440d1 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -97,14 +97,14 @@ jobs: uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} - # - name: 'Replace tokens in template file' - # uses: azure/powershell@v1 - # with: - # azPSVersion: 'latest' - # inlineScript: | - # $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # # Grouping task logs - # Write-Output '::group::Replace tokens in template file' + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # Grouping task logs + Write-Output '::group::Replace tokens in template file' # # Load used functions # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') From 75f8b6cb1336032bef553f4b60d5578e6c5f774b Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 13:21:38 +0200 Subject: [PATCH 024/133] indent --- .github/workflows/ms.keyvault.vaults.yml | 112 +++++++++++------------ 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index 1d2f5440d1..19c89f5d5a 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -97,62 +97,62 @@ jobs: uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} - - name: 'Replace tokens in template file' - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # Grouping task logs - Write-Output '::group::Replace tokens in template file' - - # # Load used functions - # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # # Populate tokens - # $Tokens = @{ - # resourceGroupName = '${{ env.resourceGroupName }}' - # subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - # managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - # tenantId = '${{ env.ARM_TENANT_ID }}' - # } - - # ## Add local (source control) tokens - # $tokenMap = @{} - # foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - # $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - # } - # Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - # $Tokens += $tokenMap - - # ## Swap 'namePrefix' token if empty and provided as a GitHub secret - # if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - # Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - # $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - # } - - # # Construct Token Function Input - # $ConvertTokensInputs = @{ - # FilePathList = @($templateFilePath) - # Tokens = $Tokens - # TokenPrefix = '${{ env.tokenPrefix }}' - # TokenSuffix = '${{ env.tokenSuffix }}' - # } - - # Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # # Invoke Token Replacement Functionality [For Module] - # $null = Convert-TokensInFileList @ConvertTokensInputs - - # Write-Output '::endgroup::' - - # # Run analysis by using the PSRule GitHub action. - # - name: Run PSRule analysis - # uses: microsoft/ps-rule@v2.4.0 - # # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project - # with: - # modules: 'PSRule.Rules.Azure' - # inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # Grouping task logs + Write-Output '::group::Replace tokens in template file' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # Populate tokens + $Tokens = @{ + resourceGroupName = '${{ env.resourceGroupName }}' + subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + tenantId = '${{ env.ARM_TENANT_ID }}' + } + + ## Add local (source control) tokens + $tokenMap = @{} + foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + } + Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + $Tokens += $tokenMap + + ## Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + } + + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = @($templateFilePath) + Tokens = $Tokens + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' + } + + Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Token Replacement Functionality [For Module] + $null = Convert-TokensInFileList @ConvertTokensInputs + + Write-Output '::endgroup::' + + # Run analysis by using the PSRule GitHub action. + - name: Run PSRule analysis + uses: microsoft/ps-rule@v2.4.0 + # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project + with: + modules: 'PSRule.Rules.Azure' + inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' # ############################# # # Deployment validation # From 5c417dbfdfffa0285698bb08a0a04d7e7be2703d Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 13:26:44 +0200 Subject: [PATCH 025/133] align vnet --- .../workflows/ms.network.virtualnetworks.yml | 92 +++++++++++++++++-- 1 file changed, 84 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index f09c176e38..b575293cc0 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -60,26 +60,102 @@ jobs: removeDeployment: ${{ steps.get-workflow-param.outputs.removeDeployment }} moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} - job_psrule_validation: + ######################### + # Static validation # + ######################### + job_module_pester_validation: runs-on: ubuntu-20.04 - name: 'psrule-validation' + name: 'Static validation' steps: - - name: Checkout + - name: 'Checkout' uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Run tests' + uses: ./.github/actions/templates/validateModulePester + with: + modulePath: '${{ env.modulePath }}' + moduleTestFilePath: '${{ env.moduleTestFilePath }}' + + job_psrule_test: + name: 'PsRule inflight validation' + runs-on: ubuntu-latest + needs: + - job_initialize_pipeline + strategy: + fail-fast: false + matrix: + moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + steps: + - name: Checkout + uses: actions/checkout@v3 - name: Set environment variables uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # Grouping task logs + Write-Output '::group::Replace tokens in template file' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # Populate tokens + $Tokens = @{ + resourceGroupName = '${{ env.resourceGroupName }}' + subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + tenantId = '${{ env.ARM_TENANT_ID }}' + } + + ## Add local (source control) tokens + $tokenMap = @{} + foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + } + Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + $Tokens += $tokenMap + + ## Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + } + + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = @($templateFilePath) + Tokens = $Tokens + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' + } + + Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Token Replacement Functionality [For Module] + $null = Convert-TokensInFileList @ConvertTokensInputs + + Write-Output '::endgroup::' + + # Run analysis by using the PSRule GitHub action. - name: Run PSRule analysis - uses: microsoft/ps-rule@main + uses: microsoft/ps-rule@v2.4.0 + # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulePath }}/' + inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' outputFormat: Sarif option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' - - ######################### # Static validation # ######################### @@ -101,7 +177,7 @@ jobs: modulePath: '${{ env.modulePath }}' moduleTestFilePath: '${{ env.moduleTestFilePath }}' - + # ############################# # # Deployment validation # From 09a730f1fb144b963daca64a279246868a279dd4 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 13:29:07 +0200 Subject: [PATCH 026/133] align vnet and enable deployment --- .github/workflows/ms.keyvault.vaults.yml | 126 +++++++++--------- .../workflows/ms.network.virtualnetworks.yml | 36 +---- .../workflows/ms.resources.resourcegroups.yml | 126 +++++++++--------- 3 files changed, 133 insertions(+), 155 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index 19c89f5d5a..b59a7022de 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -154,66 +154,66 @@ jobs: modules: 'PSRule.Rules.Azure' inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # ############################# - # # Deployment validation # - # ############################# - # job_module_deploy_validation: - # runs-on: ubuntu-20.04 - # name: 'Deployment validation' - # needs: - # - job_initialize_pipeline - # - job_module_pester_validation - # - job_psrule_test - # strategy: - # fail-fast: false - # matrix: - # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - # steps: - # - name: 'Checkout' - # uses: actions/checkout@v2 - # with: - # fetch-depth: 0 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - # uses: ./.github/actions/templates/validateModuleDeployment - # with: - # templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # location: '${{ env.location }}' - # resourceGroupName: '${{ env.resourceGroupName }}' - # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - - # ################## - # # Publishing # - # ################## - # job_publish_module: - # name: 'Publishing' - # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - # runs-on: ubuntu-20.04 - # needs: - # - job_module_deploy_validation - # steps: - # - name: 'Checkout' - # uses: actions/checkout@v2 - # with: - # fetch-depth: 0 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Publishing' - # uses: ./.github/actions/templates/publishModule - # with: - # templateFilePath: '${{ env.modulePath }}/deploy.bicep' - # templateSpecsRGName: '${{ env.templateSpecsRGName }}' - # templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - # templateSpecsDescription: '${{ env.templateSpecsDescription }}' - # templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - # bicepRegistryName: '${{ env.bicepRegistryName }}' - # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' + ############################# + # Deployment validation # + ############################# + job_module_deploy_validation: + runs-on: ubuntu-20.04 + name: 'Deployment validation' + needs: + - job_initialize_pipeline + - job_module_pester_validation + - job_psrule_test + strategy: + fail-fast: false + matrix: + moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' + uses: ./.github/actions/templates/validateModuleDeployment + with: + templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + location: '${{ env.location }}' + resourceGroupName: '${{ env.resourceGroupName }}' + subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + + ################## + # Publishing # + ################## + job_publish_module: + name: 'Publishing' + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + runs-on: ubuntu-20.04 + needs: + - job_module_deploy_validation + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Publishing' + uses: ./.github/actions/templates/publishModule + with: + templateFilePath: '${{ env.modulePath }}/deploy.bicep' + templateSpecsRGName: '${{ env.templateSpecsRGName }}' + templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + templateSpecsDescription: '${{ env.templateSpecsDescription }}' + templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + bicepRegistryName: '${{ env.bicepRegistryName }}' + bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index b575293cc0..36fd81746c 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -156,38 +156,16 @@ jobs: outputFormat: Sarif option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' - ######################### - # Static validation # - ######################### - job_module_pester_validation: - runs-on: ubuntu-20.04 - name: 'Static validation' - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Run tests' - uses: ./.github/actions/templates/validateModulePester - with: - modulePath: '${{ env.modulePath }}' - moduleTestFilePath: '${{ env.moduleTestFilePath }}' - - - - # ############################# - # # Deployment validation # - # ############################# + ############################# + # Deployment validation # + ############################# job_module_deploy_validation: runs-on: ubuntu-20.04 name: 'Deployment validation' needs: - job_initialize_pipeline - job_module_pester_validation + - job_psrule_test strategy: fail-fast: false matrix: @@ -211,9 +189,9 @@ jobs: managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - # ################## - # # Publishing # - # ################## + ################## + # Publishing # + ################## job_publish_module: name: 'Publishing' if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 9f7125e256..30050f14c0 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -154,66 +154,66 @@ jobs: modules: 'PSRule.Rules.Azure' inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # ############################# - # # Deployment validation # - # ############################# - # job_module_deploy_validation: - # runs-on: ubuntu-20.04 - # name: 'Deployment validation' - # needs: - # - job_initialize_pipeline - # - job_module_pester_validation - # - job_psrule_test - # strategy: - # fail-fast: false - # matrix: - # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - # steps: - # - name: 'Checkout' - # uses: actions/checkout@v2 - # with: - # fetch-depth: 0 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - # uses: ./.github/actions/templates/validateModuleDeployment - # with: - # templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # location: '${{ env.location }}' - # resourceGroupName: '${{ env.resourceGroupName }}' - # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - - # ################## - # # Publishing # - # ################## - # job_publish_module: - # name: 'Publishing' - # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - # runs-on: ubuntu-20.04 - # needs: - # - job_module_deploy_validation - # steps: - # - name: 'Checkout' - # uses: actions/checkout@v2 - # with: - # fetch-depth: 0 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Publishing' - # uses: ./.github/actions/templates/publishModule - # with: - # templateFilePath: '${{ env.modulePath }}/deploy.bicep' - # templateSpecsRGName: '${{ env.templateSpecsRGName }}' - # templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - # templateSpecsDescription: '${{ env.templateSpecsDescription }}' - # templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - # bicepRegistryName: '${{ env.bicepRegistryName }}' - # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' + ############################# + # Deployment validation # + ############################# + job_module_deploy_validation: + runs-on: ubuntu-20.04 + name: 'Deployment validation' + needs: + - job_initialize_pipeline + - job_module_pester_validation + - job_psrule_test + strategy: + fail-fast: false + matrix: + moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' + uses: ./.github/actions/templates/validateModuleDeployment + with: + templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + location: '${{ env.location }}' + resourceGroupName: '${{ env.resourceGroupName }}' + subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + + ################## + # Publishing # + ################## + job_publish_module: + name: 'Publishing' + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + runs-on: ubuntu-20.04 + needs: + - job_module_deploy_validation + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Publishing' + uses: ./.github/actions/templates/publishModule + with: + templateFilePath: '${{ env.modulePath }}/deploy.bicep' + templateSpecsRGName: '${{ env.templateSpecsRGName }}' + templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + templateSpecsDescription: '${{ env.templateSpecsDescription }}' + templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + bicepRegistryName: '${{ env.bicepRegistryName }}' + bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' From 04924d574db799fc11bc57e70b211ed02c9223a4 Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Tue, 20 Sep 2022 14:29:33 +0200 Subject: [PATCH 027/133] [Hackaton] Align 3 module workflows to use PSRule test matrix (#2065) * linter * exclude parameters * pathIgnore * clean up token replacement * clean up token replacement further * resize token replacement * typo * modulePath * job name * no psrule * no psrule step * fix inputpath * comment out continue on error option * Align KV * kv no psrule * replace * no replace * replace 1 * indent * align vnet * align vnet and enable deployment * linter test removed * psrule back * vnet no deploy * comment deployment --- .github/workflows/ms.keyvault.vaults.yml | 184 ++++++++++------ .../workflows/ms.network.virtualnetworks.yml | 192 +++++++++++------ .../workflows/ms.resources.resourcegroups.yml | 202 +++++++----------- ps-rule.yaml | 2 +- 4 files changed, 324 insertions(+), 256 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index 535903f37d..19c89f5d5a 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -82,8 +82,14 @@ jobs: moduleTestFilePath: '${{ env.moduleTestFilePath }}' job_psrule_test: - name: 'PsRule Analyze repository' + name: 'PsRule inflight validation' runs-on: ubuntu-latest + needs: + - job_initialize_pipeline + strategy: + fail-fast: false + matrix: + moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} steps: - name: Checkout uses: actions/checkout@v3 @@ -91,73 +97,123 @@ jobs: uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # Grouping task logs + Write-Output '::group::Replace tokens in template file' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # Populate tokens + $Tokens = @{ + resourceGroupName = '${{ env.resourceGroupName }}' + subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + tenantId = '${{ env.ARM_TENANT_ID }}' + } + + ## Add local (source control) tokens + $tokenMap = @{} + foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + } + Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + $Tokens += $tokenMap + + ## Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + } + + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = @($templateFilePath) + Tokens = $Tokens + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' + } + + Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Token Replacement Functionality [For Module] + $null = Convert-TokensInFileList @ConvertTokensInputs + + Write-Output '::endgroup::' # Run analysis by using the PSRule GitHub action. - name: Run PSRule analysis uses: microsoft/ps-rule@v2.4.0 + # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulePath }}/' + inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - ############################# - # Deployment validation # - ############################# - job_module_deploy_validation: - runs-on: ubuntu-20.04 - name: 'Deployment validation' - needs: - - job_initialize_pipeline - - job_module_pester_validation - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - uses: ./.github/actions/templates/validateModuleDeployment - with: - templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - location: '${{ env.location }}' - resourceGroupName: '${{ env.resourceGroupName }}' - subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - - ################## - # Publishing # - ################## - job_publish_module: - name: 'Publishing' - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - runs-on: ubuntu-20.04 - needs: - - job_module_deploy_validation - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Publishing' - uses: ./.github/actions/templates/publishModule - with: - templateFilePath: '${{ env.modulePath }}/deploy.bicep' - templateSpecsRGName: '${{ env.templateSpecsRGName }}' - templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - templateSpecsDescription: '${{ env.templateSpecsDescription }}' - templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - bicepRegistryName: '${{ env.bicepRegistryName }}' - bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' + # ############################# + # # Deployment validation # + # ############################# + # job_module_deploy_validation: + # runs-on: ubuntu-20.04 + # name: 'Deployment validation' + # needs: + # - job_initialize_pipeline + # - job_module_pester_validation + # - job_psrule_test + # strategy: + # fail-fast: false + # matrix: + # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + # steps: + # - name: 'Checkout' + # uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' + # uses: ./.github/actions/templates/validateModuleDeployment + # with: + # templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # location: '${{ env.location }}' + # resourceGroupName: '${{ env.resourceGroupName }}' + # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + + # ################## + # # Publishing # + # ################## + # job_publish_module: + # name: 'Publishing' + # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + # runs-on: ubuntu-20.04 + # needs: + # - job_module_deploy_validation + # steps: + # - name: 'Checkout' + # uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Publishing' + # uses: ./.github/actions/templates/publishModule + # with: + # templateFilePath: '${{ env.modulePath }}/deploy.bicep' + # templateSpecsRGName: '${{ env.templateSpecsRGName }}' + # templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + # templateSpecsDescription: '${{ env.templateSpecsDescription }}' + # templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + # bicepRegistryName: '${{ env.bicepRegistryName }}' + # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index f09c176e38..7a7feeeeb8 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -60,26 +60,6 @@ jobs: removeDeployment: ${{ steps.get-workflow-param.outputs.removeDeployment }} moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} - job_psrule_validation: - runs-on: ubuntu-20.04 - name: 'psrule-validation' - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: Run PSRule analysis - uses: microsoft/ps-rule@main - with: - modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulePath }}/' - outputFormat: Sarif - option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' - - - ######################### # Static validation # ######################### @@ -101,67 +81,141 @@ jobs: modulePath: '${{ env.modulePath }}' moduleTestFilePath: '${{ env.moduleTestFilePath }}' - - - # ############################# - # # Deployment validation # - # ############################# - job_module_deploy_validation: - runs-on: ubuntu-20.04 - name: 'Deployment validation' + job_psrule_test: + name: 'PsRule inflight validation' + runs-on: ubuntu-latest needs: - - job_initialize_pipeline - - job_module_pester_validation + - job_initialize_pipeline strategy: fail-fast: false matrix: moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 + - name: Checkout + uses: actions/checkout@v3 - name: Set environment variables uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} - - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - uses: ./.github/actions/templates/validateModuleDeployment + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 with: - templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - location: '${{ env.location }}' - resourceGroupName: '${{ env.resourceGroupName }}' - subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + azPSVersion: 'latest' + inlineScript: | + $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # Grouping task logs + Write-Output '::group::Replace tokens in template file' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # Populate tokens + $Tokens = @{ + resourceGroupName = '${{ env.resourceGroupName }}' + subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + tenantId = '${{ env.ARM_TENANT_ID }}' + } + + ## Add local (source control) tokens + $tokenMap = @{} + foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + } + Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + $Tokens += $tokenMap + + ## Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + } + + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = @($templateFilePath) + Tokens = $Tokens + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' + } + + Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Token Replacement Functionality [For Module] + $null = Convert-TokensInFileList @ConvertTokensInputs + + Write-Output '::endgroup::' + + # Run analysis by using the PSRule GitHub action. + - name: Run PSRule analysis + uses: microsoft/ps-rule@v2.4.0 + # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project + with: + modules: 'PSRule.Rules.Azure' + inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + outputFormat: Sarif + option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' + + # ############################# + # # Deployment validation # + # ############################# + # job_module_deploy_validation: + # runs-on: ubuntu-20.04 + # name: 'Deployment validation' + # needs: + # - job_initialize_pipeline + # - job_module_pester_validation + # - job_psrule_test + # strategy: + # fail-fast: false + # matrix: + # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + # steps: + # - name: 'Checkout' + # uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' + # uses: ./.github/actions/templates/validateModuleDeployment + # with: + # templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # location: '${{ env.location }}' + # resourceGroupName: '${{ env.resourceGroupName }}' + # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' # ################## # # Publishing # # ################## - job_publish_module: - name: 'Publishing' - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - runs-on: ubuntu-20.04 - needs: - - job_module_deploy_validation - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Publishing' - uses: ./.github/actions/templates/publishModule - with: - templateFilePath: '${{ env.modulePath }}/deploy.bicep' - templateSpecsRGName: '${{ env.templateSpecsRGName }}' - templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - templateSpecsDescription: '${{ env.templateSpecsDescription }}' - templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - bicepRegistryName: '${{ env.bicepRegistryName }}' - bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' + # job_publish_module: + # name: 'Publishing' + # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + # runs-on: ubuntu-20.04 + # needs: + # - job_module_deploy_validation + # steps: + # - name: 'Checkout' + # uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Publishing' + # uses: ./.github/actions/templates/publishModule + # with: + # templateFilePath: '${{ env.modulePath }}/deploy.bicep' + # templateSpecsRGName: '${{ env.templateSpecsRGName }}' + # templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + # templateSpecsDescription: '${{ env.templateSpecsDescription }}' + # templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + # bicepRegistryName: '${{ env.bicepRegistryName }}' + # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index dd79505e8e..9f7125e256 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -82,7 +82,7 @@ jobs: moduleTestFilePath: '${{ env.moduleTestFilePath }}' job_psrule_test: - name: 'PsRule Analyze repository' + name: 'PsRule inflight validation' runs-on: ubuntu-latest needs: - job_initialize_pipeline @@ -91,84 +91,52 @@ jobs: matrix: moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} steps: - - name: Checkout uses: actions/checkout@v3 - name: Set environment variables uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} - - name: Azure Login - uses: Azure/login@v1 - with: - creds: ${{ env.AZURE_CREDENTIALS }} - enable-AzPSSession: true - - # [Token replacement] task(s) - # --------------------------- - name: 'Replace tokens in template file' uses: azure/powershell@v1 with: azPSVersion: 'latest' inlineScript: | $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - $parameterFilePath = "" - $customParameterFileTokens = "" # Grouping task logs Write-Output '::group::Replace tokens in template file' # Load used functions . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - # Get target files - $targetFileList = @($templateFilePath) - if(-not [String]::IsNullOrEmpty($parameterFilePath)) { - $targetFileList += $parameterFilePath - } - - # Get Service Principal Object ID - $context = Get-AzContext - Write-Output 'Checking context' - $context - $servicePrincipalAppId = $context.Account.Id - $servicePrincipal = Get-AzADServicePrincipal -ApplicationId $servicePrincipalAppId - $servicePrincipalObjectId = $servicePrincipal.Id - - # Construct Token Function Input - $ConvertTokensInputs = @{ - FilePathList = $targetFileList - Tokens = @{} - TokenPrefix = '${{ env.tokenPrefix }}' - TokenSuffix = '${{ env.tokenSuffix }}' - } - - # Add enforced tokens - $ConvertTokensInputs.Tokens += @{ + # Populate tokens + $Tokens = @{ resourceGroupName = '${{ env.resourceGroupName }}' subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' tenantId = '${{ env.ARM_TENANT_ID }}' } - # Add local (source control) tokens + ## Add local (source control) tokens $tokenMap = @{} foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } } Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - $ConvertTokensInputs.Tokens += $tokenMap + $Tokens += $tokenMap - # Swap 'namePrefix' token if empty and provided as a GitHub secret - if([String]::IsNullOrEmpty($ConvertTokensInputs.Tokens['namePrefix'])){ + ## Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - $ConvertTokensInputs.Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' } - # Add custom tokens (passed in via the pipeline) - if(-not [String]::IsNullOrEmpty($customParameterFileTokens)) { - $customTokens = $customParameterFileTokens| ConvertFrom-Json -AsHashTable - Write-Verbose ('Using custom parameter file tokens [{0}]' -f ($customTokens.Keys -join ', ')) -Verbose - $ConvertTokensInputs.Tokens += $customTokens + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = @($templateFilePath) + Tokens = $Tokens + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' } Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose @@ -176,86 +144,76 @@ jobs: # Invoke Token Replacement Functionality [For Module] $null = Convert-TokensInFileList @ConvertTokensInputs - # Get target files for modules dependencies - $DependencyParameterFilePaths = [System.Collections.ArrayList]@() - $DependencyParameterFolders = Get-ChildItem -Path (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'dependencies') -Recurse -Filter 'parameters' -Directory - foreach ($FolderPath in $DependencyParameterFolders.FullName) { - $DependencyParameterFilePaths += Get-ChildItem -Path $FolderPath -Recurse -Filter '*.json' - } - $ConvertTokensInputs.FilePathList = $DependencyParameterFilePaths - - # Invoke Token Replacement Functionality [For Dependencies] - $null = Convert-TokensInFileList @ConvertTokensInputs - Write-Output '::endgroup::' # Run analysis by using the PSRule GitHub action. - name: Run PSRule analysis uses: microsoft/ps-rule@v2.4.0 + # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: 'modules/Microsoft.Resources/resourceGroups/${{ matrix.moduleTestFilePaths }}' - - - ############################ - # Deployment validation # - ############################ - job_module_deploy_validation: - runs-on: ubuntu-20.04 - name: 'Deployment validation' - needs: - - job_initialize_pipeline - - job_module_pester_validation - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - uses: ./.github/actions/templates/validateModuleDeployment - with: - templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - location: '${{ env.location }}' - resourceGroupName: '${{ env.resourceGroupName }}' - subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - - ################## - # Publishing # - ################## - job_publish_module: - name: 'Publishing' - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - runs-on: ubuntu-20.04 - needs: - - job_module_deploy_validation - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Publishing' - uses: ./.github/actions/templates/publishModule - with: - templateFilePath: '${{ env.modulePath }}/deploy.bicep' - templateSpecsRGName: '${{ env.templateSpecsRGName }}' - templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - templateSpecsDescription: '${{ env.templateSpecsDescription }}' - templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - bicepRegistryName: '${{ env.bicepRegistryName }}' - bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' + inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + + # ############################# + # # Deployment validation # + # ############################# + # job_module_deploy_validation: + # runs-on: ubuntu-20.04 + # name: 'Deployment validation' + # needs: + # - job_initialize_pipeline + # - job_module_pester_validation + # - job_psrule_test + # strategy: + # fail-fast: false + # matrix: + # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + # steps: + # - name: 'Checkout' + # uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' + # uses: ./.github/actions/templates/validateModuleDeployment + # with: + # templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # location: '${{ env.location }}' + # resourceGroupName: '${{ env.resourceGroupName }}' + # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + + # ################## + # # Publishing # + # ################## + # job_publish_module: + # name: 'Publishing' + # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + # runs-on: ubuntu-20.04 + # needs: + # - job_module_deploy_validation + # steps: + # - name: 'Checkout' + # uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Publishing' + # uses: ./.github/actions/templates/publishModule + # with: + # templateFilePath: '${{ env.modulePath }}/deploy.bicep' + # templateSpecsRGName: '${{ env.templateSpecsRGName }}' + # templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + # templateSpecsDescription: '${{ env.templateSpecsDescription }}' + # templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + # bicepRegistryName: '${{ env.bicepRegistryName }}' + # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' diff --git a/ps-rule.yaml b/ps-rule.yaml index 4b24175416..a0dbe090f4 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -55,7 +55,7 @@ rule: exclude: # Ignore the following rules for all resources - Azure.KeyVault.PurgeProtect - + # Suppression ignores rules for a specific Azure resource by name. suppression: Azure.Resource.UseTags: From f7e7ae616b3f7bd582d56713a28ed19db5d2da85 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 14:50:32 +0200 Subject: [PATCH 028/133] conflicts --- .github/workflows/ms.keyvault.vaults.yml | 35 ------ .../workflows/ms.network.virtualnetworks.yml | 118 ------------------ .../workflows/ms.resources.resourcegroups.yml | 66 ---------- 3 files changed, 219 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index 3d8d0730cd..19c89f5d5a 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -154,40 +154,6 @@ jobs: modules: 'PSRule.Rules.Azure' inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' -<<<<<<< HEAD - ############################# - # Deployment validation # - ############################# - job_module_deploy_validation: - runs-on: ubuntu-20.04 - name: 'Deployment validation' - needs: - - job_initialize_pipeline - - job_module_pester_validation - - job_psrule_test - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - uses: ./.github/actions/templates/validateModuleDeployment - with: - templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - location: '${{ env.location }}' - resourceGroupName: '${{ env.resourceGroupName }}' - subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' -======= # ############################# # # Deployment validation # # ############################# @@ -220,7 +186,6 @@ jobs: # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' ->>>>>>> hack/topic6 # ################## # # Publishing # diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index a307d3d940..7a7feeeeb8 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -84,7 +84,6 @@ jobs: job_psrule_test: name: 'PsRule inflight validation' runs-on: ubuntu-latest -<<<<<<< HEAD needs: - job_initialize_pipeline strategy: @@ -157,90 +156,6 @@ jobs: outputFormat: Sarif option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' - ############################# - # Deployment validation # - ############################# - job_module_deploy_validation: - runs-on: ubuntu-20.04 - name: 'Deployment validation' - needs: - - job_initialize_pipeline - - job_module_pester_validation - - job_psrule_test -======= - needs: - - job_initialize_pipeline ->>>>>>> hack/topic6 - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Replace tokens in template file' - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # Grouping task logs - Write-Output '::group::Replace tokens in template file' - - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # Populate tokens - $Tokens = @{ - resourceGroupName = '${{ env.resourceGroupName }}' - subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - tenantId = '${{ env.ARM_TENANT_ID }}' - } - - ## Add local (source control) tokens - $tokenMap = @{} - foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - } - Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - $Tokens += $tokenMap - - ## Swap 'namePrefix' token if empty and provided as a GitHub secret - if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - } - - # Construct Token Function Input - $ConvertTokensInputs = @{ - FilePathList = @($templateFilePath) - Tokens = $Tokens - TokenPrefix = '${{ env.tokenPrefix }}' - TokenSuffix = '${{ env.tokenSuffix }}' - } - - Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # Invoke Token Replacement Functionality [For Module] - $null = Convert-TokensInFileList @ConvertTokensInputs - - Write-Output '::endgroup::' - - # Run analysis by using the PSRule GitHub action. - - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.4.0 - # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project - with: - modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - outputFormat: Sarif - option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' - # ############################# # # Deployment validation # # ############################# @@ -274,38 +189,6 @@ jobs: # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' -<<<<<<< HEAD - ################## - # Publishing # - ################## - job_publish_module: - name: 'Publishing' - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - runs-on: ubuntu-20.04 - needs: - - job_module_deploy_validation - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Publishing' - uses: ./.github/actions/templates/publishModule - with: - templateFilePath: '${{ env.modulePath }}/deploy.bicep' - templateSpecsRGName: '${{ env.templateSpecsRGName }}' - templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - templateSpecsDescription: '${{ env.templateSpecsDescription }}' - templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - bicepRegistryName: '${{ env.bicepRegistryName }}' - bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' -======= # ################## # # Publishing # # ################## @@ -336,4 +219,3 @@ jobs: # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' ->>>>>>> hack/topic6 diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 9f3763b140..9f7125e256 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -154,71 +154,6 @@ jobs: modules: 'PSRule.Rules.Azure' inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' -<<<<<<< HEAD - ############################# - # Deployment validation # - ############################# - job_module_deploy_validation: - runs-on: ubuntu-20.04 - name: 'Deployment validation' - needs: - - job_initialize_pipeline - - job_module_pester_validation - - job_psrule_test - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - uses: ./.github/actions/templates/validateModuleDeployment - with: - templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - location: '${{ env.location }}' - resourceGroupName: '${{ env.resourceGroupName }}' - subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - - ################## - # Publishing # - ################## - job_publish_module: - name: 'Publishing' - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - runs-on: ubuntu-20.04 - needs: - - job_module_deploy_validation - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Publishing' - uses: ./.github/actions/templates/publishModule - with: - templateFilePath: '${{ env.modulePath }}/deploy.bicep' - templateSpecsRGName: '${{ env.templateSpecsRGName }}' - templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - templateSpecsDescription: '${{ env.templateSpecsDescription }}' - templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - bicepRegistryName: '${{ env.bicepRegistryName }}' - bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' -======= # ############################# # # Deployment validation # # ############################# @@ -282,4 +217,3 @@ jobs: # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' ->>>>>>> hack/topic6 From 3a260c916360f149e5816d71dfba1d4fa1a0a438 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 16:16:57 +0200 Subject: [PATCH 029/133] suppressedRuleWarning --- ps-rule.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/ps-rule.yaml b/ps-rule.yaml index eb0ef803db..a85e0f7db3 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -20,6 +20,7 @@ requires: execution: notProcessedWarning: false + suppressedRuleWarning: false # Use PSRule for Azure. include: From 5c737f106c11f948b64132c8f16ac5a071262af4 Mon Sep 17 00:00:00 2001 From: Karthik Venkatraman <44262238+karthikvenkat17@users.noreply.github.com> Date: Tue, 20 Sep 2022 16:27:39 +0100 Subject: [PATCH 030/133] [Hackathon] PSRule output in markdown (#2072) * output formatting * psrule output summary * output summary psrule * xml output * markdown output * publish output always * adding output to github summary * filter psrule outcome * psrul outcome env * outcome filter psrule * outcome filter psrule * output summary filter psrule * output to file psrule * adding output github summary Co-authored-by: Karthik Venkatraman --- .github/workflows/ms.network.virtualnetworks.yml | 14 +++++++++++++- .../virtualNetworks/.test/vnet-ps-rule.yaml | 3 +++ ps-rule.yaml | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index 7a7feeeeb8..ffc885a1bc 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -34,6 +34,7 @@ env: ARM_TENANT_ID: '${{ secrets.ARM_TENANT_ID }}' TOKEN_NAMEPREFIX: '${{ secrets.TOKEN_NAMEPREFIX }}' + jobs: ########################### # Initialize pipeline # @@ -153,8 +154,19 @@ jobs: with: modules: 'PSRule.Rules.Azure' inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - outputFormat: Sarif + outputFormat: Markdown + outputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.md' option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' + + # - name: 'PSRule test summary' + # uses: EnricoMi/publish-unit-test-result-action@v1 + # if: always() + # with: + # files: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.xml' + + - name: Output to Github summaries + if: always() + run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.md' >> $GITHUB_STEP_SUMMARY # ############################# # # Deployment validation # diff --git a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml index 589d2ab3b7..7012418c3c 100644 --- a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml +++ b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml @@ -26,6 +26,9 @@ include: output: culture: - 'en-US' + outcome: 'Fail' + as: 'Summary' + input: pathIgnore: diff --git a/ps-rule.yaml b/ps-rule.yaml index a0dbe090f4..151c9a2173 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -27,6 +27,7 @@ output: culture: - 'en-US' + input: pathIgnore: # Ignore other files in the repository. From 00e25159b56f1a68bdd9bc2ba98035a9620ff2a7 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 20 Sep 2022 21:06:15 +0200 Subject: [PATCH 031/133] enable deployment --- .../workflows/ms.resources.resourcegroups.yml | 126 +++++++++--------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 9f7125e256..30050f14c0 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -154,66 +154,66 @@ jobs: modules: 'PSRule.Rules.Azure' inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # ############################# - # # Deployment validation # - # ############################# - # job_module_deploy_validation: - # runs-on: ubuntu-20.04 - # name: 'Deployment validation' - # needs: - # - job_initialize_pipeline - # - job_module_pester_validation - # - job_psrule_test - # strategy: - # fail-fast: false - # matrix: - # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - # steps: - # - name: 'Checkout' - # uses: actions/checkout@v2 - # with: - # fetch-depth: 0 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - # uses: ./.github/actions/templates/validateModuleDeployment - # with: - # templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # location: '${{ env.location }}' - # resourceGroupName: '${{ env.resourceGroupName }}' - # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - - # ################## - # # Publishing # - # ################## - # job_publish_module: - # name: 'Publishing' - # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - # runs-on: ubuntu-20.04 - # needs: - # - job_module_deploy_validation - # steps: - # - name: 'Checkout' - # uses: actions/checkout@v2 - # with: - # fetch-depth: 0 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Publishing' - # uses: ./.github/actions/templates/publishModule - # with: - # templateFilePath: '${{ env.modulePath }}/deploy.bicep' - # templateSpecsRGName: '${{ env.templateSpecsRGName }}' - # templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - # templateSpecsDescription: '${{ env.templateSpecsDescription }}' - # templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - # bicepRegistryName: '${{ env.bicepRegistryName }}' - # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' + ############################# + # Deployment validation # + ############################# + job_module_deploy_validation: + runs-on: ubuntu-20.04 + name: 'Deployment validation' + needs: + - job_initialize_pipeline + - job_module_pester_validation + - job_psrule_test + strategy: + fail-fast: false + matrix: + moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' + uses: ./.github/actions/templates/validateModuleDeployment + with: + templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + location: '${{ env.location }}' + resourceGroupName: '${{ env.resourceGroupName }}' + subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + + ################## + # Publishing # + ################## + job_publish_module: + name: 'Publishing' + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + runs-on: ubuntu-20.04 + needs: + - job_module_deploy_validation + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Publishing' + uses: ./.github/actions/templates/publishModule + with: + templateFilePath: '${{ env.modulePath }}/deploy.bicep' + templateSpecsRGName: '${{ env.templateSpecsRGName }}' + templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + templateSpecsDescription: '${{ env.templateSpecsDescription }}' + templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + bicepRegistryName: '${{ env.bicepRegistryName }}' + bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' From b9c0b5a445775652df92bace12e05c4f09cf0c9b Mon Sep 17 00:00:00 2001 From: elisa anzelmo Date: Wed, 21 Sep 2022 11:21:10 +0200 Subject: [PATCH 032/133] [Hackaton] Psrule output on csv format with detailed results (#2089) * select xml output with summary * outout format as json * json without summary * no outcome, summary on output md * no outcome filter, summaery on, markdown * no summary, no outcome, markdown * output csv, detail * output csv, summary, no outcome * wide format, no summarized, no outcome * no summary, no outcome, output yaml * csv + details - no outcome and no summary --- .github/workflows/ms.keyvault.vaults.yml | 2 +- .github/workflows/ms.network.virtualnetworks.yml | 14 +++++++++----- .github/workflows/ms.resources.resourcegroups.yml | 2 +- .../virtualNetworks/.test/vnet-ps-rule.yaml | 6 +++--- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index 19c89f5d5a..ece5a08f75 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -82,7 +82,7 @@ jobs: moduleTestFilePath: '${{ env.moduleTestFilePath }}' job_psrule_test: - name: 'PsRule inflight validation' + name: 'PsRule pre-flight validation' runs-on: ubuntu-latest needs: - job_initialize_pipeline diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index ffc885a1bc..905e8915e0 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -83,7 +83,7 @@ jobs: moduleTestFilePath: '${{ env.moduleTestFilePath }}' job_psrule_test: - name: 'PsRule inflight validation' + name: 'PsRule pre-flight validation' runs-on: ubuntu-latest needs: - job_initialize_pipeline @@ -154,10 +154,14 @@ jobs: with: modules: 'PSRule.Rules.Azure' inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - outputFormat: Markdown - outputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.md' + outputFormat: Csv + outputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' - + + - name: Output to Github Logs + if: always() + run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' + # - name: 'PSRule test summary' # uses: EnricoMi/publish-unit-test-result-action@v1 # if: always() @@ -166,7 +170,7 @@ jobs: - name: Output to Github summaries if: always() - run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.md' >> $GITHUB_STEP_SUMMARY + run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' >> $GITHUB_STEP_SUMMARY # ############################# # # Deployment validation # diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 9f7125e256..c1bb79b548 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -82,7 +82,7 @@ jobs: moduleTestFilePath: '${{ env.moduleTestFilePath }}' job_psrule_test: - name: 'PsRule inflight validation' + name: 'PsRule pre-flight validation' runs-on: ubuntu-latest needs: - job_initialize_pipeline diff --git a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml index 7012418c3c..e36dc447ac 100644 --- a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml +++ b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml @@ -26,9 +26,9 @@ include: output: culture: - 'en-US' - outcome: 'Fail' - as: 'Summary' - + #outcome: 'Fail' + #as: 'Summary' + input: pathIgnore: From f16b43288859f85a3f5d699020f9a8250438967d Mon Sep 17 00:00:00 2001 From: elisa anzelmo Date: Wed, 21 Sep 2022 14:37:49 +0200 Subject: [PATCH 033/133] [Hackaton] csv output on Keyvault module (#2091) * kv summart csv no outcome * print summary * outcome all, format csv as summary * csv detailed all * csv all detail execution supresswaring * rull.pass log information * empty options with summary on * csv test --- .github/workflows/ms.keyvault.vaults.yml | 116 ++++++++++++----------- ps-rule.yaml | 11 ++- 2 files changed, 71 insertions(+), 56 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index ece5a08f75..d6ec346e70 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -86,10 +86,10 @@ jobs: runs-on: ubuntu-latest needs: - job_initialize_pipeline - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + # strategy: + # fail-fast: false + # matrix: + # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} steps: - name: Checkout uses: actions/checkout@v3 @@ -97,54 +97,54 @@ jobs: uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} - - name: 'Replace tokens in template file' - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # Grouping task logs - Write-Output '::group::Replace tokens in template file' - - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # Populate tokens - $Tokens = @{ - resourceGroupName = '${{ env.resourceGroupName }}' - subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - tenantId = '${{ env.ARM_TENANT_ID }}' - } - - ## Add local (source control) tokens - $tokenMap = @{} - foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - } - Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - $Tokens += $tokenMap - - ## Swap 'namePrefix' token if empty and provided as a GitHub secret - if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - } - - # Construct Token Function Input - $ConvertTokensInputs = @{ - FilePathList = @($templateFilePath) - Tokens = $Tokens - TokenPrefix = '${{ env.tokenPrefix }}' - TokenSuffix = '${{ env.tokenSuffix }}' - } - - Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # Invoke Token Replacement Functionality [For Module] - $null = Convert-TokensInFileList @ConvertTokensInputs - - Write-Output '::endgroup::' + # - name: 'Replace tokens in template file' + # uses: azure/powershell@v1 + # with: + # azPSVersion: 'latest' + # inlineScript: | + # $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # # Grouping task logs + # Write-Output '::group::Replace tokens in template file' + + # # Load used functions + # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # # Populate tokens + # $Tokens = @{ + # resourceGroupName = '${{ env.resourceGroupName }}' + # subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + # managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + # tenantId = '${{ env.ARM_TENANT_ID }}' + # } + + # ## Add local (source control) tokens + # $tokenMap = @{} + # foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + # $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + # } + # Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + # $Tokens += $tokenMap + + # ## Swap 'namePrefix' token if empty and provided as a GitHub secret + # if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + # Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + # $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + # } + + # # Construct Token Function Input + # $ConvertTokensInputs = @{ + # FilePathList = @($templateFilePath) + # Tokens = $Tokens + # TokenPrefix = '${{ env.tokenPrefix }}' + # TokenSuffix = '${{ env.tokenSuffix }}' + # } + + # Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # # Invoke Token Replacement Functionality [For Module] + # $null = Convert-TokensInFileList @ConvertTokensInputs + + # Write-Output '::endgroup::' # Run analysis by using the PSRule GitHub action. - name: Run PSRule analysis @@ -152,7 +152,17 @@ jobs: # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + inputPath: '${{ env.modulePath }}/' + outputFormat: Csv + outputPath: '${{ env.modulePath }}-output.csv' + + - name: Output to Github Logs + if: always() + run: cat '${{ env.modulePath }}-output.csv' + + - name: Output to Github summaries + if: always() + run: cat '${{ env.modulePath }}-output.csv' >> $GITHUB_STEP_SUMMARY # ############################# # # Deployment validation # diff --git a/ps-rule.yaml b/ps-rule.yaml index 151c9a2173..0a1e9f91b7 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -23,9 +23,14 @@ include: module: - PSRule.Rules.Azure +execution: + suppressedRuleWarning: false + output: culture: - 'en-US' + outcome: 'All' + #as: 'Summary' input: @@ -58,6 +63,6 @@ rule: - Azure.KeyVault.PurgeProtect # Suppression ignores rules for a specific Azure resource by name. -suppression: - Azure.Resource.UseTags: - - <>kvvmin001 +# suppression: +# Azure.Resource.UseTags: +# - <>kvvmin001 From 16971bd48e9ce6ed364edf31676982b3783c9d7a Mon Sep 17 00:00:00 2001 From: Elena Batanero <46710322+elbatane@users.noreply.github.com> Date: Wed, 21 Sep 2022 17:44:38 +0200 Subject: [PATCH 034/133] [Hackathon] Set PSRule output script (#2093) * suppress dependancy * comment deployment validation job * add supress yaml * supress yaml poc * rule filter * suppress rule filter * add baseline for suppression * baseline for suppression * TEsting supressiongroup * testing supressSelector * testing supression group * Create test.md * Update test.md * Added csv to md powershell script and results * Updated md * Delete test.md * Updates to PSRule output script * PS output script changes * Putting the virtualnetworks workflow back from hack/topic6 * removed output md and csv * removing baseline Co-authored-by: Karthik Venkatraman Co-authored-by: Elena Batanero Garcia --- .../workflows/ms.network.virtualnetworks.yml | 7 +- .ps-rule/dep-suppress.Rule.yaml | 7 +- .../virtualNetworks/.test/vnet-ps-rule.yaml | 2 - .../PSRuleValidation/Set-PSRuleOutput.ps1 | 118 ++++++++++++++++++ 4 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index 905e8915e0..b29dcaffcf 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -34,7 +34,6 @@ env: ARM_TENANT_ID: '${{ secrets.ARM_TENANT_ID }}' TOKEN_NAMEPREFIX: '${{ secrets.TOKEN_NAMEPREFIX }}' - jobs: ########################### # Initialize pipeline # @@ -86,7 +85,7 @@ jobs: name: 'PsRule pre-flight validation' runs-on: ubuntu-latest needs: - - job_initialize_pipeline + - job_initialize_pipeline strategy: fail-fast: false matrix: @@ -160,7 +159,7 @@ jobs: - name: Output to Github Logs if: always() - run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' + run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' # - name: 'PSRule test summary' # uses: EnricoMi/publish-unit-test-result-action@v1 @@ -170,7 +169,7 @@ jobs: - name: Output to Github summaries if: always() - run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' >> $GITHUB_STEP_SUMMARY + run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' >> $GITHUB_STEP_SUMMARY # ############################# # # Deployment validation # diff --git a/.ps-rule/dep-suppress.Rule.yaml b/.ps-rule/dep-suppress.Rule.yaml index 04681c9def..0eedcfb968 100644 --- a/.ps-rule/dep-suppress.Rule.yaml +++ b/.ps-rule/dep-suppress.Rule.yaml @@ -7,8 +7,5 @@ metadata: spec: if: name: '.' - startsWith: - - 'dep' - - - \ No newline at end of file + startsWith: + - 'dep' diff --git a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml index e36dc447ac..c88663641a 100644 --- a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml +++ b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml @@ -29,7 +29,6 @@ output: #outcome: 'Fail' #as: 'Summary' - input: pathIgnore: # Ignore other files in the repository. @@ -58,7 +57,6 @@ rule: exclude: # Ignore the following rules for all resources - Azure.Resource.UseTags - # Suppression ignores rules for a specific Azure resource by name. #suppression: # Azure.Identity.UserAssignedName: diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 new file mode 100644 index 0000000000..36af10e843 --- /dev/null +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -0,0 +1,118 @@ +function Set-PSRuleOutput { + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory)] + [String] $inputFilePath, + + [Parameter(Mandatory = $false)] + [string] $outputFilePath = './output.md' + ) + + # Import CSV output and filter by results + + $results = Import-Csv -Path $inputFilePath + + $passedRules = @() + $failedRules = @() + + $passedRules += $results | Where-Object { $_.Outcome -EQ 'Pass' } + $failedRules += $results | Where-Object { $_.Outcome -EQ 'Fail' } + + + #Create Summary table + + $headerTable = [System.Collections.ArrayList]@( + '# Output Summary ', + '', + '| Total No. of Rules Processed | Rules Passed :white_check_mark: | Rules Failed :x: |', + '| :-- | :-- | :-- |' + ) + + $headerTable += ('| {0} | {1} | {2} |' -f $results.Count, $passedRules.Count , $failedRules.Count) + $headerTable += [System.Collections.ArrayList]@( + '') + + # Create markdown file with header table + Out-File -FilePath $outputFilePath -NoClobber -InputObject $headerTable + + + if ($failedRules.Count -gt 0) { + + #Create Failing table + + $failContent = [System.Collections.ArrayList]@( + '# Rules Failed', + '', + '| RuleName | TargetName | Synopsis |', + '| :-- | :-- | :-- |' + ) + + foreach ($content in $failedRules ) { + # Shorten the target name for deployment resoure type + if ($content.TargetType -eq 'Microsoft.Resources/deployments') { + $content.TargetName = $content.TargetName.replace('/home/runner/work/ResourceModules/ResourceModules/modules/', '') + } + + # Build hyperlinks to PSrule documentation for the rules + $TemplatesBaseUrl = 'https://azure.github.io/PSRule.Rules.Azure/en/rules' + try { + $PSRuleReferenceUrl = '{0}/{1}' -f $TemplatesBaseUrl, $content.RuleName + $null = Invoke-WebRequest -Uri $PSRuleReferenceUrl + $resourceLink = "[" + $content.RuleName + "](" + $PSRuleReferenceUrl + ")" + } + catch { + Write-Warning "Unable to build url for $content.RuleName" + $resourceLink = $content.RuleName + } + $failContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) + + } + #Append markdown with failed rules table + Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $failContent + } + + # Create Passing table + if ($passedRules.Count -gt 0) { + + $passContent = [System.Collections.ArrayList]@( + '# Rules Passed', + '', + '| RuleName | TargetName | Synopsis |', + '| :-- | :-- | :-- |' + ) + + foreach ($content in $passedRules ) { + # Shorten the target name for deployment resoure type + if ($content.TargetType -eq 'Microsoft.Resources/deployments') { + $content.TargetName = $content.TargetName.replace('/home/runner/work/ResourceModules/ResourceModules/modules/', '') + } + + # Build hyperlinks to PSrule documentation for the rules + $TemplatesBaseUrl = 'https://azure.github.io/PSRule.Rules.Azure/en/rules' + try { + $PSRuleReferenceUrl = '{0}/{1}' -f $TemplatesBaseUrl, $content.RuleName + $null = Invoke-WebRequest -Uri $PSRuleReferenceUrl + $resourceLink = "[" + $content.RuleName + "](" + $PSRuleReferenceUrl + ")" + } + catch { + Write-Warning "Unable to build url for $content.RuleName" + $resourceLink = $content.RuleName + } + + $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) + + } + + $passContent += [System.Collections.ArrayList]@( + '') + + #Append markdown with passed rules table + Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $passContent + + } + + +} + + + From 0dda740751a1f6b09b8deb172116ce553be8dc64 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 19:43:17 +0200 Subject: [PATCH 035/133] linter csv --- .github/workflows/linter.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index af409cf677..e5a433cb4e 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -33,5 +33,16 @@ jobs: uses: microsoft/ps-rule@v2.4.0 continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: - modules: PSRule.Rules.Azure - baseline: Azure.Default + modules: 'PSRule.Rules.Azure' + inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + outputFormat: Csv + outputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' + option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' + + - name: Output to Github Logs + if: always() + run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' + + - name: Output to Github summaries + if: always() + run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' >> $GITHUB_STEP_SUMMARY From 5917a86250762bc1ddf9b91dfb1b0d964660e160 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 19:46:54 +0200 Subject: [PATCH 036/133] path to ignore --- ps-rule.yaml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/ps-rule.yaml b/ps-rule.yaml index e6f83a8325..afb16fb012 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -40,21 +40,9 @@ output: input: pathIgnore: # Ignore other files in the repository. - # - '.azuredevops/' - # - '.github/' - # - '.vscode/' - # - 'constructs/' - # - 'docs/' - # - 'utilities' - '**/*' - # - '*.md' - # # - '!**/.tests/*.bicep' - - - # Exclude modules but not tests. - # - 'modules/**/*.bicep' + # Do not ignore tests. - '!modules/**/*.test.bicep' - # - 'modules/**/*version.json' configuration: # Enable automatic expansion of Azure parameter files. From 729a9c69b9cda461c715d0fad7ed0aad89a93d92 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 19:47:46 +0200 Subject: [PATCH 037/133] execution --- ps-rule.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ps-rule.yaml b/ps-rule.yaml index afb16fb012..e1f4a76ca0 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -18,10 +18,6 @@ requires: PSRule: '@pre >=2.4.0' PSRule.Rules.Azure: '@pre >=1.19.2' -execution: - notProcessedWarning: false - suppressedRuleWarning: false - # Use PSRule for Azure. include: module: @@ -29,6 +25,7 @@ include: execution: suppressedRuleWarning: false + notProcessedWarning: false output: culture: From c19be048fcb168217afa1bc53c4d0d52535be889 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 19:49:21 +0200 Subject: [PATCH 038/133] disable rg deployment --- .../workflows/ms.resources.resourcegroups.yml | 126 +++++++++--------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 849e5ecb3b..c1bb79b548 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -154,66 +154,66 @@ jobs: modules: 'PSRule.Rules.Azure' inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - ############################# - # Deployment validation # - ############################# - job_module_deploy_validation: - runs-on: ubuntu-20.04 - name: 'Deployment validation' - needs: - - job_initialize_pipeline - - job_module_pester_validation - - job_psrule_test - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - uses: ./.github/actions/templates/validateModuleDeployment - with: - templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - location: '${{ env.location }}' - resourceGroupName: '${{ env.resourceGroupName }}' - subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - - ################## - # Publishing # - ################## - job_publish_module: - name: 'Publishing' - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - runs-on: ubuntu-20.04 - needs: - - job_module_deploy_validation - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Publishing' - uses: ./.github/actions/templates/publishModule - with: - templateFilePath: '${{ env.modulePath }}/deploy.bicep' - templateSpecsRGName: '${{ env.templateSpecsRGName }}' - templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - templateSpecsDescription: '${{ env.templateSpecsDescription }}' - templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - bicepRegistryName: '${{ env.bicepRegistryName }}' - bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' + # ############################# + # # Deployment validation # + # ############################# + # job_module_deploy_validation: + # runs-on: ubuntu-20.04 + # name: 'Deployment validation' + # needs: + # - job_initialize_pipeline + # - job_module_pester_validation + # - job_psrule_test + # strategy: + # fail-fast: false + # matrix: + # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + # steps: + # - name: 'Checkout' + # uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' + # uses: ./.github/actions/templates/validateModuleDeployment + # with: + # templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # location: '${{ env.location }}' + # resourceGroupName: '${{ env.resourceGroupName }}' + # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + + # ################## + # # Publishing # + # ################## + # job_publish_module: + # name: 'Publishing' + # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + # runs-on: ubuntu-20.04 + # needs: + # - job_module_deploy_validation + # steps: + # - name: 'Checkout' + # uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + # - name: Set environment variables + # uses: ./.github/actions/templates/setEnvironmentVariables + # with: + # variablesPath: ${{ env.variablesPath }} + # - name: 'Publishing' + # uses: ./.github/actions/templates/publishModule + # with: + # templateFilePath: '${{ env.modulePath }}/deploy.bicep' + # templateSpecsRGName: '${{ env.templateSpecsRGName }}' + # templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + # templateSpecsDescription: '${{ env.templateSpecsDescription }}' + # templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + # bicepRegistryName: '${{ env.bicepRegistryName }}' + # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' From 8e26711753f07180e3cc453f17cabfb31177fe0f Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 19:51:23 +0200 Subject: [PATCH 039/133] 2 jobs --- .github/workflows/linter.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index e5a433cb4e..679fbe2927 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -28,6 +28,10 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} FILTER_REGEX_EXCLUDE: '[module.tests.ps1|Get\-ModulesAsMarkdownTable.ps1|.*yml]' + psrule: + name: PSRule + runs-on: ubuntu-latest + steps: # Analyze repository with PSRule - name: Run PSRule analysis uses: microsoft/ps-rule@v2.4.0 From 0d6d1d14e067d3f3479af0bcab2638ca8406ca59 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 19:54:33 +0200 Subject: [PATCH 040/133] inputpath --- .github/workflows/linter.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 679fbe2927..4ee5a5036e 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -38,15 +38,14 @@ jobs: continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + inputPath: 'modules/' outputFormat: Csv - outputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' - option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' + outputPath: 'modules/PSRule-output.csv' - name: Output to Github Logs if: always() - run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' + run: cat 'modules/PSRule-output.csv' - name: Output to Github summaries if: always() - run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' >> $GITHUB_STEP_SUMMARY + run: cat 'modules/PSRule-output.csv' >> $GITHUB_STEP_SUMMARY From d9220500ffabc230e306111822ed723a11315de2 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 19:57:23 +0200 Subject: [PATCH 041/133] add init --- .github/workflows/linter.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 4ee5a5036e..cb3e1de4c2 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -33,6 +33,12 @@ jobs: runs-on: ubuntu-latest steps: # Analyze repository with PSRule + - name: Checkout + uses: actions/checkout@v3 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} - name: Run PSRule analysis uses: microsoft/ps-rule@v2.4.0 continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project @@ -41,11 +47,9 @@ jobs: inputPath: 'modules/' outputFormat: Csv outputPath: 'modules/PSRule-output.csv' - - name: Output to Github Logs if: always() run: cat 'modules/PSRule-output.csv' - - name: Output to Github summaries if: always() run: cat 'modules/PSRule-output.csv' >> $GITHUB_STEP_SUMMARY From 289e5a13f164ea7b2a939a9ab7a19072994acbed Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 20:01:02 +0200 Subject: [PATCH 042/133] variables --- .github/workflows/linter.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index cb3e1de4c2..16f136f107 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -2,6 +2,10 @@ name: '.Platform: Linter' on: [pull_request] +env: + variablesPath: 'settings.yml' + modulesPath: 'modules' + jobs: build: name: Linter @@ -44,12 +48,12 @@ jobs: continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: 'modules/' + inputPath: '${{ modulesPath }}/' outputFormat: Csv - outputPath: 'modules/PSRule-output.csv' + outputPath: '${{ modulesPath }}/PSRule-output.csv' - name: Output to Github Logs if: always() - run: cat 'modules/PSRule-output.csv' + run: cat '${{ modulesPath }}/PSRule-output.csv' - name: Output to Github summaries if: always() - run: cat 'modules/PSRule-output.csv' >> $GITHUB_STEP_SUMMARY + run: cat '${{ modulesPath }}/PSRule-output.csv' >> $GITHUB_STEP_SUMMARY From 18b729a5a86f275b2818b771fdb40a6b7cf03368 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 20:02:06 +0200 Subject: [PATCH 043/133] env variables --- .github/workflows/linter.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 16f136f107..05b1376512 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -48,12 +48,12 @@ jobs: continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: '${{ modulesPath }}/' + inputPath: '${{ env.modulesPath }}/' outputFormat: Csv - outputPath: '${{ modulesPath }}/PSRule-output.csv' + outputPath: '${{ env.modulesPath }}/PSRule-output.csv' - name: Output to Github Logs if: always() - run: cat '${{ modulesPath }}/PSRule-output.csv' + run: cat '${{ env.modulesPath }}/PSRule-output.csv' - name: Output to Github summaries if: always() - run: cat '${{ modulesPath }}/PSRule-output.csv' >> $GITHUB_STEP_SUMMARY + run: cat '${{ env.modulesPath }}/PSRule-output.csv' >> $GITHUB_STEP_SUMMARY From c2c19616f8737a862f695009c5b020faec789916 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 20:09:52 +0200 Subject: [PATCH 044/133] call function --- .github/workflows/linter.yml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 05b1376512..749117135b 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -54,6 +54,27 @@ jobs: - name: Output to Github Logs if: always() run: cat '${{ env.modulesPath }}/PSRule-output.csv' + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + # Grouping task logs + Write-Output '::group::Replace tokens in template file' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'PSRuleValidation' 'Set-PSRuleOutput.ps1') + + # Populate parameter input + $ParameterInput = @{ + inputFilePath = '${{ env.modulesPath }}/PSRule-output.csv' + outputFilePath = '${{ env.modulesPath }}/PSRule-output.md' + } + + # Invoke function + $null = Set-PSRuleOutput @ParameterInput + + Write-Output '::endgroup::' - name: Output to Github summaries if: always() - run: cat '${{ env.modulesPath }}/PSRule-output.csv' >> $GITHUB_STEP_SUMMARY + run: cat '${{ env.modulesPath }}/PSRule-output.md' >> $GITHUB_STEP_SUMMARY From 0f7848d2aa997b5c32764af23384523a2939c387 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 20:31:54 +0200 Subject: [PATCH 045/133] md detail summary --- .../PSRuleValidation/Set-PSRuleOutput.ps1 | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 36af10e843..b7ea66f473 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -39,14 +39,16 @@ if ($failedRules.Count -gt 0) { #Create Failing table - + $failContent = [System.Collections.ArrayList]@( '# Rules Failed', '', + '
', + 'GitHub private repository', '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' ) - + foreach ($content in $failedRules ) { # Shorten the target name for deployment resoure type if ($content.TargetType -eq 'Microsoft.Resources/deployments') { @@ -58,15 +60,16 @@ try { $PSRuleReferenceUrl = '{0}/{1}' -f $TemplatesBaseUrl, $content.RuleName $null = Invoke-WebRequest -Uri $PSRuleReferenceUrl - $resourceLink = "[" + $content.RuleName + "](" + $PSRuleReferenceUrl + ")" - } - catch { + $resourceLink = '[' + $content.RuleName + '](' + $PSRuleReferenceUrl + ')' + } catch { Write-Warning "Unable to build url for $content.RuleName" $resourceLink = $content.RuleName } $failContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) - + } + $failContent += [System.Collections.ArrayList]@( + '
') #Append markdown with failed rules table Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $failContent } @@ -92,13 +95,12 @@ try { $PSRuleReferenceUrl = '{0}/{1}' -f $TemplatesBaseUrl, $content.RuleName $null = Invoke-WebRequest -Uri $PSRuleReferenceUrl - $resourceLink = "[" + $content.RuleName + "](" + $PSRuleReferenceUrl + ")" - } - catch { + $resourceLink = '[' + $content.RuleName + '](' + $PSRuleReferenceUrl + ')' + } catch { Write-Warning "Unable to build url for $content.RuleName" $resourceLink = $content.RuleName } - + $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) } @@ -114,5 +116,5 @@ } - + From 393c348cd431aa439a80e4e03a703ebd1b9a116d Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 20:35:00 +0200 Subject: [PATCH 046/133] new lines --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index b7ea66f473..35b9f37ba7 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -45,6 +45,7 @@ '', '
', 'GitHub private repository', + '', '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' ) @@ -69,7 +70,9 @@ } $failContent += [System.Collections.ArrayList]@( - '
') + '', + '', + '') #Append markdown with failed rules table Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $failContent } From 6cf317b0d91f9d1178c61e0fecc11c90a1e15521 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 20:43:19 +0200 Subject: [PATCH 047/133] details pass and fail --- .github/workflows/linter.yml | 7 ++----- .../pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 13 ++++++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 749117135b..585bb60c67 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -51,10 +51,7 @@ jobs: inputPath: '${{ env.modulesPath }}/' outputFormat: Csv outputPath: '${{ env.modulesPath }}/PSRule-output.csv' - - name: Output to Github Logs - if: always() - run: cat '${{ env.modulesPath }}/PSRule-output.csv' - - name: 'Replace tokens in template file' + - name: 'Parse CSV content' uses: azure/powershell@v1 with: azPSVersion: 'latest' @@ -75,6 +72,6 @@ jobs: $null = Set-PSRuleOutput @ParameterInput Write-Output '::endgroup::' - - name: Output to Github summaries + - name: Output to GitHub job summaries if: always() run: cat '${{ env.modulesPath }}/PSRule-output.md' >> $GITHUB_STEP_SUMMARY diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 35b9f37ba7..8127fa3395 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -41,10 +41,10 @@ #Create Failing table $failContent = [System.Collections.ArrayList]@( - '# Rules Failed', + '## Rules Failed', '', '
', - 'GitHub private repository', + 'List of Rules Failed', '', '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' @@ -81,7 +81,10 @@ if ($passedRules.Count -gt 0) { $passContent = [System.Collections.ArrayList]@( - '# Rules Passed', + '## Rules Passed', + '', + '
', + 'List of Rules Passed', '', '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' @@ -109,14 +112,14 @@ } $passContent += [System.Collections.ArrayList]@( + '', + '
', '') #Append markdown with passed rules table Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $passContent } - - } From d930a7af6cba08f7b308dc69240f526dd133d7e4 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 21:08:02 +0200 Subject: [PATCH 048/133] moduleTestFiles --- .github/workflows/linter.yml | 52 ++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 585bb60c67..c67b24a3db 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -43,6 +43,58 @@ jobs: uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + # Grouping task logs + Write-Output '::group::Replace tokens in template file' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # Populate tokens + $Tokens = @{ + resourceGroupName = '${{ env.resourceGroupName }}' + subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + tenantId = '${{ env.ARM_TENANT_ID }}' + } + + ## Add local (source control) tokens + $tokenMap = @{} + foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + } + Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + $Tokens += $tokenMap + + ## Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + } + + # Get File Path List + $moduleTestFiles = @() + $moduleTestFiles += Get-ChildItem -Path '${{ env.modulesPath }} -Filter *.test.bicep -Recurse -File -Name + + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = $moduleTestFiles + Tokens = $Tokens + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' + } + + Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Token Replacement Functionality [For Module] + $null = Convert-TokensInFileList @ConvertTokensInputs + + Write-Output '::endgroup::' + - name: Run PSRule analysis uses: microsoft/ps-rule@v2.4.0 continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project From bc4539812ac71fb6fee4520213beffb6a8df3504 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 21:15:51 +0200 Subject: [PATCH 049/133] moduleTest --- .github/workflows/linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index c67b24a3db..49be52bf17 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -78,7 +78,7 @@ jobs: # Get File Path List $moduleTestFiles = @() - $moduleTestFiles += Get-ChildItem -Path '${{ env.modulesPath }} -Filter *.test.bicep -Recurse -File -Name + $moduleTestFiles += Get-ChildItem -Path '${{ env.modulesPath }}' -Filter *.test.bicep -Recurse -File -Name # Construct Token Function Input $ConvertTokensInputs = @{ From ddd803f80d3d6683a8807a0cbe24da499cf8ad74 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 21:20:24 +0200 Subject: [PATCH 050/133] join path --- .github/workflows/linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 49be52bf17..27e78951d0 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -78,7 +78,7 @@ jobs: # Get File Path List $moduleTestFiles = @() - $moduleTestFiles += Get-ChildItem -Path '${{ env.modulesPath }}' -Filter *.test.bicep -Recurse -File -Name + $moduleTestFiles += Get-ChildItem -Path (Join-Path $env:GITHUB_WORKSPACE '${{ env.modulesPath }}') -Filter *.test.bicep -Recurse -File -Name # Construct Token Function Input $ConvertTokensInputs = @{ From 6a87be4f62772315bb1cb66bdd50341b8d7f43e1 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 21:28:55 +0200 Subject: [PATCH 051/133] modulesFolderPath --- .github/workflows/linter.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 27e78951d0..d9c6257380 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -77,9 +77,12 @@ jobs: } # Get File Path List + $modulesFolderPath = Join-Path $env:GITHUB_WORKSPACE '${{ env.modulesPath }}' + Write-Verbose $modulesFolderPath -Verbose $moduleTestFiles = @() - $moduleTestFiles += Get-ChildItem -Path (Join-Path $env:GITHUB_WORKSPACE '${{ env.modulesPath }}') -Filter *.test.bicep -Recurse -File -Name - + $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Filter *.test.bicep -Recurse -File -Name + Write-Verbose '$($moduleTestFiles.Count)' -Verbose + Write-Verbose '$($moduleTestFiles.GetType())' -Verbose # Construct Token Function Input $ConvertTokensInputs = @{ FilePathList = $moduleTestFiles From 72d7987e1e635c3a30c8668a36d67c7c0a4fc496 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 21:32:41 +0200 Subject: [PATCH 052/133] modulesFolderPath collection --- .github/workflows/linter.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index d9c6257380..568f645477 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -79,10 +79,10 @@ jobs: # Get File Path List $modulesFolderPath = Join-Path $env:GITHUB_WORKSPACE '${{ env.modulesPath }}' Write-Verbose $modulesFolderPath -Verbose - $moduleTestFiles = @() + $moduleTestFiles = [System.Collections.ArrayList]@() $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Filter *.test.bicep -Recurse -File -Name - Write-Verbose '$($moduleTestFiles.Count)' -Verbose - Write-Verbose '$($moduleTestFiles.GetType())' -Verbose + Write-Verbose $($moduleTestFiles.Count) -Verbose + Write-Verbose $($moduleTestFiles.GetType()) -Verbose # Construct Token Function Input $ConvertTokensInputs = @{ FilePathList = $moduleTestFiles From f674dcc0f741f453f77a75744c4fa82390deb021 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 21:38:08 +0200 Subject: [PATCH 053/133] modulesFolderPath no filter --- .github/workflows/linter.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 568f645477..b5dc99f406 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -79,8 +79,10 @@ jobs: # Get File Path List $modulesFolderPath = Join-Path $env:GITHUB_WORKSPACE '${{ env.modulesPath }}' Write-Verbose $modulesFolderPath -Verbose + $moduleTestFiles = [System.Collections.ArrayList]@() - $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Filter *.test.bicep -Recurse -File -Name + $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Recurse -File -Name + # -Filter *.test.bicep Write-Verbose $($moduleTestFiles.Count) -Verbose Write-Verbose $($moduleTestFiles.GetType()) -Verbose # Construct Token Function Input From a84b424056da1276bad5eac188bf2185b20efd2a Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 21:39:30 +0200 Subject: [PATCH 054/133] modulesFolderPath no file --- .github/workflows/linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index b5dc99f406..412e678415 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -81,7 +81,7 @@ jobs: Write-Verbose $modulesFolderPath -Verbose $moduleTestFiles = [System.Collections.ArrayList]@() - $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Recurse -File -Name + $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Recurse # -Filter *.test.bicep Write-Verbose $($moduleTestFiles.Count) -Verbose Write-Verbose $($moduleTestFiles.GetType()) -Verbose From 057a4c2f71ba1a73f5c50ce0b5d721dcb895159d Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 21:46:02 +0200 Subject: [PATCH 055/133] force --- .github/workflows/linter.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 412e678415..9ead8722aa 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -81,8 +81,8 @@ jobs: Write-Verbose $modulesFolderPath -Verbose $moduleTestFiles = [System.Collections.ArrayList]@() - $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Recurse - # -Filter *.test.bicep + $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Filter *.test.bicep -Recurse -Force -Name + Write-Verbose $($moduleTestFiles.Count) -Verbose Write-Verbose $($moduleTestFiles.GetType()) -Verbose # Construct Token Function Input From a60f695f8f702f1d42252f4ca757aceb99f62843 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 21:49:53 +0200 Subject: [PATCH 056/133] cleanup --- .github/workflows/linter.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 9ead8722aa..d29549539d 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -78,13 +78,9 @@ jobs: # Get File Path List $modulesFolderPath = Join-Path $env:GITHUB_WORKSPACE '${{ env.modulesPath }}' - Write-Verbose $modulesFolderPath -Verbose - $moduleTestFiles = [System.Collections.ArrayList]@() $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Filter *.test.bicep -Recurse -Force -Name - Write-Verbose $($moduleTestFiles.Count) -Verbose - Write-Verbose $($moduleTestFiles.GetType()) -Verbose # Construct Token Function Input $ConvertTokensInputs = @{ FilePathList = $moduleTestFiles From a62af964e55971aefa163430fb37ecf7b0383a3b Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 21:54:41 +0200 Subject: [PATCH 057/133] root --- .github/workflows/linter.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index d29549539d..1ebf6f738a 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -79,7 +79,9 @@ jobs: # Get File Path List $modulesFolderPath = Join-Path $env:GITHUB_WORKSPACE '${{ env.modulesPath }}' $moduleTestFiles = [System.Collections.ArrayList]@() - $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Filter *.test.bicep -Recurse -Force -Name + # $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Filter *.test.bicep -Recurse -Force -Name + $moduleTestFiles += Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.test.bicep -Recurse -Force -Name + # Construct Token Function Input $ConvertTokensInputs = @{ From 3baec7b23d01765bec321071ab89083067068e88 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 22:17:25 +0200 Subject: [PATCH 058/133] noroot --- .github/workflows/linter.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 1ebf6f738a..ae99467535 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -80,7 +80,8 @@ jobs: $modulesFolderPath = Join-Path $env:GITHUB_WORKSPACE '${{ env.modulesPath }}' $moduleTestFiles = [System.Collections.ArrayList]@() # $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Filter *.test.bicep -Recurse -Force -Name - $moduleTestFiles += Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.test.bicep -Recurse -Force -Name + # Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.test.bicep -Recurse -Force -Name | Join-Path $env:GITHUB_WORKSPACE '$._' + $moduleTestFiles += Get-ChildItem -Filter *.test.bicep -Recurse -Force -Name | ForEach-Object {$_.root} | Join-Path -ChildPath "Subdir" # Construct Token Function Input From de1233e5dcfdb336814fcece614cc9a0bf64fee6 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 22:23:13 +0200 Subject: [PATCH 059/133] convert verbose --- .github/workflows/linter.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index ae99467535..665602b991 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -81,7 +81,7 @@ jobs: $moduleTestFiles = [System.Collections.ArrayList]@() # $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Filter *.test.bicep -Recurse -Force -Name # Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.test.bicep -Recurse -Force -Name | Join-Path $env:GITHUB_WORKSPACE '$._' - $moduleTestFiles += Get-ChildItem -Filter *.test.bicep -Recurse -Force -Name | ForEach-Object {$_.root} | Join-Path -ChildPath "Subdir" + $moduleTestFiles += Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.test.bicep -Recurse -Force -Name | ForEach-Object {$_.root} | Join-Path -ChildPath "Subdir" # Construct Token Function Input @@ -95,7 +95,8 @@ jobs: Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose # Invoke Token Replacement Functionality [For Module] - $null = Convert-TokensInFileList @ConvertTokensInputs + # $null = + Convert-TokensInFileList @ConvertTokensInputs -verbose Write-Output '::endgroup::' From 21e5a32532f3e72e8bee13158b30cd0516bcb687 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 21 Sep 2022 22:29:36 +0200 Subject: [PATCH 060/133] no pipe --- .github/workflows/linter.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 665602b991..7d0e53bcf3 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -81,7 +81,8 @@ jobs: $moduleTestFiles = [System.Collections.ArrayList]@() # $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Filter *.test.bicep -Recurse -Force -Name # Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.test.bicep -Recurse -Force -Name | Join-Path $env:GITHUB_WORKSPACE '$._' - $moduleTestFiles += Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.test.bicep -Recurse -Force -Name | ForEach-Object {$_.root} | Join-Path -ChildPath "Subdir" + $moduleTestFiles += Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.test.bicep -Recurse -Force -Name + # | ForEach-Object {$_.root} | Join-Path -ChildPath "Subdir" # Construct Token Function Input From bca177c0409cd08b71c33b6651f7b720571ab101 Mon Sep 17 00:00:00 2001 From: elisa anzelmo Date: Thu, 22 Sep 2022 10:12:32 +0200 Subject: [PATCH 061/133] [Hackaton] Added PSRule Output to job summary (#2110) * Create ps-rule.yaml (#2066) * [Hackaton] First test for PSRule exclusions on KeyVault (#2067) * Changed KeyVault workflow * Changed inputPath for KeyVault workflow * Comment other steps * Added exclude modules for version.json * Enable custom rules exclusions * Suppression of Azure.Resource.UseTags * Added suppression with namePrefix * Uncomment validation steps * [Hackathon] Token replacement for the resource group module (#2068) * Added ps-rule.yaml * testing psrule * fixing typo yml * fixed typo * testing input path * Added token replacement task * Adding Azure login task * checking context * adding write-output * Adding setEnvironmentVariables * Added matrix and needs * Testing inputPath /${{ matrix.moduleTestFilePaths }} * Uncommented workflow Co-authored-by: Elena Batanero Garcia * [Hackathon] PSRule addition for VNet resource (#2070) * setting json expansion to false * add psrule to vnet workflow * exclude tagging psrule * custom psrule.yaml per module * typo in psrule.yaml * updated typo in psrule.yaml * uncomment deployment job Co-authored-by: Karthik Venkatraman * [Hackathon] Added suppression group for dependency resources (#2071) * suppress dependancy * comment deployment validation job * add supress yaml * supress yaml poc * rule filter * suppress rule filter * add baseline for suppression * baseline for suppression * filed suppress poc * suppress poc * psrule suppression * Updates to suppression yaml Co-authored-by: Karthik Venkatraman * [Hackaton] Align 3 module workflows to use PSRule test matrix (#2065) * linter * exclude parameters * pathIgnore * clean up token replacement * clean up token replacement further * resize token replacement * typo * modulePath * job name * no psrule * no psrule step * fix inputpath * comment out continue on error option * Align KV * kv no psrule * replace * no replace * replace 1 * indent * align vnet * align vnet and enable deployment * linter test removed * psrule back * vnet no deploy * comment deployment * [Hackathon] PSRule output in markdown (#2072) * output formatting * psrule output summary * output summary psrule * xml output * markdown output * publish output always * adding output to github summary * filter psrule outcome * psrul outcome env * outcome filter psrule * outcome filter psrule * output summary filter psrule * output to file psrule * adding output github summary Co-authored-by: Karthik Venkatraman * [Hackaton] Psrule output on csv format with detailed results (#2089) * select xml output with summary * outout format as json * json without summary * no outcome, summary on output md * no outcome filter, summaery on, markdown * no summary, no outcome, markdown * output csv, detail * output csv, summary, no outcome * wide format, no summarized, no outcome * no summary, no outcome, output yaml * csv + details - no outcome and no summary * [Hackaton] csv output on Keyvault module (#2091) * kv summart csv no outcome * print summary * outcome all, format csv as summary * csv detailed all * csv all detail execution supresswaring * rull.pass log information * empty options with summary on * csv test * [Hackathon] Set PSRule output script (#2093) * suppress dependancy * comment deployment validation job * add supress yaml * supress yaml poc * rule filter * suppress rule filter * add baseline for suppression * baseline for suppression * TEsting supressiongroup * testing supressSelector * testing supression group * Create test.md * Update test.md * Added csv to md powershell script and results * Updated md * Delete test.md * Updates to PSRule output script * PS output script changes * Putting the virtualnetworks workflow back from hack/topic6 * removed output md and csv * removing baseline Co-authored-by: Karthik Venkatraman Co-authored-by: Elena Batanero Garcia * psoutput from workflow * suppressed warnings * added summary markdown * addedd summary mkd for pass rules * updated failing title Co-authored-by: Elena Batanero <46710322+elbatane@users.noreply.github.com> Co-authored-by: Karel De Winter <40666689+kareldewinter@users.noreply.github.com> Co-authored-by: Elena Batanero Garcia Co-authored-by: Karthik Venkatraman <44262238+karthikvenkat17@users.noreply.github.com> Co-authored-by: Karthik Venkatraman Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- .../workflows/ms.network.virtualnetworks.yml | 31 +++++++++++---- .../virtualNetworks/.test/vnet-ps-rule.yaml | 3 ++ .../PSRuleValidation/Set-PSRuleOutput.ps1 | 39 ++++++++++++------- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index b29dcaffcf..2fd94c7e7e 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -157,19 +157,34 @@ jobs: outputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' - - name: Output to Github Logs + - name: 'Set PSRule Output' if: always() - run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + # Grouping task logs + Write-Output '::group::Setting Output' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'PSRuleValidation' 'Set-PSRuleOutput.ps1') - # - name: 'PSRule test summary' - # uses: EnricoMi/publish-unit-test-result-action@v1 - # if: always() - # with: - # files: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.xml' + # Populate tokens + $Input = @{ + inputFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' + outputFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.md' + } + + Write-Verbose "Set PS Rule Output with following parameters:`n $($Input | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Set PSRule Output Functionality + $null = Set-PSRuleOutput @Input + + Write-Output '::endgroup::' - name: Output to Github summaries if: always() - run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' >> $GITHUB_STEP_SUMMARY + run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.md' >> $GITHUB_STEP_SUMMARY # ############################# # # Deployment validation # diff --git a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml index c88663641a..5b96b4abf4 100644 --- a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml +++ b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml @@ -23,6 +23,9 @@ include: module: - PSRule.Rules.Azure +execution: + suppressedRuleWarning: false + output: culture: - 'en-US' diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 36af10e843..be9e296736 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -39,14 +39,17 @@ if ($failedRules.Count -gt 0) { #Create Failing table - + $failContent = [System.Collections.ArrayList]@( '# Rules Failed', '', + '
', + 'Rules Failed', + '', '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' ) - + foreach ($content in $failedRules ) { # Shorten the target name for deployment resoure type if ($content.TargetType -eq 'Microsoft.Resources/deployments') { @@ -58,15 +61,19 @@ try { $PSRuleReferenceUrl = '{0}/{1}' -f $TemplatesBaseUrl, $content.RuleName $null = Invoke-WebRequest -Uri $PSRuleReferenceUrl - $resourceLink = "[" + $content.RuleName + "](" + $PSRuleReferenceUrl + ")" - } - catch { + $resourceLink = '[' + $content.RuleName + '](' + $PSRuleReferenceUrl + ')' + } catch { Write-Warning "Unable to build url for $content.RuleName" $resourceLink = $content.RuleName } $failContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) - + } + $failContent += [System.Collections.ArrayList]@( + '', + '
', + '' + ) #Append markdown with failed rules table Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $failContent } @@ -77,6 +84,10 @@ $passContent = [System.Collections.ArrayList]@( '# Rules Passed', '', + '
', + 'Rules Passed', + '', + '', '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' ) @@ -92,20 +103,20 @@ try { $PSRuleReferenceUrl = '{0}/{1}' -f $TemplatesBaseUrl, $content.RuleName $null = Invoke-WebRequest -Uri $PSRuleReferenceUrl - $resourceLink = "[" + $content.RuleName + "](" + $PSRuleReferenceUrl + ")" - } - catch { + $resourceLink = '[' + $content.RuleName + '](' + $PSRuleReferenceUrl + ')' + } catch { Write-Warning "Unable to build url for $content.RuleName" $resourceLink = $content.RuleName } - + $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) } - $passContent += [System.Collections.ArrayList]@( - '') - + '', + '
', + '' + ) #Append markdown with passed rules table Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $passContent @@ -114,5 +125,5 @@ } - + From f7fd0c468d9d04a84ab00c0d32fa56246bd93f6a Mon Sep 17 00:00:00 2001 From: Fabio Masciotra Date: Thu, 22 Sep 2022 11:38:08 +0200 Subject: [PATCH 062/133] [Hackaton] Hack/topic6 virtual network PSrule on GitHub Action (#2111) * hackaton: action.yml * update action * update * update * update * output * deleted commented block Co-authored-by: Fabio Masciotra --- .../templates/validateModulePSRule/action.yml | 134 ++++++++++++++ .../workflows/ms.network.virtualnetworks.yml | 170 +++++++++--------- ps-rule.yaml | 5 +- 3 files changed, 225 insertions(+), 84 deletions(-) create mode 100644 .github/actions/templates/validateModulePSRule/action.yml diff --git a/.github/actions/templates/validateModulePSRule/action.yml b/.github/actions/templates/validateModulePSRule/action.yml new file mode 100644 index 0000000000..d1e753bb54 --- /dev/null +++ b/.github/actions/templates/validateModulePSRule/action.yml @@ -0,0 +1,134 @@ +######################################################### +## 'Validate module with PSRule' Composite Action ## +######################################################### +## +## This composite action contains the logic to validate a module using a set of PSRule tests +## +######################################################### +## +##-------------------------------------------## +## ACTION PARAMETERS ## +##-------------------------------------------## +## +## |==================================================================================================================================================| +## | Parameter | Required | Default | Description | Example | +## |--------------------------|----------|---------|--------------------------------------|-----------------------------------------------------------| +## | modulePath | true | '' | The path to the module's folder | 'modules/Microsoft.ApiManagement/service' | +## | moduleTestFilePath | true | '' | The path to the module PSRule tests. | 'utilities/pipelines/staticValidation/module.tests.ps1' | +## |==================================================================================================================================================| +## +##---------------------------------------------## + +name: 'Execute PSRule module tests' +description: 'Execute PSRule module tests (if any)' + +inputs: + modulePath: + description: "The path to the module's folder" + required: true + default: '' + moduleTestFilePath: + description: 'The path to the test file' + required: true + default: '' + subscriptionId: + description: 'The subscription ID to deploy to' + required: false + managementGroupId: + description: 'The management group ID to deploy to' + required: false + +runs: + using: 'composite' + steps: + # [Module PSRule Test] task(s) + #----------------------------- + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + $templateFilePath = '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}' + # Grouping task logs + Write-Output '::group::Replace tokens in template file' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # Populate tokens + $Tokens = @{ + resourceGroupName = '${{ env.resourceGroupName }}' + subscriptionId = '${{ inputs.subscriptionId }}' + managementGroupId = '${{ inputs.managementGroupId }}' + tenantId = '${{ env.ARM_TENANT_ID }}' + } + + ## Add local (source control) tokens + $tokenMap = @{} + foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + } + Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + $Tokens += $tokenMap + + ## Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + } + + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = @($templateFilePath) + Tokens = $Tokens + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' + } + + Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Token Replacement Functionality [For Module] + $null = Convert-TokensInFileList @ConvertTokensInputs + + Write-Output '::endgroup::' + + # Run analysis by using the PSRule GitHub action. + - name: Run PSRule analysis + uses: microsoft/ps-rule@v2.4.0 + # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project + with: + modules: 'PSRule.Rules.Azure' + inputPath: '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}' + outputFormat: Csv + outputPath: '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}-output.csv' + # option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' + + - name: 'Set PSRule Output' + if: always() + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + # Grouping task logs + Write-Output '::group::Setting Output' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'PSRuleValidation' 'Set-PSRuleOutput.ps1') + + # Populate tokens + $Input = @{ + inputFilePath = '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}-output.csv' + outputFilePath = '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}-output.md' + } + + Write-Verbose "Set PS Rule Output with following parameters:`n $($Input | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Set PSRule Output Functionality + $null = Set-PSRuleOutput @Input + + Write-Output '::endgroup::' + + - name: Output to Github summaries + shell: pwsh + if: always() + run: cat '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}-output.md' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index 2fd94c7e7e..6736acfeb9 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -97,94 +97,102 @@ jobs: uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} - - name: 'Replace tokens in template file' - uses: azure/powershell@v1 + - name: Set PSRule validation + uses: ./.github/actions/templates/validateModulePSRule with: - azPSVersion: 'latest' - inlineScript: | - $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # Grouping task logs - Write-Output '::group::Replace tokens in template file' - - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # Populate tokens - $Tokens = @{ - resourceGroupName = '${{ env.resourceGroupName }}' - subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - tenantId = '${{ env.ARM_TENANT_ID }}' - } - - ## Add local (source control) tokens - $tokenMap = @{} - foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - } - Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - $Tokens += $tokenMap - - ## Swap 'namePrefix' token if empty and provided as a GitHub secret - if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - } - - # Construct Token Function Input - $ConvertTokensInputs = @{ - FilePathList = @($templateFilePath) - Tokens = $Tokens - TokenPrefix = '${{ env.tokenPrefix }}' - TokenSuffix = '${{ env.tokenSuffix }}' - } - - Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # Invoke Token Replacement Functionality [For Module] - $null = Convert-TokensInFileList @ConvertTokensInputs - - Write-Output '::endgroup::' - - # Run analysis by using the PSRule GitHub action. - - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.4.0 - # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project - with: - modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - outputFormat: Csv - outputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' - option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' - - - name: 'Set PSRule Output' - if: always() - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - # Grouping task logs - Write-Output '::group::Setting Output' + modulePath: ${{ env.modulePath }} + moduleTestFilePath: ${{ matrix.moduleTestFilePaths }} + subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + + # - name: 'Replace tokens in template file' + # uses: azure/powershell@v1 + # with: + # azPSVersion: 'latest' + # inlineScript: | + # $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # # Grouping task logs + # Write-Output '::group::Replace tokens in template file' + + # # Load used functions + # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # # Populate tokens + # $Tokens = @{ + # resourceGroupName = '${{ env.resourceGroupName }}' + # subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + # managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + # tenantId = '${{ env.ARM_TENANT_ID }}' + # } + + # ## Add local (source control) tokens + # $tokenMap = @{} + # foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + # $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + # } + # Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + # $Tokens += $tokenMap + + # ## Swap 'namePrefix' token if empty and provided as a GitHub secret + # if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + # Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + # $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + # } + + # # Construct Token Function Input + # $ConvertTokensInputs = @{ + # FilePathList = @($templateFilePath) + # Tokens = $Tokens + # TokenPrefix = '${{ env.tokenPrefix }}' + # TokenSuffix = '${{ env.tokenSuffix }}' + # } + + # Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # # Invoke Token Replacement Functionality [For Module] + # $null = Convert-TokensInFileList @ConvertTokensInputs + + # Write-Output '::endgroup::' + + # # Run analysis by using the PSRule GitHub action. + # - name: Run PSRule analysis + # uses: microsoft/ps-rule@v2.4.0 + # # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project + # with: + # modules: 'PSRule.Rules.Azure' + # inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + # outputFormat: Csv + # outputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' + # option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' + + # - name: 'Set PSRule Output' + # if: always() + # uses: azure/powershell@v1 + # with: + # azPSVersion: 'latest' + # inlineScript: | + # # Grouping task logs + # Write-Output '::group::Setting Output' - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'PSRuleValidation' 'Set-PSRuleOutput.ps1') + # # Load used functions + # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'PSRuleValidation' 'Set-PSRuleOutput.ps1') - # Populate tokens - $Input = @{ - inputFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' - outputFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.md' - } + # # Populate tokens + # $Input = @{ + # inputFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' + # outputFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.md' + # } - Write-Verbose "Set PS Rule Output with following parameters:`n $($Input | ConvertTo-Json -Depth 10)" -Verbose + # Write-Verbose "Set PS Rule Output with following parameters:`n $($Input | ConvertTo-Json -Depth 10)" -Verbose - # Invoke Set PSRule Output Functionality - $null = Set-PSRuleOutput @Input + # # Invoke Set PSRule Output Functionality + # $null = Set-PSRuleOutput @Input - Write-Output '::endgroup::' + # Write-Output '::endgroup::' - - name: Output to Github summaries - if: always() - run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.md' >> $GITHUB_STEP_SUMMARY + # - name: Output to Github summaries + # if: always() + # run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.md' >> $GITHUB_STEP_SUMMARY # ############################# # # Deployment validation # diff --git a/ps-rule.yaml b/ps-rule.yaml index 0a1e9f91b7..5d6208f63d 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -29,10 +29,9 @@ execution: output: culture: - 'en-US' - outcome: 'All' + #outcome: 'All' #as: 'Summary' - input: pathIgnore: # Ignore other files in the repository. @@ -61,7 +60,7 @@ rule: exclude: # Ignore the following rules for all resources - Azure.KeyVault.PurgeProtect - + - Azure.Resource.UseTags # Suppression ignores rules for a specific Azure resource by name. # suppression: # Azure.Resource.UseTags: From da2a9de872246d93e44cdeaac605ec465eacbe5e Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Thu, 22 Sep 2022 14:29:36 +0200 Subject: [PATCH 063/133] [Hackaton] Fix job summary output in GH action (#2112) * linter * exclude parameters * pathIgnore * clean up token replacement * clean up token replacement further * resize token replacement * typo * modulePath * job name * no psrule * no psrule step * fix inputpath * comment out continue on error option * Align KV * kv no psrule * replace * no replace * replace 1 * indent * align vnet * align vnet and enable deployment * linter test removed * psrule back * vnet no deploy * comment deployment * job summary out * Print to job schedule back to action * action shell * remove module input * cleanup vnet --- .../templates/validateModulePSRule/action.yml | 6 +- .../workflows/ms.network.virtualnetworks.yml | 89 ------------------- 2 files changed, 1 insertion(+), 94 deletions(-) diff --git a/.github/actions/templates/validateModulePSRule/action.yml b/.github/actions/templates/validateModulePSRule/action.yml index d1e753bb54..aae731530c 100644 --- a/.github/actions/templates/validateModulePSRule/action.yml +++ b/.github/actions/templates/validateModulePSRule/action.yml @@ -23,10 +23,6 @@ name: 'Execute PSRule module tests' description: 'Execute PSRule module tests (if any)' inputs: - modulePath: - description: "The path to the module's folder" - required: true - default: '' moduleTestFilePath: description: 'The path to the test file' required: true @@ -129,6 +125,6 @@ runs: Write-Output '::endgroup::' - name: Output to Github summaries - shell: pwsh + shell: bash if: always() run: cat '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}-output.md' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index 6736acfeb9..9109ada5a4 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -105,95 +105,6 @@ jobs: subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - # - name: 'Replace tokens in template file' - # uses: azure/powershell@v1 - # with: - # azPSVersion: 'latest' - # inlineScript: | - # $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # # Grouping task logs - # Write-Output '::group::Replace tokens in template file' - - # # Load used functions - # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # # Populate tokens - # $Tokens = @{ - # resourceGroupName = '${{ env.resourceGroupName }}' - # subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - # managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - # tenantId = '${{ env.ARM_TENANT_ID }}' - # } - - # ## Add local (source control) tokens - # $tokenMap = @{} - # foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - # $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - # } - # Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - # $Tokens += $tokenMap - - # ## Swap 'namePrefix' token if empty and provided as a GitHub secret - # if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - # Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - # $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - # } - - # # Construct Token Function Input - # $ConvertTokensInputs = @{ - # FilePathList = @($templateFilePath) - # Tokens = $Tokens - # TokenPrefix = '${{ env.tokenPrefix }}' - # TokenSuffix = '${{ env.tokenSuffix }}' - # } - - # Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # # Invoke Token Replacement Functionality [For Module] - # $null = Convert-TokensInFileList @ConvertTokensInputs - - # Write-Output '::endgroup::' - - # # Run analysis by using the PSRule GitHub action. - # - name: Run PSRule analysis - # uses: microsoft/ps-rule@v2.4.0 - # # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project - # with: - # modules: 'PSRule.Rules.Azure' - # inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # outputFormat: Csv - # outputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' - # option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' - - # - name: 'Set PSRule Output' - # if: always() - # uses: azure/powershell@v1 - # with: - # azPSVersion: 'latest' - # inlineScript: | - # # Grouping task logs - # Write-Output '::group::Setting Output' - - # # Load used functions - # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'PSRuleValidation' 'Set-PSRuleOutput.ps1') - - # # Populate tokens - # $Input = @{ - # inputFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.csv' - # outputFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.md' - # } - - # Write-Verbose "Set PS Rule Output with following parameters:`n $($Input | ConvertTo-Json -Depth 10)" -Verbose - - # # Invoke Set PSRule Output Functionality - # $null = Set-PSRuleOutput @Input - - # Write-Output '::endgroup::' - - # - name: Output to Github summaries - # if: always() - # run: cat '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}-output.md' >> $GITHUB_STEP_SUMMARY - # ############################# # # Deployment validation # # ############################# From f4c8e31e692d9e18e4931927b046228d3b1863cf Mon Sep 17 00:00:00 2001 From: Fabio Masciotra Date: Thu, 22 Sep 2022 16:02:05 +0200 Subject: [PATCH 064/133] [hackaton]: Hack/topic6 fabmas/psrule test updated RG, KV, VNET (#2113) * update * update * kv update * ps1 * update Co-authored-by: Fabio Masciotra --- .../templates/validateModulePSRule/action.yml | 3 +- .github/workflows/ms.keyvault.vaults.yml | 80 +++---------------- .../workflows/ms.network.virtualnetworks.yml | 1 - .../workflows/ms.resources.resourcegroups.yml | 76 +++--------------- ps-rule.yaml | 8 +- .../PSRuleValidation/Set-PSRuleOutput.ps1 | 12 +-- 6 files changed, 35 insertions(+), 145 deletions(-) diff --git a/.github/actions/templates/validateModulePSRule/action.yml b/.github/actions/templates/validateModulePSRule/action.yml index aae731530c..2696c0b87c 100644 --- a/.github/actions/templates/validateModulePSRule/action.yml +++ b/.github/actions/templates/validateModulePSRule/action.yml @@ -13,8 +13,9 @@ ## |==================================================================================================================================================| ## | Parameter | Required | Default | Description | Example | ## |--------------------------|----------|---------|--------------------------------------|-----------------------------------------------------------| -## | modulePath | true | '' | The path to the module's folder | 'modules/Microsoft.ApiManagement/service' | ## | moduleTestFilePath | true | '' | The path to the module PSRule tests. | 'utilities/pipelines/staticValidation/module.tests.ps1' | +## | subscriptionId | false | '' | The subscriptionId to deploy to | '1a97b80a-4dda-4f50-ab53-349e29344654' | +## | managementGroupId | false | '' | The managementGroupId to deploy to | '1a97b80a-4dda-4f50-ab53-349e29344654' | ## |==================================================================================================================================================| ## ##---------------------------------------------## diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index d6ec346e70..e7a5b9e66b 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -85,11 +85,11 @@ jobs: name: 'PsRule pre-flight validation' runs-on: ubuntu-latest needs: - - job_initialize_pipeline - # strategy: - # fail-fast: false - # matrix: - # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + - job_initialize_pipeline + strategy: + fail-fast: false + matrix: + moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} steps: - name: Checkout uses: actions/checkout@v3 @@ -97,72 +97,12 @@ jobs: uses: ./.github/actions/templates/setEnvironmentVariables with: variablesPath: ${{ env.variablesPath }} - # - name: 'Replace tokens in template file' - # uses: azure/powershell@v1 - # with: - # azPSVersion: 'latest' - # inlineScript: | - # $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # # Grouping task logs - # Write-Output '::group::Replace tokens in template file' - - # # Load used functions - # . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # # Populate tokens - # $Tokens = @{ - # resourceGroupName = '${{ env.resourceGroupName }}' - # subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - # managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - # tenantId = '${{ env.ARM_TENANT_ID }}' - # } - - # ## Add local (source control) tokens - # $tokenMap = @{} - # foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - # $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - # } - # Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - # $Tokens += $tokenMap - - # ## Swap 'namePrefix' token if empty and provided as a GitHub secret - # if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - # Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - # $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - # } - - # # Construct Token Function Input - # $ConvertTokensInputs = @{ - # FilePathList = @($templateFilePath) - # Tokens = $Tokens - # TokenPrefix = '${{ env.tokenPrefix }}' - # TokenSuffix = '${{ env.tokenSuffix }}' - # } - - # Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # # Invoke Token Replacement Functionality [For Module] - # $null = Convert-TokensInFileList @ConvertTokensInputs - - # Write-Output '::endgroup::' - - # Run analysis by using the PSRule GitHub action. - - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.4.0 - # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project + - name: Set PSRule validation + uses: ./.github/actions/templates/validateModulePSRule with: - modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulePath }}/' - outputFormat: Csv - outputPath: '${{ env.modulePath }}-output.csv' - - - name: Output to Github Logs - if: always() - run: cat '${{ env.modulePath }}-output.csv' - - - name: Output to Github summaries - if: always() - run: cat '${{ env.modulePath }}-output.csv' >> $GITHUB_STEP_SUMMARY + moduleTestFilePath: ${{ matrix.moduleTestFilePaths }} + subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' # ############################# # # Deployment validation # diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index 9109ada5a4..1ae28606d7 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -100,7 +100,6 @@ jobs: - name: Set PSRule validation uses: ./.github/actions/templates/validateModulePSRule with: - modulePath: ${{ env.modulePath }} moduleTestFilePath: ${{ matrix.moduleTestFilePaths }} subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index c1bb79b548..6dd3f78b7a 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -85,74 +85,24 @@ jobs: name: 'PsRule pre-flight validation' runs-on: ubuntu-latest needs: - - job_initialize_pipeline + - job_initialize_pipeline strategy: fail-fast: false matrix: moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Replace tokens in template file' - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - $templateFilePath = '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # Grouping task logs - Write-Output '::group::Replace tokens in template file' - - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # Populate tokens - $Tokens = @{ - resourceGroupName = '${{ env.resourceGroupName }}' - subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - tenantId = '${{ env.ARM_TENANT_ID }}' - } - - ## Add local (source control) tokens - $tokenMap = @{} - foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - } - Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - $Tokens += $tokenMap - - ## Swap 'namePrefix' token if empty and provided as a GitHub secret - if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - } - - # Construct Token Function Input - $ConvertTokensInputs = @{ - FilePathList = @($templateFilePath) - Tokens = $Tokens - TokenPrefix = '${{ env.tokenPrefix }}' - TokenSuffix = '${{ env.tokenSuffix }}' - } - - Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # Invoke Token Replacement Functionality [For Module] - $null = Convert-TokensInFileList @ConvertTokensInputs - - Write-Output '::endgroup::' - - # Run analysis by using the PSRule GitHub action. - - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.4.0 - # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project - with: - modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + - name: Checkout + uses: actions/checkout@v3 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: Set PSRule validation + uses: ./.github/actions/templates/validateModulePSRule + with: + moduleTestFilePath: ${{ matrix.moduleTestFilePaths }} + subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' # ############################# # # Deployment validation # diff --git a/ps-rule.yaml b/ps-rule.yaml index 5d6208f63d..7c66b607a7 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -57,10 +57,10 @@ configuration: rule: # Enable custom rules that don't exist in the baseline includeLocal: false - exclude: - # Ignore the following rules for all resources - - Azure.KeyVault.PurgeProtect - - Azure.Resource.UseTags + #exclude: + # Ignore the following rules for all resources + # - Azure.KeyVault.PurgeProtect + # - Azure.Resource.UseTags # Suppression ignores rules for a specific Azure resource by name. # suppression: # Azure.Resource.UseTags: diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index be9e296736..0a8b0a7aa4 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -24,7 +24,7 @@ $headerTable = [System.Collections.ArrayList]@( '# Output Summary ', '', - '| Total No. of Rules Processed | Rules Passed :white_check_mark: | Rules Failed :x: |', + '| Total No. of Processed Rules| Passed Rules :white_check_mark: | Failed Rules :x: |', '| :-- | :-- | :-- |' ) @@ -41,10 +41,10 @@ #Create Failing table $failContent = [System.Collections.ArrayList]@( - '# Rules Failed', + '# Failed Rules', '', '
', - 'Rules Failed', + 'Failed Rules', '', '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' @@ -82,10 +82,10 @@ if ($passedRules.Count -gt 0) { $passContent = [System.Collections.ArrayList]@( - '# Rules Passed', + '# Passed Rules', '', '
', - 'Rules Passed', + 'Passed Rules', '', '', '| RuleName | TargetName | Synopsis |', @@ -109,7 +109,7 @@ $resourceLink = $content.RuleName } - $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) + $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) } $passContent += [System.Collections.ArrayList]@( From 4e994d885ca5f19294b308cea8e262684c7a62e8 Mon Sep 17 00:00:00 2001 From: Karthik Venkatraman <44262238+karthikvenkat17@users.noreply.github.com> Date: Thu, 22 Sep 2022 16:56:23 +0100 Subject: [PATCH 065/133] [Hackathon] Suppress rules where not required (#2115) * exclude tags for min * typo in ps-rule yaml * update suppression yaml * keyvault rule exclusions Co-authored-by: Karthik Venkatraman --- .ps-rule/min-suppress.Rule.yaml | 16 ++++++++++++++++ ps-rule.yaml | 10 +++++----- 2 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 .ps-rule/min-suppress.Rule.yaml diff --git a/.ps-rule/min-suppress.Rule.yaml b/.ps-rule/min-suppress.Rule.yaml new file mode 100644 index 0000000000..9c984ea8da --- /dev/null +++ b/.ps-rule/min-suppress.Rule.yaml @@ -0,0 +1,16 @@ +--- +# Synopsis: Suppress Rules for min tests +apiVersion: github.com/microsoft/PSRule/v1 +kind: SuppressionGroup +metadata: + name: 'Suppressmin' +spec: + rule: + - Azure.Resource.UseTags + - Azure.KeyVault.Logs + if: + name: '.' + contains: + - 'min' + + \ No newline at end of file diff --git a/ps-rule.yaml b/ps-rule.yaml index 7c66b607a7..9ee2318a42 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -57,11 +57,11 @@ configuration: rule: # Enable custom rules that don't exist in the baseline includeLocal: false - #exclude: + exclude: # Ignore the following rules for all resources - # - Azure.KeyVault.PurgeProtect - # - Azure.Resource.UseTags + - Azure.KeyVault.PurgeProtect + # Suppression ignores rules for a specific Azure resource by name. # suppression: -# Azure.Resource.UseTags: -# - <>kvvmin001 +# Azure.KeyVault.PurgeProtect: +# - '*min*' From df43f6b26bdc94fd773311f760445e7e04505bdf Mon Sep 17 00:00:00 2001 From: Karel De Winter <40666689+kareldewinter@users.noreply.github.com> Date: Fri, 23 Sep 2022 12:04:36 +0200 Subject: [PATCH 066/133] [Hackathon] Added psrule to ado pipelines (#2121) * Added ADO validate module for PS Rule * Added DefaultWorkingDirectory to inputPath var * Changed inputPath to moduleTestFilePath * Changed inputPath and outputPath * Cleanup unused parameters templateFilePath * Added task Publish PSRule results * Changed for continue on failed * Changed for continue on error * changed displayname for getting test files * Added seperate stage for PSRuleValidation * Add dependsOn for PSRuleValidation * Added dependsOn for validation and PSRule stages * Added dependsOn for validation and PSRule * added .result for condition * Added documentation to stages and validatePsRule * Changed documentation formatting --- .../jobs.validateModulePSRule.yml | 203 ++++++++++++++++++ .../pipelineTemplates/stages.module.yml | 12 ++ 2 files changed, 215 insertions(+) create mode 100644 .azuredevops/pipelineTemplates/jobs.validateModulePSRule.yml diff --git a/.azuredevops/pipelineTemplates/jobs.validateModulePSRule.yml b/.azuredevops/pipelineTemplates/jobs.validateModulePSRule.yml new file mode 100644 index 0000000000..9084bee412 --- /dev/null +++ b/.azuredevops/pipelineTemplates/jobs.validateModulePSRule.yml @@ -0,0 +1,203 @@ +######################################################### +## 'Validate module with PSRule' Pipeline Template ## +######################################################### +## +## This pipeline template contains the logic to validate a given module's testfile with PSRule +## +## +######################################################## +## +##---------------------------------------------## +## TEMPLATE PARAMETERS ## +##---------------------------------------------## +## +## By default it uses the variables specified in the below [parameters] section. However, you can overwrite these variables in the +## referencing pipeline by providing the parameter explicitly. +## +## NOTE: If you don't need to overwrite a shared value, you can IGNORE this section +## +## |============================================================================================================================================================================================================================================| +## | Parameter | Default Value | Description | Example | +## |---------------------------------|--------------------------------------|-----------------------------------------------------------------------------------------------------------|-------------------------------------------------------| +## | serviceConnection | '$(serviceConnection)' | The service connection that connects to Azure. | 'demo-internal' | +## | poolName | '$(poolName)' | You can provide either a [poolname] or [vmImage] to run the job on. | 'Custom Deployment Pool' | +## | vmImage | '$(vmImage)' | You can provide either a [poolname] or [vmImage] to run the job on. | 'ubuntu20.04' | +## | defaultJobTimeoutInMinutes | 120 | The timeout for the job in this pipeline. | 120 | +## | customParameterFileTokens | '' | | | +## | modulePath | '$(modulePath)' | The path to the module to deploy. | 'c:/KeyVault' | +## | resourceGroupName | '$(resourceGroupName)' | The resourcegroup to deploy into. Required only for Resource-Group-Level deployments. | 'validation-rg' | +## | subscriptionId | '$(ARM_SUBSCRIPTION_ID)' | The id of the subscription to deploy into when using a Management group service connection. | 'aed7c000-6387-412e-bed0-24dfddf4bbc6' | +## | managementGroupId | '$(ARM_MGMTGROUP_ID)' | The id of the management group to deploy into. Required only for Management-Group-Level deployments. | '6ycc9620-cb01-454f-9ebc-fc6b1df48d64' | +## | azurePowerShellVersion | '$(azurePowerShellVersion)' | Used for configuring the Azure PowerShellModules Version, one of the example values. | 'latestVersion' or 'OtherVersion' | +## | preferredAzurePowerShellVersion | '$(preferredAzurePowerShellVersion)' | Used for configuring the Azure PowerShellModules Version, either an empty string or the specific version. | '4.4.0' | +## |============================================================================================================================================================================================================================================| +## +##---------------------------------------------## + +parameters: + # Pipeline-related parameters + serviceConnection: '$(serviceConnection)' + poolName: '$(poolName)' + vmImage: '$(vmImage)' + defaultJobTimeoutInMinutes: 120 + # Logic-related parameters + customParameterFileTokens: '' + modulePath: '$(modulePath)' + resourceGroupName: '$(resourceGroupName)' + subscriptionId: '$(ARM_SUBSCRIPTION_ID)' + managementGroupId: '$(ARM_MGMTGROUP_ID)' + # Azure PowerShell Version parameters + azurePowerShellVersion: '$(azurePowerShellVersion)' + preferredAzurePowerShellVersion: '$(preferredAzurePowerShellVersion)' + +##---------------------------------------------## +## TEMPLATE LOGIC ## +##---------------------------------------------## +jobs: + - template: /.azuredevops/pipelineTemplates/jobs.getModuleTestFiles.yml + - job: deploy + displayName: 'Run PSRule on ' # Auto-populated + timeoutInMinutes: ${{ parameters.defaultJobTimeoutInMinutes }} + pool: + ${{ if ne(parameters.vmImage, '') }}: + vmImage: ${{ parameters.vmImage }} + ${{ if ne(parameters.poolName, '') }}: + name: ${{ parameters.poolName }} + dependsOn: + - getModuleTestFiles + strategy: + matrix: $[ dependencies.getModuleTestFiles.outputs['getModuleTestFilesTask.moduleTests'] ] + ##---------------------------------------------## + ## TEMPLATE LOGIC ## + ##---------------------------------------------## + steps: + # [Agent] Prepare environment + #---------------------------- + - task: PowerShell@2 + displayName: 'Setup agent for deployment' + inputs: + targetType: inline + pwsh: true + script: | + # Load used functions + . (Join-Path '$(System.DefaultWorkingDirectory)' 'utilities' 'pipelines' 'sharedScripts' 'Set-EnvironmentOnAgent.ps1') + + # Define PS modules to install on the runner + $Modules = @( + @{ Name = 'Az.Accounts' }, + @{ Name = 'Az.Resources' }, + @{ Name = 'powershell-yaml'; Version = '0.4.2'} + ) + + # Additional PS modules need to be installed for the removal step in case it is enabled + if ('${{ parameters.removeDeployment}}' -eq 'true') { + $Modules += @( + @{ Name = 'Az.CognitiveServices' }, + @{ Name = 'Az.Compute' }, + @{ Name = 'Az.KeyVault' }, + @{ Name = 'Az.Monitor' }, + @{ Name = 'Az.OperationalInsights' }, + @{ Name = 'Az.RecoveryServices' } + ) + } + + # Set agent up + Set-EnvironmentOnAgent -PSModules $Modules + + # [Agent] Replace tokens + #----------------------- + - task: AzurePowerShell@5 + displayName: 'Replace tokens in template file via connection [${{ parameters.serviceConnection }}]' + inputs: + azureSubscription: ${{ parameters.serviceConnection }} + azurePowerShellVersion: 'latestVersion' + preferredAzurePowerShellVersion: '' + ScriptType: InlineScript + pwsh: true + inline: | + # Load used functions + . (Join-Path '$(System.DefaultWorkingDirectory)' 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # Get Service Principal Object ID + $context = Get-AzContext + $servicePrincipalAppId = $context.Account.Id + $servicePrincipal = Get-AzADServicePrincipal -ApplicationId $servicePrincipalAppId + $servicePrincipalObjectId = $servicePrincipal.Id + + # Get target files + $moduleTestFilePath = Join-Path '$(System.DefaultWorkingDirectory)' '$(modulePath)' '$(moduleTestFilePath)' + $targetFileList = @($moduleTestFilePath) + + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = $targetFileList + Tokens = @{} + TokenPrefix = '$(tokenPrefix)' + TokenSuffix = '$(tokenSuffix)' + } + + # Add enforced tokens + $ConvertTokensInputs.Tokens += @{ + resourceGroupName = '${{ parameters.resourceGroupName }}' + subscriptionId = '${{ parameters.subscriptionId }}' + managementGroupId = '${{ parameters.managementGroupId }}' + tenantId = '$(ARM_TENANT_ID)' + deploymentSpId = $servicePrincipalObjectId + } + + # Add local (source control) tokens + $tokenMap = @{} + foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + } + Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + $ConvertTokensInputs.Tokens += $tokenMap + + # Swap 'namePrefix' token if empty and provided as a Azure DevOps variable + if([String]::IsNullOrEmpty($ConvertTokensInputs.Tokens['namePrefix'])){ + Write-Verbose 'Using [namePrefix] token from Azure DevOps Variable Groups' -Verbose + $ConvertTokensInputs.Tokens['namePrefix'] = "$(TOKEN_NAMEPREFIX)" + } + + # Add custom tokens (passed in via the pipeline) + if(-not [String]::IsNullOrEmpty('${{ parameters.customParameterFileTokens }}')) { + $customTokens = '${{ parameters.customParameterFileTokens }}' | ConvertFrom-Json -AsHashTable + Write-Verbose ('Using custom parameter file tokens [{0}]' -f ($customTokens.Keys -join ', ')) -Verbose + $ConvertTokensInputs.Tokens += $customTokens + } + + Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Token Replacement Functionality [For Module] + $null = Convert-TokensInFileList @ConvertTokensInputs + + # Get target files for modules dependencies + $DependencyParameterFilePaths = [System.Collections.ArrayList]@() + $DependencyParameterFolders = Get-ChildItem -Path (Join-Path '$(System.DefaultWorkingDirectory)' 'utilities' 'pipelines' 'dependencies') -Recurse -Filter 'parameters' -Directory + foreach ($FolderPath in $DependencyParameterFolders.FullName) { + $DependencyParameterFilePaths += Get-ChildItem -Path $FolderPath -Recurse -Filter '*.json' + } + $ConvertTokensInputs.FilePathList = $DependencyParameterFilePaths + + # Invoke Token Replacement Functionality [For Dependencies] + $null = Convert-TokensInFileList @ConvertTokensInputs + + # [Validation] task(s) + #--------------------- + # Analyze Azure resources using PSRule for Azure + - task: ps-rule-assert@2 + displayName: Analyze Azure template files + inputs: + modules: 'PSRule.Rules.Azure' + inputPath: '$(System.DefaultWorkingDirectory)$(modulePath)/$(moduleTestFilePath)' + outputFormat: NUnit3 # Save results to an NUnit report. + outputPath: '$(System.DefaultWorkingDirectory)$(modulePath)/$(moduleTestFilePath)-output.xml' # Write NUnit report to '$(System.DefaultWorkingDirectory)$(modulePath)/$(moduleTestFilePath)-output.xml'. + + # Publish NUnit report as test results + - task: PublishTestResults@2 + displayName: 'Publish PSRule results' + inputs: + testRunTitle: 'PSRule' # The title to use for the test run. + testRunner: NUnit # Import report using the NUnit format. + testResultsFiles: '$(System.DefaultWorkingDirectory)$(modulePath)/$(moduleTestFilePath)-output.xml' # The previously saved NUnit report. + condition: succeededOrFailed() diff --git a/.azuredevops/pipelineTemplates/stages.module.yml b/.azuredevops/pipelineTemplates/stages.module.yml index 647517a17e..fd8278776e 100644 --- a/.azuredevops/pipelineTemplates/stages.module.yml +++ b/.azuredevops/pipelineTemplates/stages.module.yml @@ -10,8 +10,20 @@ stages: jobs: - template: /.azuredevops/pipelineTemplates/jobs.validateModulePester.yml + - stage: PSRuleValidation + dependsOn: [] + displayName: PSRule validation + jobs: + - template: /.azuredevops/pipelineTemplates/jobs.validateModulePSRule.yml + - stage: deployment displayName: Deployment validation + dependsOn: + - validation + - PSRuleValidation + condition: + | # Deployment validation will not be blocked by the failing PSRule validation stage (for the time being). <- Remove the condition block to change behavior + eq(dependencies.validation.result, 'Succeeded') jobs: - template: /.azuredevops/pipelineTemplates/jobs.validateModuleDeployment.yml parameters: From bf996415ff0a69c483198873c6302e9475e25d9f Mon Sep 17 00:00:00 2001 From: Elena Batanero <46710322+elbatane@users.noreply.github.com> Date: Fri, 23 Sep 2022 14:11:20 +0200 Subject: [PATCH 067/133] [Hackathon] Refined PSRule results, uncommented Deploy and Publish jobs and added condition to run deployment if psrule fails (#2123) * Uncommented deploy and publish and added if * Uncommented deploy and publish and added if into rg and vnet workflows * removed vnet-ps-rule.yaml * Updated Set-PSRuleOutput.ps1 Co-authored-by: Elena Batanero Garcia --- .../templates/validateModulePSRule/action.yml | 1 - .github/workflows/ms.keyvault.vaults.yml | 125 +++++++++--------- .../workflows/ms.network.virtualnetworks.yml | 125 +++++++++--------- .../workflows/ms.resources.resourcegroups.yml | 125 +++++++++--------- .../virtualNetworks/.test/vnet-ps-rule.yaml | 67 ---------- .../PSRuleValidation/Set-PSRuleOutput.ps1 | 9 +- 6 files changed, 194 insertions(+), 258 deletions(-) delete mode 100644 modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml diff --git a/.github/actions/templates/validateModulePSRule/action.yml b/.github/actions/templates/validateModulePSRule/action.yml index 2696c0b87c..9f25acea98 100644 --- a/.github/actions/templates/validateModulePSRule/action.yml +++ b/.github/actions/templates/validateModulePSRule/action.yml @@ -98,7 +98,6 @@ runs: inputPath: '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}' outputFormat: Csv outputPath: '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}-output.csv' - # option: '${{ env.modulePath }}/.test/vnet-ps-rule.yaml' - name: 'Set PSRule Output' if: always() diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index e7a5b9e66b..ba77f330c1 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -104,66 +104,67 @@ jobs: subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - # ############################# - # # Deployment validation # - # ############################# - # job_module_deploy_validation: - # runs-on: ubuntu-20.04 - # name: 'Deployment validation' - # needs: - # - job_initialize_pipeline - # - job_module_pester_validation - # - job_psrule_test - # strategy: - # fail-fast: false - # matrix: - # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - # steps: - # - name: 'Checkout' - # uses: actions/checkout@v2 - # with: - # fetch-depth: 0 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - # uses: ./.github/actions/templates/validateModuleDeployment - # with: - # templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # location: '${{ env.location }}' - # resourceGroupName: '${{ env.resourceGroupName }}' - # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + ############################# + # Deployment validation # + ############################# + job_module_deploy_validation: + runs-on: ubuntu-20.04 + if: ${{ always() && (needs.job_module_pester_validation.result == 'success') }} # Deployment validation will not be blocked by the failing PSRule validation stage (for the time being). <- Remove the condition block to change behavior + name: 'Deployment validation' + needs: + - job_initialize_pipeline + - job_module_pester_validation + - job_psrule_test + strategy: + fail-fast: false + matrix: + moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' + uses: ./.github/actions/templates/validateModuleDeployment + with: + templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + location: '${{ env.location }}' + resourceGroupName: '${{ env.resourceGroupName }}' + subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - # ################## - # # Publishing # - # ################## - # job_publish_module: - # name: 'Publishing' - # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - # runs-on: ubuntu-20.04 - # needs: - # - job_module_deploy_validation - # steps: - # - name: 'Checkout' - # uses: actions/checkout@v2 - # with: - # fetch-depth: 0 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Publishing' - # uses: ./.github/actions/templates/publishModule - # with: - # templateFilePath: '${{ env.modulePath }}/deploy.bicep' - # templateSpecsRGName: '${{ env.templateSpecsRGName }}' - # templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - # templateSpecsDescription: '${{ env.templateSpecsDescription }}' - # templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - # bicepRegistryName: '${{ env.bicepRegistryName }}' - # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' + ################## + # Publishing # + ################## + job_publish_module: + name: 'Publishing' + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + runs-on: ubuntu-20.04 + needs: + - job_module_deploy_validation + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Publishing' + uses: ./.github/actions/templates/publishModule + with: + templateFilePath: '${{ env.modulePath }}/deploy.bicep' + templateSpecsRGName: '${{ env.templateSpecsRGName }}' + templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + templateSpecsDescription: '${{ env.templateSpecsDescription }}' + templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + bicepRegistryName: '${{ env.bicepRegistryName }}' + bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index 1ae28606d7..182f2500a2 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -104,66 +104,67 @@ jobs: subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - # ############################# - # # Deployment validation # - # ############################# - # job_module_deploy_validation: - # runs-on: ubuntu-20.04 - # name: 'Deployment validation' - # needs: - # - job_initialize_pipeline - # - job_module_pester_validation - # - job_psrule_test - # strategy: - # fail-fast: false - # matrix: - # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - # steps: - # - name: 'Checkout' - # uses: actions/checkout@v2 - # with: - # fetch-depth: 0 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - # uses: ./.github/actions/templates/validateModuleDeployment - # with: - # templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # location: '${{ env.location }}' - # resourceGroupName: '${{ env.resourceGroupName }}' - # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + ############################# + # Deployment validation # + ############################# + job_module_deploy_validation: + runs-on: ubuntu-20.04 + if: ${{ always() && (needs.job_module_pester_validation.result == 'success') }} # Deployment validation will not be blocked by the failing PSRule validation stage (for the time being). <- Remove the condition block to change behavior + name: 'Deployment validation' + needs: + - job_initialize_pipeline + - job_module_pester_validation + - job_psrule_test + strategy: + fail-fast: false + matrix: + moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' + uses: ./.github/actions/templates/validateModuleDeployment + with: + templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + location: '${{ env.location }}' + resourceGroupName: '${{ env.resourceGroupName }}' + subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - # ################## - # # Publishing # - # ################## - # job_publish_module: - # name: 'Publishing' - # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - # runs-on: ubuntu-20.04 - # needs: - # - job_module_deploy_validation - # steps: - # - name: 'Checkout' - # uses: actions/checkout@v2 - # with: - # fetch-depth: 0 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Publishing' - # uses: ./.github/actions/templates/publishModule - # with: - # templateFilePath: '${{ env.modulePath }}/deploy.bicep' - # templateSpecsRGName: '${{ env.templateSpecsRGName }}' - # templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - # templateSpecsDescription: '${{ env.templateSpecsDescription }}' - # templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - # bicepRegistryName: '${{ env.bicepRegistryName }}' - # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' + ################## + # Publishing # + ################## + job_publish_module: + name: 'Publishing' + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + runs-on: ubuntu-20.04 + needs: + - job_module_deploy_validation + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Publishing' + uses: ./.github/actions/templates/publishModule + with: + templateFilePath: '${{ env.modulePath }}/deploy.bicep' + templateSpecsRGName: '${{ env.templateSpecsRGName }}' + templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + templateSpecsDescription: '${{ env.templateSpecsDescription }}' + templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + bicepRegistryName: '${{ env.bicepRegistryName }}' + bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 6dd3f78b7a..c341a8d9bc 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -104,66 +104,67 @@ jobs: subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - # ############################# - # # Deployment validation # - # ############################# - # job_module_deploy_validation: - # runs-on: ubuntu-20.04 - # name: 'Deployment validation' - # needs: - # - job_initialize_pipeline - # - job_module_pester_validation - # - job_psrule_test - # strategy: - # fail-fast: false - # matrix: - # moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - # steps: - # - name: 'Checkout' - # uses: actions/checkout@v2 - # with: - # fetch-depth: 0 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' - # uses: ./.github/actions/templates/validateModuleDeployment - # with: - # templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' - # location: '${{ env.location }}' - # resourceGroupName: '${{ env.resourceGroupName }}' - # subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - # managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - # removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + ############################# + # Deployment validation # + ############################# + job_module_deploy_validation: + runs-on: ubuntu-20.04 + if: ${{ always() && (needs.job_module_pester_validation.result == 'success') }} # Deployment validation will not be blocked by the failing PSRule validation stage (for the time being). <- Remove the condition block to change behavior + name: 'Deployment validation' + needs: + - job_initialize_pipeline + - job_module_pester_validation + - job_psrule_test + strategy: + fail-fast: false + matrix: + moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Using test file [${{ matrix.moduleTestFilePaths }}]' + uses: ./.github/actions/templates/validateModuleDeployment + with: + templateFilePath: '${{ env.modulePath }}/${{ matrix.moduleTestFilePaths }}' + location: '${{ env.location }}' + resourceGroupName: '${{ env.resourceGroupName }}' + subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' - # ################## - # # Publishing # - # ################## - # job_publish_module: - # name: 'Publishing' - # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' - # runs-on: ubuntu-20.04 - # needs: - # - job_module_deploy_validation - # steps: - # - name: 'Checkout' - # uses: actions/checkout@v2 - # with: - # fetch-depth: 0 - # - name: Set environment variables - # uses: ./.github/actions/templates/setEnvironmentVariables - # with: - # variablesPath: ${{ env.variablesPath }} - # - name: 'Publishing' - # uses: ./.github/actions/templates/publishModule - # with: - # templateFilePath: '${{ env.modulePath }}/deploy.bicep' - # templateSpecsRGName: '${{ env.templateSpecsRGName }}' - # templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' - # templateSpecsDescription: '${{ env.templateSpecsDescription }}' - # templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' - # bicepRegistryName: '${{ env.bicepRegistryName }}' - # bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' - # bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' - # bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' + ################## + # Publishing # + ################## + job_publish_module: + name: 'Publishing' + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + runs-on: ubuntu-20.04 + needs: + - job_module_deploy_validation + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Publishing' + uses: ./.github/actions/templates/publishModule + with: + templateFilePath: '${{ env.modulePath }}/deploy.bicep' + templateSpecsRGName: '${{ env.templateSpecsRGName }}' + templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + templateSpecsDescription: '${{ env.templateSpecsDescription }}' + templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + bicepRegistryName: '${{ env.bicepRegistryName }}' + bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' diff --git a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml b/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml deleted file mode 100644 index 5b96b4abf4..0000000000 --- a/modules/Microsoft.Network/virtualNetworks/.test/vnet-ps-rule.yaml +++ /dev/null @@ -1,67 +0,0 @@ -# -# PSRule for Azure configuration -# - -# Please see the documentation for all configuration options: -# https://aka.ms/ps-rule/options -# https://aka.ms/ps-rule-azure/options - -# Configure binding for local rules. -binding: - preferTargetInfo: true - targetType: - - type - - resourceType - -# Require minimum versions of modules. -requires: - PSRule: '@pre >=2.4.0' - PSRule.Rules.Azure: '@pre >=1.19.2' - -# Use PSRule for Azure. -include: - module: - - PSRule.Rules.Azure - -execution: - suppressedRuleWarning: false - -output: - culture: - - 'en-US' - #outcome: 'Fail' - #as: 'Summary' - -input: - pathIgnore: - # Ignore other files in the repository. - - '.vscode/' - - '.github/' - - '*.md' - - # Exclude modules but not tests. - - 'modules/**/*.bicep' - - '!modules/**/*.test.bicep' - - 'modules/**/*version.json' - -configuration: - # Enable automatic expansion of Azure parameter files. - AZURE_PARAMETER_FILE_EXPANSION: false - - # Enable automatic expansion of Azure Bicep source files. - AZURE_BICEP_FILE_EXPANSION: true - - # Configures the number of seconds to wait for build Bicep files. - AZURE_BICEP_FILE_EXPANSION_TIMEOUT: 10 - -rule: - # Enable custom rules that don't exist in the baseline - includeLocal: false - exclude: - # Ignore the following rules for all resources - - Azure.Resource.UseTags -# Suppression ignores rules for a specific Azure resource by name. -#suppression: -# Azure.Identity.UserAssignedName: -# - 'dep*' - diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 0a8b0a7aa4..fc24b5c17b 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -41,10 +41,11 @@ #Create Failing table $failContent = [System.Collections.ArrayList]@( - '# Failed Rules', '', '
', - 'Failed Rules', + 'List of Failed Rules', + '', + '## Failed Rules', '', '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' @@ -82,11 +83,11 @@ if ($passedRules.Count -gt 0) { $passContent = [System.Collections.ArrayList]@( - '# Passed Rules', '', '
', - 'Passed Rules', + 'List of Passed Rules', '', + '## Passed Rules', '', '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' From 89f3a24d6df5ed89462eeede0fcc38660d952aeb Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 19:16:15 +0200 Subject: [PATCH 068/133] yay --- .../PSRuleValidation/Set-PSRuleOutput.ps1 | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 9c5f5d7393..fd3f08703c 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -18,28 +18,37 @@ $passedRules += $results | Where-Object { $_.Outcome -EQ 'Pass' } $failedRules += $results | Where-Object { $_.Outcome -EQ 'Fail' } - - #Create Summary table - - $headerTable = [System.Collections.ArrayList]@( - '# Output Summary ', - '', - '| Total No. of Processed Rules| Passed Rules :white_check_mark: | Failed Rules :x: |', - '| :-- | :-- | :-- |' + #Create header and first output + $header = [System.Collections.ArrayList]@( + '# PSRule Summary ', + '' ) + Out-File -FilePath $outputFilePath -NoClobber -InputObject $header - $headerTable += ('| {0} | {1} | {2} |' -f $results.Count, $passedRules.Count , $failedRules.Count) - $headerTable += [System.Collections.ArrayList]@( - '') - - # Create markdown file with header table - Out-File -FilePath $outputFilePath -NoClobber -InputObject $headerTable - + if ($failedRules.Count -gt 0) { + # Create header content + $headerContent = [System.Collections.ArrayList]@( + 'YAY!' + ) + # Append header content + Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $headerContent + } if ($failedRules.Count -gt 0) { + # Create header table + $headerTable = [System.Collections.ArrayList]@( + '| Total No. of Processed Rules| Passed Rules :white_check_mark: | Failed Rules :x: |', + '| :-- | :-- | :-- |' + ) + $headerTable += ('| {0} | {1} | {2} |' -f $results.Count, $passedRules.Count , $failedRules.Count) + $headerTable += [System.Collections.ArrayList]@( + '' + ) - #Create Failing table + # Append header table + Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $headerTable + # Create Failing table $failContent = [System.Collections.ArrayList]@( '', '
', From e3324dd70ed8a4f4acdc8a30caf1b8ba02007453 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 19:24:33 +0200 Subject: [PATCH 069/133] token secret --- .github/workflows/linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 7d0e53bcf3..8522d5ad6b 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -5,6 +5,7 @@ on: [pull_request] env: variablesPath: 'settings.yml' modulesPath: 'modules' + TOKEN_NAMEPREFIX: '${{ secrets.TOKEN_NAMEPREFIX }}' jobs: build: @@ -84,7 +85,6 @@ jobs: $moduleTestFiles += Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.test.bicep -Recurse -Force -Name # | ForEach-Object {$_.root} | Join-Path -ChildPath "Subdir" - # Construct Token Function Input $ConvertTokensInputs = @{ FilePathList = $moduleTestFiles From 084b06ab0fd19e6b28b402b1b6877ff8e31285b8 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 19:32:28 +0200 Subject: [PATCH 070/133] skip passed --- .github/workflows/linter.yml | 5 +++-- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 8522d5ad6b..439709aff3 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -122,8 +122,9 @@ jobs: # Populate parameter input $ParameterInput = @{ - inputFilePath = '${{ env.modulesPath }}/PSRule-output.csv' - outputFilePath = '${{ env.modulesPath }}/PSRule-output.md' + inputFilePath = '${{ env.modulesPath }}/PSRule-output.csv' + outputFilePath = '${{ env.modulesPath }}/PSRule-output.md' + skipPassedRulesReport = $true } # Invoke function diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index fd3f08703c..c2bcbeebb7 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -5,11 +5,13 @@ [String] $inputFilePath, [Parameter(Mandatory = $false)] - [string] $outputFilePath = './output.md' + [string] $outputFilePath = './output.md', + + [Parameter(Mandatory = $false)] + [switch] $skipPassedRulesReport ) # Import CSV output and filter by results - $results = Import-Csv -Path $inputFilePath $passedRules = @() @@ -89,7 +91,7 @@ } # Create Passing table - if ($passedRules.Count -gt 0) { + if (($passedRules.Count -gt 0) -and -not $skipPassedRulesReport) { $passContent = [System.Collections.ArrayList]@( '', From a53defe256ce3d55e1433aee42896aae25ed4eff Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 19:36:09 +0200 Subject: [PATCH 071/133] rocket --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index c2bcbeebb7..0ebd95b7e6 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -28,9 +28,10 @@ Out-File -FilePath $outputFilePath -NoClobber -InputObject $header if ($failedRules.Count -gt 0) { + # if ($failedRules.Count -eq 0) { # Create header content $headerContent = [System.Collections.ArrayList]@( - 'YAY!' + 'All $($results.Count) rules passed, YAY! :rocket:' ) # Append header content Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $headerContent From 658b2c49c03e641ac2feb9248809dcb410827896 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 19:44:01 +0200 Subject: [PATCH 072/133] noFailuresContent --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 0ebd95b7e6..7e4d24a4fb 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -30,11 +30,9 @@ if ($failedRules.Count -gt 0) { # if ($failedRules.Count -eq 0) { # Create header content - $headerContent = [System.Collections.ArrayList]@( - 'All $($results.Count) rules passed, YAY! :rocket:' - ) + $noFailuresContent = ('All [{0}] rules passed, YAY! :rocket:' -f $results.Count) # Append header content - Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $headerContent + Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $noFailuresContent } if ($failedRules.Count -gt 0) { @@ -132,7 +130,6 @@ ) #Append markdown with passed rules table Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $passContent - } } From de2c95bacf285f744a4a394ab048688633beff5d Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 19:50:31 +0200 Subject: [PATCH 073/133] yay Content --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 7e4d24a4fb..6b6808360e 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -30,7 +30,7 @@ if ($failedRules.Count -gt 0) { # if ($failedRules.Count -eq 0) { # Create header content - $noFailuresContent = ('All [{0}] rules passed, YAY! :rocket:' -f $results.Count) + $noFailuresContent = ('## All {0} rules passed, YAY! :rocket:' -f $results.Count) # Append header content Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $noFailuresContent } From a3c96287ba4ec110cf485af584bbbd56e8e4b14c Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 19:50:54 +0200 Subject: [PATCH 074/133] yay Content if the case --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 6b6808360e..9456f38def 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -27,8 +27,7 @@ ) Out-File -FilePath $outputFilePath -NoClobber -InputObject $header - if ($failedRules.Count -gt 0) { - # if ($failedRules.Count -eq 0) { + if ($failedRules.Count -eq 0) { # Create header content $noFailuresContent = ('## All {0} rules passed, YAY! :rocket:' -f $results.Count) # Append header content From 6dd8b88851429cef88ea04f90317aeebd1429309 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 19:51:31 +0200 Subject: [PATCH 075/133] double rocket --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 9456f38def..19e0ed4735 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -29,7 +29,7 @@ if ($failedRules.Count -eq 0) { # Create header content - $noFailuresContent = ('## All {0} rules passed, YAY! :rocket:' -f $results.Count) + $noFailuresContent = ('## :rocket: All {0} rules passed, YAY! :rocket:' -f $results.Count) # Append header content Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $noFailuresContent } From cadcdb1cd04d80d7bbcaec9c9c872b2e3e74cad3 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 20:01:17 +0200 Subject: [PATCH 076/133] cleanup --- .../PSRuleValidation/Set-PSRuleOutput.ps1 | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 19e0ed4735..660d9002af 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -20,7 +20,8 @@ $passedRules += $results | Where-Object { $_.Outcome -EQ 'Pass' } $failedRules += $results | Where-Object { $_.Outcome -EQ 'Fail' } - #Create header and first output + # Set content + # Header $header = [System.Collections.ArrayList]@( '# PSRule Summary ', '' @@ -28,14 +29,12 @@ Out-File -FilePath $outputFilePath -NoClobber -InputObject $header if ($failedRules.Count -eq 0) { - # Create header content + # No failure content $noFailuresContent = ('## :rocket: All {0} rules passed, YAY! :rocket:' -f $results.Count) - # Append header content Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $noFailuresContent - } - - if ($failedRules.Count -gt 0) { - # Create header table + } else { + # Failure content + # Header table $headerTable = [System.Collections.ArrayList]@( '| Total No. of Processed Rules| Passed Rules :white_check_mark: | Failed Rules :x: |', '| :-- | :-- | :-- |' @@ -44,11 +43,9 @@ $headerTable += [System.Collections.ArrayList]@( '' ) - - # Append header table Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $headerTable - # Create Failing table + # Failed rules $failContent = [System.Collections.ArrayList]@( '', '
', @@ -59,7 +56,6 @@ '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' ) - foreach ($content in $failedRules ) { # Shorten the target name for deployment resoure type if ($content.TargetType -eq 'Microsoft.Resources/deployments') { @@ -77,20 +73,18 @@ $resourceLink = $content.RuleName } $failContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) - } $failContent += [System.Collections.ArrayList]@( '', '
', '' ) - #Append markdown with failed rules table + # Append markdown with failed rules table Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $failContent } - # Create Passing table + # Passed rules if (($passedRules.Count -gt 0) -and -not $skipPassedRulesReport) { - $passContent = [System.Collections.ArrayList]@( '', '
', @@ -101,7 +95,6 @@ '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' ) - foreach ($content in $passedRules ) { # Shorten the target name for deployment resoure type if ($content.TargetType -eq 'Microsoft.Resources/deployments') { @@ -127,10 +120,6 @@ '
', '' ) - #Append markdown with passed rules table Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $passContent } } - - - From 085750d266257d9935d3f87240c9ea2a7e28192e Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 20:07:04 +0200 Subject: [PATCH 077/133] header content --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 660d9002af..6cec71daa2 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -38,11 +38,13 @@ $headerTable = [System.Collections.ArrayList]@( '| Total No. of Processed Rules| Passed Rules :white_check_mark: | Failed Rules :x: |', '| :-- | :-- | :-- |' - ) - $headerTable += ('| {0} | {1} | {2} |' -f $results.Count, $passedRules.Count , $failedRules.Count) - $headerTable += [System.Collections.ArrayList]@( + ('| {0} | {1} | {2} |' -f $results.Count, $passedRules.Count , $failedRules.Count), '' ) + # $headerTable += ('| {0} | {1} | {2} |' -f $results.Count, $passedRules.Count , $failedRules.Count) + # $headerTable += [System.Collections.ArrayList]@( + # '' + # ) Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $headerTable # Failed rules From 5bbc4e290a84c022a602623554c141d190247da9 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 20:08:21 +0200 Subject: [PATCH 078/133] header --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 6cec71daa2..022832eaaa 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -23,7 +23,7 @@ # Set content # Header $header = [System.Collections.ArrayList]@( - '# PSRule Summary ', + '# PSRule pre-flight validation summary ', '' ) Out-File -FilePath $outputFilePath -NoClobber -InputObject $header From f46300708e7fa458c79cf8c65922542d9f326899 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 20:11:28 +0200 Subject: [PATCH 079/133] cleanup --- .../pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 022832eaaa..a8b2dbed1a 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -41,13 +41,9 @@ ('| {0} | {1} | {2} |' -f $results.Count, $passedRules.Count , $failedRules.Count), '' ) - # $headerTable += ('| {0} | {1} | {2} |' -f $results.Count, $passedRules.Count , $failedRules.Count) - # $headerTable += [System.Collections.ArrayList]@( - # '' - # ) Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $headerTable - # Failed rules + # List of failed rules $failContent = [System.Collections.ArrayList]@( '', '
', @@ -85,7 +81,7 @@ Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $failContent } - # Passed rules + # List of passed rules if (($passedRules.Count -gt 0) -and -not $skipPassedRulesReport) { $passContent = [System.Collections.ArrayList]@( '', @@ -122,6 +118,7 @@ '
', '' ) + # Append markdown with passed rules table Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $passContent } } From e34183664d38a8155c27c9b571c8e780e3a79247 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 20:15:38 +0200 Subject: [PATCH 080/133] cleanerup --- .../PSRuleValidation/Set-PSRuleOutput.ps1 | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index a8b2dbed1a..4771aa17e9 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -11,7 +11,10 @@ [switch] $skipPassedRulesReport ) - # Import CSV output and filter by results + ########################################### + # Import CSV output and filter by results # + ########################################### + $results = Import-Csv -Path $inputFilePath $passedRules = @() @@ -20,7 +23,10 @@ $passedRules += $results | Where-Object { $_.Outcome -EQ 'Pass' } $failedRules += $results | Where-Object { $_.Outcome -EQ 'Fail' } - # Set content + ###################### + # Set output content # + ###################### + # Header $header = [System.Collections.ArrayList]@( '# PSRule pre-flight validation summary ', @@ -34,7 +40,8 @@ Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $noFailuresContent } else { # Failure content - # Header table + + ## Header table $headerTable = [System.Collections.ArrayList]@( '| Total No. of Processed Rules| Passed Rules :white_check_mark: | Failed Rules :x: |', '| :-- | :-- | :-- |' @@ -43,7 +50,7 @@ ) Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $headerTable - # List of failed rules + ## List of failed rules $failContent = [System.Collections.ArrayList]@( '', '
', @@ -77,12 +84,12 @@ '
', '' ) - # Append markdown with failed rules table + # Append to output Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $failContent } - # List of passed rules if (($passedRules.Count -gt 0) -and -not $skipPassedRulesReport) { + # List of passed rules $passContent = [System.Collections.ArrayList]@( '', '
', @@ -109,7 +116,6 @@ Write-Warning "Unable to build url for $content.RuleName" $resourceLink = $content.RuleName } - $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) } @@ -118,7 +124,7 @@ '
', '' ) - # Append markdown with passed rules table + # Append to output Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $passContent } } From 86aa6b9e662ca4c7c2b83923a34f8370071f7585 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Thu, 29 Sep 2022 20:17:53 +0200 Subject: [PATCH 081/133] tbd --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 4771aa17e9..b3896d243c 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -27,7 +27,7 @@ # Set output content # ###################### - # Header + # Header //TBD: Remove? $header = [System.Collections.ArrayList]@( '# PSRule pre-flight validation summary ', '' From d07aae667546893505e40837f51224c9c2fa7b0d Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 4 Oct 2022 19:02:58 +0200 Subject: [PATCH 082/133] test on kv only --- .github/workflows/linter.yml | 5 ++--- .ps-rule/dep-suppress.Rule.yaml | 2 +- .ps-rule/min-suppress.Rule.yaml | 5 ++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 439709aff3..1e329468e5 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -100,13 +100,12 @@ jobs: Convert-TokensInFileList @ConvertTokensInputs -verbose Write-Output '::endgroup::' - - name: Run PSRule analysis uses: microsoft/ps-rule@v2.4.0 continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulesPath }}/' + inputPath: '${{ env.modulesPath }}/Microsoft.KeyVault/vaults' outputFormat: Csv outputPath: '${{ env.modulesPath }}/PSRule-output.csv' - name: 'Parse CSV content' @@ -115,7 +114,7 @@ jobs: azPSVersion: 'latest' inlineScript: | # Grouping task logs - Write-Output '::group::Replace tokens in template file' + Write-Output '::group::Parse CSV content' # Load used functions . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'PSRuleValidation' 'Set-PSRuleOutput.ps1') diff --git a/.ps-rule/dep-suppress.Rule.yaml b/.ps-rule/dep-suppress.Rule.yaml index 0eedcfb968..e5b1c0a1bd 100644 --- a/.ps-rule/dep-suppress.Rule.yaml +++ b/.ps-rule/dep-suppress.Rule.yaml @@ -3,7 +3,7 @@ apiVersion: github.com/microsoft/PSRule/v1 kind: SuppressionGroup metadata: - name: 'SuppressDependancy' + name: 'SuppressDependency' spec: if: name: '.' diff --git a/.ps-rule/min-suppress.Rule.yaml b/.ps-rule/min-suppress.Rule.yaml index 9c984ea8da..85b70eb2c0 100644 --- a/.ps-rule/min-suppress.Rule.yaml +++ b/.ps-rule/min-suppress.Rule.yaml @@ -3,7 +3,7 @@ apiVersion: github.com/microsoft/PSRule/v1 kind: SuppressionGroup metadata: - name: 'Suppressmin' + name: 'SuppressMin' spec: rule: - Azure.Resource.UseTags @@ -12,5 +12,4 @@ spec: name: '.' contains: - 'min' - - \ No newline at end of file + From 4121f34d1fd9627eb0313f78948ad3503652ec98 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 4 Oct 2022 19:19:46 +0200 Subject: [PATCH 083/133] test on kv only change --- modules/Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep b/modules/Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep index 99ecd3a4b4..d336874338 100644 --- a/modules/Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep +++ b/modules/Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep @@ -3,6 +3,7 @@ targetScope = 'subscription' // ========== // // Parameters // // ========== // + @description('Optional. The name of the resource group to deploy for testing purposes') @maxLength(90) param resourceGroupName string = 'ms.keyvault.vaults-${serviceShort}-rg' From 950c54280f1d047cbc6962a8c3ccdcb8186d4c57 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 4 Oct 2022 19:27:56 +0200 Subject: [PATCH 084/133] test on kv only change path --- .github/workflows/linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 1e329468e5..67a5c913cf 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -105,7 +105,7 @@ jobs: continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulesPath }}/Microsoft.KeyVault/vaults' + inputPath: '${{ env.modulesPath }}/Microsoft.KeyVault/vaults/' outputFormat: Csv outputPath: '${{ env.modulesPath }}/PSRule-output.csv' - name: 'Parse CSV content' From d204934f6c3376f11a1ea281fb4a3b995393227e Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 4 Oct 2022 19:32:08 +0200 Subject: [PATCH 085/133] library --- .github/workflows/linter.yml | 2 +- .../Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 67a5c913cf..86748f1fbe 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -105,7 +105,7 @@ jobs: continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulesPath }}/Microsoft.KeyVault/vaults/' + inputPath: '${{ env.modulesPath }}/' outputFormat: Csv outputPath: '${{ env.modulesPath }}/PSRule-output.csv' - name: 'Parse CSV content' diff --git a/modules/Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep b/modules/Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep index d336874338..99ecd3a4b4 100644 --- a/modules/Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep +++ b/modules/Microsoft.KeyVault/vaults/.test/common/deploy.test.bicep @@ -3,7 +3,6 @@ targetScope = 'subscription' // ========== // // Parameters // // ========== // - @description('Optional. The name of the resource group to deploy for testing purposes') @maxLength(90) param resourceGroupName string = 'ms.keyvault.vaults-${serviceShort}-rg' From 361905e372655a967ad88037bc593eb2fd6956cf Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 4 Oct 2022 19:39:36 +0200 Subject: [PATCH 086/133] comment --- .github/workflows/linter.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 86748f1fbe..40269f5db6 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -33,6 +33,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} FILTER_REGEX_EXCLUDE: '[module.tests.ps1|Get\-ModulesAsMarkdownTable.ps1|.*yml]' + # Discuss if running on PR to the whole repo (current implementation) or only on files changed psrule: name: PSRule runs-on: ubuntu-latest From ffb336388061656f385a31369b9de518d1942135 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 10 Oct 2022 17:07:47 +0200 Subject: [PATCH 087/133] suppress ms rg --- .ps-rule/dep-suppress.Rule.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.ps-rule/dep-suppress.Rule.yaml b/.ps-rule/dep-suppress.Rule.yaml index e5b1c0a1bd..16399d8be9 100644 --- a/.ps-rule/dep-suppress.Rule.yaml +++ b/.ps-rule/dep-suppress.Rule.yaml @@ -9,3 +9,4 @@ spec: name: '.' startsWith: - 'dep' + - 'ms.' From 6bca85c8c2ee2b1f20a75abe21a0323467ec5b90 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 10 Oct 2022 17:43:52 +0200 Subject: [PATCH 088/133] suppress privatelink --- .ps-rule/dep-suppress.Rule.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.ps-rule/dep-suppress.Rule.yaml b/.ps-rule/dep-suppress.Rule.yaml index 16399d8be9..9ace1e259a 100644 --- a/.ps-rule/dep-suppress.Rule.yaml +++ b/.ps-rule/dep-suppress.Rule.yaml @@ -10,3 +10,4 @@ spec: startsWith: - 'dep' - 'ms.' + - 'privatelink.'' From 4fabf3a9dd493dbe0a5e967dc817fda19cdb9875 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 12 Oct 2022 11:35:03 +0200 Subject: [PATCH 089/133] reduce scope --- .github/workflows/linter.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 40269f5db6..d61a327c4e 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -106,7 +106,8 @@ jobs: continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulesPath }}/' + # inputPath: '${{ env.modulesPath }}/' + inputPath: '${{ env.modulesPath }}/Microsoft.ApiManagement/service/' outputFormat: Csv outputPath: '${{ env.modulesPath }}/PSRule-output.csv' - name: 'Parse CSV content' From 9d874c0aaa4e22b8b1da809a365afcc314d7ae81 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 12 Oct 2022 11:38:44 +0200 Subject: [PATCH 090/133] reduce scope csv --- .github/workflows/linter.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index d61a327c4e..ea6f0a79c0 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -109,7 +109,8 @@ jobs: # inputPath: '${{ env.modulesPath }}/' inputPath: '${{ env.modulesPath }}/Microsoft.ApiManagement/service/' outputFormat: Csv - outputPath: '${{ env.modulesPath }}/PSRule-output.csv' + # outputPath: '${{ env.modulesPath }}/PSRule-output.csv' + outputPath: '${{ env.modulesPath }}/Microsoft.ApiManagement/service/PSRule-output.csv' - name: 'Parse CSV content' uses: azure/powershell@v1 with: From 8105f814c8441fc5395cba371451ed4dbd55e70c Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 12 Oct 2022 11:40:41 +0200 Subject: [PATCH 091/133] reduce scope csv kv --- .github/workflows/linter.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index ea6f0a79c0..fe06414637 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -107,10 +107,10 @@ jobs: with: modules: 'PSRule.Rules.Azure' # inputPath: '${{ env.modulesPath }}/' - inputPath: '${{ env.modulesPath }}/Microsoft.ApiManagement/service/' + inputPath: '${{ env.modulesPath }}/Microsoft.KeyVault/' outputFormat: Csv # outputPath: '${{ env.modulesPath }}/PSRule-output.csv' - outputPath: '${{ env.modulesPath }}/Microsoft.ApiManagement/service/PSRule-output.csv' + outputPath: '${{ env.modulesPath }}/Microsoft.KeyVault/PSRule-output.csv' - name: 'Parse CSV content' uses: azure/powershell@v1 with: From f9b826fe55e833a541bd01acaa19c8e131d3165e Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 12 Oct 2022 11:46:12 +0200 Subject: [PATCH 092/133] 242 --- .github/actions/templates/validateModulePSRule/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/templates/validateModulePSRule/action.yml b/.github/actions/templates/validateModulePSRule/action.yml index 9f25acea98..e4152d3cc1 100644 --- a/.github/actions/templates/validateModulePSRule/action.yml +++ b/.github/actions/templates/validateModulePSRule/action.yml @@ -91,7 +91,7 @@ runs: # Run analysis by using the PSRule GitHub action. - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.4.0 + uses: microsoft/ps-rule@v2.4.2 # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' From 69fa3791ac7676bde3b15efc282c230d42c7177e Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 12 Oct 2022 11:48:28 +0200 Subject: [PATCH 093/133] remove require --- ps-rule.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ps-rule.yaml b/ps-rule.yaml index e1fe3eeaa3..b6fed9ec9f 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -15,7 +15,7 @@ binding: # Require minimum versions of modules. requires: - PSRule: '@pre >=2.4.0' + # PSRule: '@pre >=2.4.0' PSRule.Rules.Azure: '@pre >=1.19.2' # Use PSRule for Azure. @@ -56,7 +56,7 @@ rule: exclude: # Ignore the following rules for all resources - Azure.KeyVault.PurgeProtect - + # Suppression ignores rules for a specific Azure resource by name. # suppression: # Azure.KeyVault.PurgeProtect: From c60eb483afc2902fada3a7937b342678dc0be0e2 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 12 Oct 2022 15:21:02 +0200 Subject: [PATCH 094/133] 250 --- .github/actions/templates/validateModulePSRule/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/templates/validateModulePSRule/action.yml b/.github/actions/templates/validateModulePSRule/action.yml index e4152d3cc1..14c4ad4d76 100644 --- a/.github/actions/templates/validateModulePSRule/action.yml +++ b/.github/actions/templates/validateModulePSRule/action.yml @@ -91,7 +91,7 @@ runs: # Run analysis by using the PSRule GitHub action. - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.4.2 + uses: microsoft/ps-rule@v2.5.0 # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' From ac2a1933eb26afd9106d2a1efdd94c785e5484c9 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 12 Oct 2022 15:30:59 +0200 Subject: [PATCH 095/133] 250 linter --- .github/actions/templates/validateModulePSRule/action.yml | 2 +- .github/workflows/linter.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/templates/validateModulePSRule/action.yml b/.github/actions/templates/validateModulePSRule/action.yml index 14c4ad4d76..9f25acea98 100644 --- a/.github/actions/templates/validateModulePSRule/action.yml +++ b/.github/actions/templates/validateModulePSRule/action.yml @@ -91,7 +91,7 @@ runs: # Run analysis by using the PSRule GitHub action. - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.5.0 + uses: microsoft/ps-rule@v2.4.0 # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index fe06414637..464519e7c8 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -102,7 +102,7 @@ jobs: Write-Output '::endgroup::' - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.4.0 + uses: microsoft/ps-rule@v2.5.0 continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' From 412687395c7c4b4e6f036bc12b7c941d33db67da Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 12 Oct 2022 15:31:43 +0200 Subject: [PATCH 096/133] 240 linter --- .github/workflows/linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 464519e7c8..fe06414637 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -102,7 +102,7 @@ jobs: Write-Output '::endgroup::' - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.5.0 + uses: microsoft/ps-rule@v2.4.0 continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' From fba2d2c12c9166a820364c83ad9e3a87b5299fe7 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 12 Oct 2022 15:43:41 +0200 Subject: [PATCH 097/133] requires 240 linter --- ps-rule.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/ps-rule.yaml b/ps-rule.yaml index b6fed9ec9f..1bd554fe34 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -15,6 +15,7 @@ binding: # Require minimum versions of modules. requires: + PSRule: '2.4.0' # PSRule: '@pre >=2.4.0' PSRule.Rules.Azure: '@pre >=1.19.2' From 3bbee13c2f20c201f96158aafa4481fe0716f255 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Wed, 12 Oct 2022 15:47:01 +0200 Subject: [PATCH 098/133] include 240 --- ps-rule.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ps-rule.yaml b/ps-rule.yaml index 1bd554fe34..40efd1043b 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -15,12 +15,14 @@ binding: # Require minimum versions of modules. requires: - PSRule: '2.4.0' + # PSRule: '2.5.0' # PSRule: '@pre >=2.4.0' PSRule.Rules.Azure: '@pre >=1.19.2' # Use PSRule for Azure. include: + versions: + - 2.4.0 module: - PSRule.Rules.Azure From a56cd66e7e5ee04ea5efaac484d11120816660d9 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 18 Oct 2022 16:16:07 +0200 Subject: [PATCH 099/133] EoF --- .ps-rule/dep-suppress.Rule.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ps-rule/dep-suppress.Rule.yaml b/.ps-rule/dep-suppress.Rule.yaml index 9ace1e259a..6dc96bce1e 100644 --- a/.ps-rule/dep-suppress.Rule.yaml +++ b/.ps-rule/dep-suppress.Rule.yaml @@ -10,4 +10,4 @@ spec: startsWith: - 'dep' - 'ms.' - - 'privatelink.'' + - 'privatelink.' From cc6968e853c1294e5dd6964b1be10e4db9a5b9e5 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 18 Oct 2022 16:22:36 +0200 Subject: [PATCH 100/133] update inputpath to all modules --- .github/workflows/linter.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index fe06414637..40269f5db6 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -106,11 +106,9 @@ jobs: continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project with: modules: 'PSRule.Rules.Azure' - # inputPath: '${{ env.modulesPath }}/' - inputPath: '${{ env.modulesPath }}/Microsoft.KeyVault/' + inputPath: '${{ env.modulesPath }}/' outputFormat: Csv - # outputPath: '${{ env.modulesPath }}/PSRule-output.csv' - outputPath: '${{ env.modulesPath }}/Microsoft.KeyVault/PSRule-output.csv' + outputPath: '${{ env.modulesPath }}/PSRule-output.csv' - name: 'Parse CSV content' uses: azure/powershell@v1 with: From aaeb0f7178a78d5aee3c5cb6e13c45fa626f9b65 Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Tue, 13 Dec 2022 18:50:51 +0100 Subject: [PATCH 101/133] [Hackaton] Update PSRule suppress rules (#2451) * linter * exclude parameters * pathIgnore * clean up token replacement * clean up token replacement further * resize token replacement * typo * modulePath * job name * no psrule * no psrule step * fix inputpath * comment out continue on error option * Align KV * kv no psrule * replace * no replace * replace 1 * indent * align vnet * align vnet and enable deployment * conflicts * suppressedRuleWarning * enable deployment * linter csv * path to ignore * execution * disable rg deployment * 2 jobs * inputpath * add init * variables * env variables * call function * md detail summary * new lines * details pass and fail * moduleTestFiles * moduleTest * join path * modulesFolderPath * modulesFolderPath collection * modulesFolderPath no filter * modulesFolderPath no file * force * cleanup * root * noroot * convert verbose * no pipe * yay * token secret * skip passed * rocket * noFailuresContent * yay Content * yay Content if the case * double rocket * cleanup * header content * header * cleanup * cleanerup * tbd * test on kv only * test on kv only change * test on kv only change path * library * comment * suppress ms rg * suppress privatelink * reduce scope * reduce scope csv * reduce scope csv kv * 242 * remove require * 250 * 250 linter * 240 linter * requires 240 linter * include 240 * EoF * update inputpath to all modules * clean linter * clean linter env * update yaml * add script synopsis * remove include version setting --- .ps-rule/dep-suppress.Rule.yaml | 4 +- .ps-rule/min-suppress.Rule.yaml | 5 +- ps-rule.yaml | 12 +-- .../PSRuleValidation/Set-PSRuleOutput.ps1 | 100 +++++++++++------- 4 files changed, 72 insertions(+), 49 deletions(-) diff --git a/.ps-rule/dep-suppress.Rule.yaml b/.ps-rule/dep-suppress.Rule.yaml index 0eedcfb968..6dc96bce1e 100644 --- a/.ps-rule/dep-suppress.Rule.yaml +++ b/.ps-rule/dep-suppress.Rule.yaml @@ -3,9 +3,11 @@ apiVersion: github.com/microsoft/PSRule/v1 kind: SuppressionGroup metadata: - name: 'SuppressDependancy' + name: 'SuppressDependency' spec: if: name: '.' startsWith: - 'dep' + - 'ms.' + - 'privatelink.' diff --git a/.ps-rule/min-suppress.Rule.yaml b/.ps-rule/min-suppress.Rule.yaml index 9c984ea8da..85b70eb2c0 100644 --- a/.ps-rule/min-suppress.Rule.yaml +++ b/.ps-rule/min-suppress.Rule.yaml @@ -3,7 +3,7 @@ apiVersion: github.com/microsoft/PSRule/v1 kind: SuppressionGroup metadata: - name: 'Suppressmin' + name: 'SuppressMin' spec: rule: - Azure.Resource.UseTags @@ -12,5 +12,4 @@ spec: name: '.' contains: - 'min' - - \ No newline at end of file + diff --git a/ps-rule.yaml b/ps-rule.yaml index 9ee2318a42..088b834cf7 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -25,6 +25,7 @@ include: execution: suppressedRuleWarning: false + notProcessedWarning: false output: culture: @@ -35,14 +36,9 @@ output: input: pathIgnore: # Ignore other files in the repository. - - '.vscode/' - - '.github/' - - '*.md' - - # Exclude modules but not tests. - - 'modules/**/*.bicep' + - '**/*' + # Do not ignore tests. - '!modules/**/*.test.bicep' - - 'modules/**/*version.json' configuration: # Enable automatic expansion of Azure parameter files. @@ -60,7 +56,7 @@ rule: exclude: # Ignore the following rules for all resources - Azure.KeyVault.PurgeProtect - + # Suppression ignores rules for a specific Azure resource by name. # suppression: # Azure.KeyVault.PurgeProtect: diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index fc24b5c17b..39657ce5fe 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -1,14 +1,45 @@ -function Set-PSRuleOutput { +<# +.SYNOPSIS +Parse an input csv file containing the output of the PSRule pre-flight checks and generate formatted markdown file out of it. + +.DESCRIPTION +Parse input csv file containing the output of the PSRule pre-flight checks and generate formatted markdown file out of it. + +.PARAMETER inputFilePath +Mandatory. The path to the output file created by PSRule in csv format. + +.PARAMETER outputFilePath +Optional. The path to the formatted .md file to be created. + +.PARAMETER skipPassedRulesReport +Optional. Whether to add the detail of passed PSRule to the output markdown file or to limit the list to the failed ones. + +.EXAMPLE +Set-PSRuleOutput -inputFilePath 'C:/PSRule-output.csv' + +Generate a markdown file 'output.md' in the current folder, out of the 'C:/PSRule-output.csv' input, listing all passed and failed rules. + +.EXAMPLE +Set-PSRuleOutput -inputFilePath 'C:/PSRule-output.csv' -outputFilePath 'C:/PSRule-output.md' -skipPassedRulesReport + +Generate a markdown file 'C:/PSRule-output.md', out of the 'C:/PSRule-output.csv' input, listing only the failed rules. +#> +function Set-PSRuleOutput { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory)] [String] $inputFilePath, [Parameter(Mandatory = $false)] - [string] $outputFilePath = './output.md' + [string] $outputFilePath = './output.md', + + [Parameter(Mandatory = $false)] + [switch] $skipPassedRulesReport ) - # Import CSV output and filter by results + ########################################### + # Import CSV output and filter by results # + ########################################### $results = Import-Csv -Path $inputFilePath @@ -18,28 +49,34 @@ $passedRules += $results | Where-Object { $_.Outcome -EQ 'Pass' } $failedRules += $results | Where-Object { $_.Outcome -EQ 'Fail' } + ###################### + # Set output content # + ###################### - #Create Summary table - - $headerTable = [System.Collections.ArrayList]@( - '# Output Summary ', - '', - '| Total No. of Processed Rules| Passed Rules :white_check_mark: | Failed Rules :x: |', - '| :-- | :-- | :-- |' + # Header //TBD: Remove? + $header = [System.Collections.ArrayList]@( + '# PSRule pre-flight validation summary ', + '' ) + Out-File -FilePath $outputFilePath -NoClobber -InputObject $header + + if ($failedRules.Count -eq 0) { + # No failure content + $noFailuresContent = ('## :rocket: All {0} rules passed, YAY! :rocket:' -f $results.Count) + Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $noFailuresContent + } else { + # Failure content + + ## Header table + $headerTable = [System.Collections.ArrayList]@( + '| Total No. of Processed Rules| Passed Rules :white_check_mark: | Failed Rules :x: |', + '| :-- | :-- | :-- |' + ('| {0} | {1} | {2} |' -f $results.Count, $passedRules.Count , $failedRules.Count), + '' + ) + Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $headerTable - $headerTable += ('| {0} | {1} | {2} |' -f $results.Count, $passedRules.Count , $failedRules.Count) - $headerTable += [System.Collections.ArrayList]@( - '') - - # Create markdown file with header table - Out-File -FilePath $outputFilePath -NoClobber -InputObject $headerTable - - - if ($failedRules.Count -gt 0) { - - #Create Failing table - + ## List of failed rules $failContent = [System.Collections.ArrayList]@( '', '
', @@ -50,7 +87,6 @@ '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' ) - foreach ($content in $failedRules ) { # Shorten the target name for deployment resoure type if ($content.TargetType -eq 'Microsoft.Resources/deployments') { @@ -68,20 +104,18 @@ $resourceLink = $content.RuleName } $failContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) - } $failContent += [System.Collections.ArrayList]@( '', '
', '' ) - #Append markdown with failed rules table + # Append to output Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $failContent } - # Create Passing table - if ($passedRules.Count -gt 0) { - + if (($passedRules.Count -gt 0) -and -not $skipPassedRulesReport) { + # List of passed rules $passContent = [System.Collections.ArrayList]@( '', '
', @@ -92,7 +126,6 @@ '| RuleName | TargetName | Synopsis |', '| :-- | :-- | :-- |' ) - foreach ($content in $passedRules ) { # Shorten the target name for deployment resoure type if ($content.TargetType -eq 'Microsoft.Resources/deployments') { @@ -109,7 +142,6 @@ Write-Warning "Unable to build url for $content.RuleName" $resourceLink = $content.RuleName } - $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) } @@ -118,13 +150,7 @@ '
', '' ) - #Append markdown with passed rules table + # Append to output Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $passContent - } - - } - - - From e444749aa3a610d3d28a3c90faf324e1f2199adb Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 13 Dec 2022 18:54:49 +0100 Subject: [PATCH 102/133] clean psrule settings --- ps-rule.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ps-rule.yaml b/ps-rule.yaml index 40efd1043b..088b834cf7 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -15,14 +15,11 @@ binding: # Require minimum versions of modules. requires: - # PSRule: '2.5.0' - # PSRule: '@pre >=2.4.0' + PSRule: '@pre >=2.4.0' PSRule.Rules.Azure: '@pre >=1.19.2' # Use PSRule for Azure. include: - versions: - - 2.4.0 module: - PSRule.Rules.Azure From 23f1be185d87c8b9231aa4b84b163c47992657b6 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 13 Dec 2022 18:57:52 +0100 Subject: [PATCH 103/133] pipeline trigger --- .github/workflows/platform.linter.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/platform.linter.yml b/.github/workflows/platform.linter.yml index 83fe787548..6a04359de4 100644 --- a/.github/workflows/platform.linter.yml +++ b/.github/workflows/platform.linter.yml @@ -5,6 +5,7 @@ on: pull_request: branches: - main + - hack/topic6 env: variablesPath: 'settings.yml' From 1a4dcdc69a1f4a245d2d9ef638b1ae4774931005 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 13 Dec 2022 19:08:21 +0100 Subject: [PATCH 104/133] scheduled pipeline --- .../workflows/platform.schedule.psrule.yml | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 .github/workflows/platform.schedule.psrule.yml diff --git a/.github/workflows/platform.schedule.psrule.yml b/.github/workflows/platform.schedule.psrule.yml new file mode 100644 index 0000000000..e5520372d4 --- /dev/null +++ b/.github/workflows/platform.schedule.psrule.yml @@ -0,0 +1,108 @@ +name: '.Platform: PSRule Pre-Flight validation' + +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' + +env: + variablesPath: 'settings.yml' + modulesPath: 'modules' + TOKEN_NAMEPREFIX: '${{ secrets.TOKEN_NAMEPREFIX }}' + +jobs: + psrule: + name: PSRule + runs-on: ubuntu-latest + steps: + # Analyze repository with PSRule + - name: Checkout + uses: actions/checkout@v3 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Replace tokens in template file' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + # Grouping task logs + Write-Output '::group::Replace tokens in template file' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') + + # Populate tokens + $Tokens = @{ + subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' + tenantId = '${{ env.ARM_TENANT_ID }}' + } + + ## Add local (source control) tokens + $tokenMap = @{} + foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { + $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } + } + Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose + $Tokens += $tokenMap + + ## Swap 'namePrefix' token if empty and provided as a GitHub secret + if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ + Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose + $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' + } + + # Get File Path List + $modulesFolderPath = Join-Path $env:GITHUB_WORKSPACE '${{ env.modulesPath }}' + $moduleTestFiles = [System.Collections.ArrayList]@() + $moduleTestFiles += Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.test.bicep -Recurse -Force -Name + + # Construct Token Function Input + $ConvertTokensInputs = @{ + FilePathList = $moduleTestFiles + Tokens = $Tokens + TokenPrefix = '${{ env.tokenPrefix }}' + TokenSuffix = '${{ env.tokenSuffix }}' + } + + Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose + + # Invoke Token Replacement Functionality [For Module] + $null = Convert-TokensInFileList @ConvertTokensInputs -verbose + + Write-Output '::endgroup::' + - name: Run PSRule analysis + uses: microsoft/ps-rule@v2.4.0 + continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project + with: + modules: 'PSRule.Rules.Azure' + inputPath: '${{ env.modulesPath }}/' + outputFormat: Csv + outputPath: '${{ env.modulesPath }}/PSRule-output.csv' + - name: 'Parse CSV content' + uses: azure/powershell@v1 + with: + azPSVersion: 'latest' + inlineScript: | + # Grouping task logs + Write-Output '::group::Parse CSV content' + + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'PSRuleValidation' 'Set-PSRuleOutput.ps1') + + # Populate parameter input + $ParameterInput = @{ + inputFilePath = '${{ env.modulesPath }}/PSRule-output.csv' + outputFilePath = '${{ env.modulesPath }}/PSRule-output.md' + skipPassedRulesReport = $true + } + + # Invoke function + $null = Set-PSRuleOutput @ParameterInput + + Write-Output '::endgroup::' + - name: Output to GitHub job summaries + if: always() + run: cat '${{ env.modulesPath }}/PSRule-output.md' >> $GITHUB_STEP_SUMMARY From 80df859655154f4f5b2f2240786c28860f7e1d8f Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 13 Dec 2022 19:10:35 +0100 Subject: [PATCH 105/133] linter cleanup --- .github/workflows/platform.linter.yml | 108 ------------------ .../workflows/platform.schedule.psrule.yml | 4 + 2 files changed, 4 insertions(+), 108 deletions(-) diff --git a/.github/workflows/platform.linter.yml b/.github/workflows/platform.linter.yml index 6a04359de4..f43db21184 100644 --- a/.github/workflows/platform.linter.yml +++ b/.github/workflows/platform.linter.yml @@ -5,12 +5,6 @@ on: pull_request: branches: - main - - hack/topic6 - -env: - variablesPath: 'settings.yml' - modulesPath: 'modules' - TOKEN_NAMEPREFIX: '${{ secrets.TOKEN_NAMEPREFIX }}' jobs: build: @@ -37,105 +31,3 @@ jobs: DEFAULT_BRANCH: ${{ github.base_ref }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} FILTER_REGEX_EXCLUDE: '[module.tests.ps1|Get\-ModulesAsMarkdownTable.ps1|.*yml]' - - # Discuss if running on PR to the whole repo (current implementation) or only on files changed - psrule: - name: PSRule - runs-on: ubuntu-latest - steps: - # Analyze repository with PSRule - - name: Checkout - uses: actions/checkout@v3 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: 'Replace tokens in template file' - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - # Grouping task logs - Write-Output '::group::Replace tokens in template file' - - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # Populate tokens - $Tokens = @{ - resourceGroupName = '${{ env.resourceGroupName }}' - subscriptionId = '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId = '${{ secrets.ARM_MGMTGROUP_ID }}' - tenantId = '${{ env.ARM_TENANT_ID }}' - } - - ## Add local (source control) tokens - $tokenMap = @{} - foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - } - Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - $Tokens += $tokenMap - - ## Swap 'namePrefix' token if empty and provided as a GitHub secret - if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - } - - # Get File Path List - $modulesFolderPath = Join-Path $env:GITHUB_WORKSPACE '${{ env.modulesPath }}' - $moduleTestFiles = [System.Collections.ArrayList]@() - # $moduleTestFiles += Get-ChildItem -Path $modulesFolderPath -Filter *.test.bicep -Recurse -Force -Name - # Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.test.bicep -Recurse -Force -Name | Join-Path $env:GITHUB_WORKSPACE '$._' - $moduleTestFiles += Get-ChildItem -Path $env:GITHUB_WORKSPACE -Filter *.test.bicep -Recurse -Force -Name - # | ForEach-Object {$_.root} | Join-Path -ChildPath "Subdir" - - # Construct Token Function Input - $ConvertTokensInputs = @{ - FilePathList = $moduleTestFiles - Tokens = $Tokens - TokenPrefix = '${{ env.tokenPrefix }}' - TokenSuffix = '${{ env.tokenSuffix }}' - } - - Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # Invoke Token Replacement Functionality [For Module] - # $null = - Convert-TokensInFileList @ConvertTokensInputs -verbose - - Write-Output '::endgroup::' - - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.4.0 - continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project - with: - modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulesPath }}/' - outputFormat: Csv - outputPath: '${{ env.modulesPath }}/PSRule-output.csv' - - name: 'Parse CSV content' - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - # Grouping task logs - Write-Output '::group::Parse CSV content' - - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'PSRuleValidation' 'Set-PSRuleOutput.ps1') - - # Populate parameter input - $ParameterInput = @{ - inputFilePath = '${{ env.modulesPath }}/PSRule-output.csv' - outputFilePath = '${{ env.modulesPath }}/PSRule-output.md' - skipPassedRulesReport = $true - } - - # Invoke function - $null = Set-PSRuleOutput @ParameterInput - - Write-Output '::endgroup::' - - name: Output to GitHub job summaries - if: always() - run: cat '${{ env.modulesPath }}/PSRule-output.md' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/platform.schedule.psrule.yml b/.github/workflows/platform.schedule.psrule.yml index e5520372d4..47631c33f8 100644 --- a/.github/workflows/platform.schedule.psrule.yml +++ b/.github/workflows/platform.schedule.psrule.yml @@ -4,6 +4,10 @@ on: workflow_dispatch: schedule: - cron: '0 0 * * *' + push: + branches: + - users/erikag/psrule-check-onschedule + env: variablesPath: 'settings.yml' From c572528aac9666084e4a37f6eb455b17cfbd5fd6 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 13 Dec 2022 20:00:07 +0100 Subject: [PATCH 106/133] update workflow --- ...schedule.psrule.yml => platform.psrule.librarycheck.yml} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{platform.schedule.psrule.yml => platform.psrule.librarycheck.yml} (97%) diff --git a/.github/workflows/platform.schedule.psrule.yml b/.github/workflows/platform.psrule.librarycheck.yml similarity index 97% rename from .github/workflows/platform.schedule.psrule.yml rename to .github/workflows/platform.psrule.librarycheck.yml index 47631c33f8..c88de2d6ba 100644 --- a/.github/workflows/platform.schedule.psrule.yml +++ b/.github/workflows/platform.psrule.librarycheck.yml @@ -1,4 +1,4 @@ -name: '.Platform: PSRule Pre-Flight validation' +name: '.Platform: Library PSRule pre-flight validation' on: workflow_dispatch: @@ -17,9 +17,9 @@ env: jobs: psrule: name: PSRule - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - # Analyze repository with PSRule + # Analyze module library with PSRule - name: Checkout uses: actions/checkout@v3 - name: Set environment variables From c91316866b5e6f38847e586d814e552102dcf3b6 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 13 Dec 2022 20:06:06 +0100 Subject: [PATCH 107/133] remove actions and templates --- .../jobs.validateModulePSRule.yml | 203 ------------------ .../pipelineTemplates/stages.module.yml | 12 -- .../templates/validateModulePSRule/action.yml | 130 ----------- ...k.yml => platform.librarycheck.psrule.yml} | 6 +- 4 files changed, 3 insertions(+), 348 deletions(-) delete mode 100644 .azuredevops/pipelineTemplates/jobs.validateModulePSRule.yml delete mode 100644 .github/actions/templates/validateModulePSRule/action.yml rename .github/workflows/{platform.psrule.librarycheck.yml => platform.librarycheck.psrule.yml} (98%) diff --git a/.azuredevops/pipelineTemplates/jobs.validateModulePSRule.yml b/.azuredevops/pipelineTemplates/jobs.validateModulePSRule.yml deleted file mode 100644 index 9084bee412..0000000000 --- a/.azuredevops/pipelineTemplates/jobs.validateModulePSRule.yml +++ /dev/null @@ -1,203 +0,0 @@ -######################################################### -## 'Validate module with PSRule' Pipeline Template ## -######################################################### -## -## This pipeline template contains the logic to validate a given module's testfile with PSRule -## -## -######################################################## -## -##---------------------------------------------## -## TEMPLATE PARAMETERS ## -##---------------------------------------------## -## -## By default it uses the variables specified in the below [parameters] section. However, you can overwrite these variables in the -## referencing pipeline by providing the parameter explicitly. -## -## NOTE: If you don't need to overwrite a shared value, you can IGNORE this section -## -## |============================================================================================================================================================================================================================================| -## | Parameter | Default Value | Description | Example | -## |---------------------------------|--------------------------------------|-----------------------------------------------------------------------------------------------------------|-------------------------------------------------------| -## | serviceConnection | '$(serviceConnection)' | The service connection that connects to Azure. | 'demo-internal' | -## | poolName | '$(poolName)' | You can provide either a [poolname] or [vmImage] to run the job on. | 'Custom Deployment Pool' | -## | vmImage | '$(vmImage)' | You can provide either a [poolname] or [vmImage] to run the job on. | 'ubuntu20.04' | -## | defaultJobTimeoutInMinutes | 120 | The timeout for the job in this pipeline. | 120 | -## | customParameterFileTokens | '' | | | -## | modulePath | '$(modulePath)' | The path to the module to deploy. | 'c:/KeyVault' | -## | resourceGroupName | '$(resourceGroupName)' | The resourcegroup to deploy into. Required only for Resource-Group-Level deployments. | 'validation-rg' | -## | subscriptionId | '$(ARM_SUBSCRIPTION_ID)' | The id of the subscription to deploy into when using a Management group service connection. | 'aed7c000-6387-412e-bed0-24dfddf4bbc6' | -## | managementGroupId | '$(ARM_MGMTGROUP_ID)' | The id of the management group to deploy into. Required only for Management-Group-Level deployments. | '6ycc9620-cb01-454f-9ebc-fc6b1df48d64' | -## | azurePowerShellVersion | '$(azurePowerShellVersion)' | Used for configuring the Azure PowerShellModules Version, one of the example values. | 'latestVersion' or 'OtherVersion' | -## | preferredAzurePowerShellVersion | '$(preferredAzurePowerShellVersion)' | Used for configuring the Azure PowerShellModules Version, either an empty string or the specific version. | '4.4.0' | -## |============================================================================================================================================================================================================================================| -## -##---------------------------------------------## - -parameters: - # Pipeline-related parameters - serviceConnection: '$(serviceConnection)' - poolName: '$(poolName)' - vmImage: '$(vmImage)' - defaultJobTimeoutInMinutes: 120 - # Logic-related parameters - customParameterFileTokens: '' - modulePath: '$(modulePath)' - resourceGroupName: '$(resourceGroupName)' - subscriptionId: '$(ARM_SUBSCRIPTION_ID)' - managementGroupId: '$(ARM_MGMTGROUP_ID)' - # Azure PowerShell Version parameters - azurePowerShellVersion: '$(azurePowerShellVersion)' - preferredAzurePowerShellVersion: '$(preferredAzurePowerShellVersion)' - -##---------------------------------------------## -## TEMPLATE LOGIC ## -##---------------------------------------------## -jobs: - - template: /.azuredevops/pipelineTemplates/jobs.getModuleTestFiles.yml - - job: deploy - displayName: 'Run PSRule on ' # Auto-populated - timeoutInMinutes: ${{ parameters.defaultJobTimeoutInMinutes }} - pool: - ${{ if ne(parameters.vmImage, '') }}: - vmImage: ${{ parameters.vmImage }} - ${{ if ne(parameters.poolName, '') }}: - name: ${{ parameters.poolName }} - dependsOn: - - getModuleTestFiles - strategy: - matrix: $[ dependencies.getModuleTestFiles.outputs['getModuleTestFilesTask.moduleTests'] ] - ##---------------------------------------------## - ## TEMPLATE LOGIC ## - ##---------------------------------------------## - steps: - # [Agent] Prepare environment - #---------------------------- - - task: PowerShell@2 - displayName: 'Setup agent for deployment' - inputs: - targetType: inline - pwsh: true - script: | - # Load used functions - . (Join-Path '$(System.DefaultWorkingDirectory)' 'utilities' 'pipelines' 'sharedScripts' 'Set-EnvironmentOnAgent.ps1') - - # Define PS modules to install on the runner - $Modules = @( - @{ Name = 'Az.Accounts' }, - @{ Name = 'Az.Resources' }, - @{ Name = 'powershell-yaml'; Version = '0.4.2'} - ) - - # Additional PS modules need to be installed for the removal step in case it is enabled - if ('${{ parameters.removeDeployment}}' -eq 'true') { - $Modules += @( - @{ Name = 'Az.CognitiveServices' }, - @{ Name = 'Az.Compute' }, - @{ Name = 'Az.KeyVault' }, - @{ Name = 'Az.Monitor' }, - @{ Name = 'Az.OperationalInsights' }, - @{ Name = 'Az.RecoveryServices' } - ) - } - - # Set agent up - Set-EnvironmentOnAgent -PSModules $Modules - - # [Agent] Replace tokens - #----------------------- - - task: AzurePowerShell@5 - displayName: 'Replace tokens in template file via connection [${{ parameters.serviceConnection }}]' - inputs: - azureSubscription: ${{ parameters.serviceConnection }} - azurePowerShellVersion: 'latestVersion' - preferredAzurePowerShellVersion: '' - ScriptType: InlineScript - pwsh: true - inline: | - # Load used functions - . (Join-Path '$(System.DefaultWorkingDirectory)' 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # Get Service Principal Object ID - $context = Get-AzContext - $servicePrincipalAppId = $context.Account.Id - $servicePrincipal = Get-AzADServicePrincipal -ApplicationId $servicePrincipalAppId - $servicePrincipalObjectId = $servicePrincipal.Id - - # Get target files - $moduleTestFilePath = Join-Path '$(System.DefaultWorkingDirectory)' '$(modulePath)' '$(moduleTestFilePath)' - $targetFileList = @($moduleTestFilePath) - - # Construct Token Function Input - $ConvertTokensInputs = @{ - FilePathList = $targetFileList - Tokens = @{} - TokenPrefix = '$(tokenPrefix)' - TokenSuffix = '$(tokenSuffix)' - } - - # Add enforced tokens - $ConvertTokensInputs.Tokens += @{ - resourceGroupName = '${{ parameters.resourceGroupName }}' - subscriptionId = '${{ parameters.subscriptionId }}' - managementGroupId = '${{ parameters.managementGroupId }}' - tenantId = '$(ARM_TENANT_ID)' - deploymentSpId = $servicePrincipalObjectId - } - - # Add local (source control) tokens - $tokenMap = @{} - foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - } - Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - $ConvertTokensInputs.Tokens += $tokenMap - - # Swap 'namePrefix' token if empty and provided as a Azure DevOps variable - if([String]::IsNullOrEmpty($ConvertTokensInputs.Tokens['namePrefix'])){ - Write-Verbose 'Using [namePrefix] token from Azure DevOps Variable Groups' -Verbose - $ConvertTokensInputs.Tokens['namePrefix'] = "$(TOKEN_NAMEPREFIX)" - } - - # Add custom tokens (passed in via the pipeline) - if(-not [String]::IsNullOrEmpty('${{ parameters.customParameterFileTokens }}')) { - $customTokens = '${{ parameters.customParameterFileTokens }}' | ConvertFrom-Json -AsHashTable - Write-Verbose ('Using custom parameter file tokens [{0}]' -f ($customTokens.Keys -join ', ')) -Verbose - $ConvertTokensInputs.Tokens += $customTokens - } - - Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # Invoke Token Replacement Functionality [For Module] - $null = Convert-TokensInFileList @ConvertTokensInputs - - # Get target files for modules dependencies - $DependencyParameterFilePaths = [System.Collections.ArrayList]@() - $DependencyParameterFolders = Get-ChildItem -Path (Join-Path '$(System.DefaultWorkingDirectory)' 'utilities' 'pipelines' 'dependencies') -Recurse -Filter 'parameters' -Directory - foreach ($FolderPath in $DependencyParameterFolders.FullName) { - $DependencyParameterFilePaths += Get-ChildItem -Path $FolderPath -Recurse -Filter '*.json' - } - $ConvertTokensInputs.FilePathList = $DependencyParameterFilePaths - - # Invoke Token Replacement Functionality [For Dependencies] - $null = Convert-TokensInFileList @ConvertTokensInputs - - # [Validation] task(s) - #--------------------- - # Analyze Azure resources using PSRule for Azure - - task: ps-rule-assert@2 - displayName: Analyze Azure template files - inputs: - modules: 'PSRule.Rules.Azure' - inputPath: '$(System.DefaultWorkingDirectory)$(modulePath)/$(moduleTestFilePath)' - outputFormat: NUnit3 # Save results to an NUnit report. - outputPath: '$(System.DefaultWorkingDirectory)$(modulePath)/$(moduleTestFilePath)-output.xml' # Write NUnit report to '$(System.DefaultWorkingDirectory)$(modulePath)/$(moduleTestFilePath)-output.xml'. - - # Publish NUnit report as test results - - task: PublishTestResults@2 - displayName: 'Publish PSRule results' - inputs: - testRunTitle: 'PSRule' # The title to use for the test run. - testRunner: NUnit # Import report using the NUnit format. - testResultsFiles: '$(System.DefaultWorkingDirectory)$(modulePath)/$(moduleTestFilePath)-output.xml' # The previously saved NUnit report. - condition: succeededOrFailed() diff --git a/.azuredevops/pipelineTemplates/stages.module.yml b/.azuredevops/pipelineTemplates/stages.module.yml index fd8278776e..647517a17e 100644 --- a/.azuredevops/pipelineTemplates/stages.module.yml +++ b/.azuredevops/pipelineTemplates/stages.module.yml @@ -10,20 +10,8 @@ stages: jobs: - template: /.azuredevops/pipelineTemplates/jobs.validateModulePester.yml - - stage: PSRuleValidation - dependsOn: [] - displayName: PSRule validation - jobs: - - template: /.azuredevops/pipelineTemplates/jobs.validateModulePSRule.yml - - stage: deployment displayName: Deployment validation - dependsOn: - - validation - - PSRuleValidation - condition: - | # Deployment validation will not be blocked by the failing PSRule validation stage (for the time being). <- Remove the condition block to change behavior - eq(dependencies.validation.result, 'Succeeded') jobs: - template: /.azuredevops/pipelineTemplates/jobs.validateModuleDeployment.yml parameters: diff --git a/.github/actions/templates/validateModulePSRule/action.yml b/.github/actions/templates/validateModulePSRule/action.yml deleted file mode 100644 index 9f25acea98..0000000000 --- a/.github/actions/templates/validateModulePSRule/action.yml +++ /dev/null @@ -1,130 +0,0 @@ -######################################################### -## 'Validate module with PSRule' Composite Action ## -######################################################### -## -## This composite action contains the logic to validate a module using a set of PSRule tests -## -######################################################### -## -##-------------------------------------------## -## ACTION PARAMETERS ## -##-------------------------------------------## -## -## |==================================================================================================================================================| -## | Parameter | Required | Default | Description | Example | -## |--------------------------|----------|---------|--------------------------------------|-----------------------------------------------------------| -## | moduleTestFilePath | true | '' | The path to the module PSRule tests. | 'utilities/pipelines/staticValidation/module.tests.ps1' | -## | subscriptionId | false | '' | The subscriptionId to deploy to | '1a97b80a-4dda-4f50-ab53-349e29344654' | -## | managementGroupId | false | '' | The managementGroupId to deploy to | '1a97b80a-4dda-4f50-ab53-349e29344654' | -## |==================================================================================================================================================| -## -##---------------------------------------------## - -name: 'Execute PSRule module tests' -description: 'Execute PSRule module tests (if any)' - -inputs: - moduleTestFilePath: - description: 'The path to the test file' - required: true - default: '' - subscriptionId: - description: 'The subscription ID to deploy to' - required: false - managementGroupId: - description: 'The management group ID to deploy to' - required: false - -runs: - using: 'composite' - steps: - # [Module PSRule Test] task(s) - #----------------------------- - - name: 'Replace tokens in template file' - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - $templateFilePath = '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}' - # Grouping task logs - Write-Output '::group::Replace tokens in template file' - - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'tokensReplacement' 'Convert-TokensInFileList.ps1') - - # Populate tokens - $Tokens = @{ - resourceGroupName = '${{ env.resourceGroupName }}' - subscriptionId = '${{ inputs.subscriptionId }}' - managementGroupId = '${{ inputs.managementGroupId }}' - tenantId = '${{ env.ARM_TENANT_ID }}' - } - - ## Add local (source control) tokens - $tokenMap = @{} - foreach ($token in (Get-ChildItem env: | Where-Object -Property Name -Like "localToken_*")) { - $tokenMap += @{ $token.Name.Replace('localToken_','','OrdinalIgnoreCase') = $token.value } - } - Write-Verbose ('Using local tokens [{0}]' -f ($tokenMap.Keys -join ', ')) -Verbose - $Tokens += $tokenMap - - ## Swap 'namePrefix' token if empty and provided as a GitHub secret - if([String]::IsNullOrEmpty($Tokens['namePrefix'])){ - Write-Verbose 'Using [namePrefix] token from GitHub' -Verbose - $Tokens['namePrefix'] = '${{ env.TOKEN_NAMEPREFIX }}' - } - - # Construct Token Function Input - $ConvertTokensInputs = @{ - FilePathList = @($templateFilePath) - Tokens = $Tokens - TokenPrefix = '${{ env.tokenPrefix }}' - TokenSuffix = '${{ env.tokenSuffix }}' - } - - Write-Verbose "Convert Tokens Input:`n $($ConvertTokensInputs | ConvertTo-Json -Depth 10)" -Verbose - - # Invoke Token Replacement Functionality [For Module] - $null = Convert-TokensInFileList @ConvertTokensInputs - - Write-Output '::endgroup::' - - # Run analysis by using the PSRule GitHub action. - - name: Run PSRule analysis - uses: microsoft/ps-rule@v2.4.0 - # continue-on-error: true # Setting this whilst PSRule gets bedded in, in this project - with: - modules: 'PSRule.Rules.Azure' - inputPath: '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}' - outputFormat: Csv - outputPath: '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}-output.csv' - - - name: 'Set PSRule Output' - if: always() - uses: azure/powershell@v1 - with: - azPSVersion: 'latest' - inlineScript: | - # Grouping task logs - Write-Output '::group::Setting Output' - - # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'PSRuleValidation' 'Set-PSRuleOutput.ps1') - - # Populate tokens - $Input = @{ - inputFilePath = '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}-output.csv' - outputFilePath = '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}-output.md' - } - - Write-Verbose "Set PS Rule Output with following parameters:`n $($Input | ConvertTo-Json -Depth 10)" -Verbose - - # Invoke Set PSRule Output Functionality - $null = Set-PSRuleOutput @Input - - Write-Output '::endgroup::' - - - name: Output to Github summaries - shell: bash - if: always() - run: cat '${{ env.modulePath }}/${{ inputs.moduleTestFilePath }}-output.md' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/platform.psrule.librarycheck.yml b/.github/workflows/platform.librarycheck.psrule.yml similarity index 98% rename from .github/workflows/platform.psrule.librarycheck.yml rename to .github/workflows/platform.librarycheck.psrule.yml index c88de2d6ba..1bd6bfa0de 100644 --- a/.github/workflows/platform.psrule.librarycheck.yml +++ b/.github/workflows/platform.librarycheck.psrule.yml @@ -4,9 +4,9 @@ on: workflow_dispatch: schedule: - cron: '0 0 * * *' - push: - branches: - - users/erikag/psrule-check-onschedule + # push: + # branches: + # - users/erikag/psrule-check-onschedule env: From 53cc4ac16449b5404b008b7e33f7cb0681797d9d Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 13 Dec 2022 20:07:54 +0100 Subject: [PATCH 108/133] cleanup module pipelines --- .github/workflows/ms.keyvault.vaults.yml | 25 ------------------- .../workflows/ms.network.virtualnetworks.yml | 25 ------------------- .../workflows/ms.resources.resourcegroups.yml | 25 ------------------- 3 files changed, 75 deletions(-) diff --git a/.github/workflows/ms.keyvault.vaults.yml b/.github/workflows/ms.keyvault.vaults.yml index d04873f37b..924c68b64e 100644 --- a/.github/workflows/ms.keyvault.vaults.yml +++ b/.github/workflows/ms.keyvault.vaults.yml @@ -84,40 +84,15 @@ jobs: modulePath: '${{ env.modulePath }}' moduleTestFilePath: '${{ env.moduleTestFilePath }}' - job_psrule_test: - name: 'PsRule pre-flight validation' - runs-on: ubuntu-latest - needs: - - job_initialize_pipeline - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: Set PSRule validation - uses: ./.github/actions/templates/validateModulePSRule - with: - moduleTestFilePath: ${{ matrix.moduleTestFilePaths }} - subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - ############################# # Deployment validation # ############################# job_module_deploy_validation: runs-on: ubuntu-20.04 - if: ${{ always() && (needs.job_module_pester_validation.result == 'success') }} # Deployment validation will not be blocked by the failing PSRule validation stage (for the time being). <- Remove the condition block to change behavior name: 'Deployment validation' needs: - job_initialize_pipeline - job_module_pester_validation - - job_psrule_test strategy: fail-fast: false matrix: diff --git a/.github/workflows/ms.network.virtualnetworks.yml b/.github/workflows/ms.network.virtualnetworks.yml index 2763ddbe8f..9e55ffa905 100644 --- a/.github/workflows/ms.network.virtualnetworks.yml +++ b/.github/workflows/ms.network.virtualnetworks.yml @@ -84,40 +84,15 @@ jobs: modulePath: '${{ env.modulePath }}' moduleTestFilePath: '${{ env.moduleTestFilePath }}' - job_psrule_test: - name: 'PsRule pre-flight validation' - runs-on: ubuntu-latest - needs: - - job_initialize_pipeline - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: Set PSRule validation - uses: ./.github/actions/templates/validateModulePSRule - with: - moduleTestFilePath: ${{ matrix.moduleTestFilePaths }} - subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - ############################# # Deployment validation # ############################# job_module_deploy_validation: runs-on: ubuntu-20.04 - if: ${{ always() && (needs.job_module_pester_validation.result == 'success') }} # Deployment validation will not be blocked by the failing PSRule validation stage (for the time being). <- Remove the condition block to change behavior name: 'Deployment validation' needs: - job_initialize_pipeline - job_module_pester_validation - - job_psrule_test strategy: fail-fast: false matrix: diff --git a/.github/workflows/ms.resources.resourcegroups.yml b/.github/workflows/ms.resources.resourcegroups.yml index 291fc1b06b..0f4080a311 100644 --- a/.github/workflows/ms.resources.resourcegroups.yml +++ b/.github/workflows/ms.resources.resourcegroups.yml @@ -84,40 +84,15 @@ jobs: modulePath: '${{ env.modulePath }}' moduleTestFilePath: '${{ env.moduleTestFilePath }}' - job_psrule_test: - name: 'PsRule pre-flight validation' - runs-on: ubuntu-latest - needs: - - job_initialize_pipeline - strategy: - fail-fast: false - matrix: - moduleTestFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.moduleTestFilePaths) }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set environment variables - uses: ./.github/actions/templates/setEnvironmentVariables - with: - variablesPath: ${{ env.variablesPath }} - - name: Set PSRule validation - uses: ./.github/actions/templates/validateModulePSRule - with: - moduleTestFilePath: ${{ matrix.moduleTestFilePaths }} - subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' - managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' - ############################# # Deployment validation # ############################# job_module_deploy_validation: runs-on: ubuntu-20.04 - if: ${{ always() && (needs.job_module_pester_validation.result == 'success') }} # Deployment validation will not be blocked by the failing PSRule validation stage (for the time being). <- Remove the condition block to change behavior name: 'Deployment validation' needs: - job_initialize_pipeline - job_module_pester_validation - - job_psrule_test strategy: fail-fast: false matrix: From 96cc4591eb6763fd098b99374428d46cc1fff87b Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 13 Dec 2022 20:08:50 +0100 Subject: [PATCH 109/133] typo --- .ps-rule/dep-suppress.Rule.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ps-rule/dep-suppress.Rule.yaml b/.ps-rule/dep-suppress.Rule.yaml index 6dc96bce1e..9384dbda4c 100644 --- a/.ps-rule/dep-suppress.Rule.yaml +++ b/.ps-rule/dep-suppress.Rule.yaml @@ -1,5 +1,5 @@ --- -# Synopsis: Suppress Rules for dependancies +# Synopsis: Suppress Rules for dependencies apiVersion: github.com/microsoft/PSRule/v1 kind: SuppressionGroup metadata: From 22c74f309f7cc7b5826c3b69327be96eb4e302cb Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 13 Dec 2022 20:09:54 +0100 Subject: [PATCH 110/133] main settings cleanup --- ps-rule.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ps-rule.yaml b/ps-rule.yaml index 088b834cf7..1efe9161f8 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -30,8 +30,6 @@ execution: output: culture: - 'en-US' - #outcome: 'All' - #as: 'Summary' input: pathIgnore: @@ -56,8 +54,3 @@ rule: exclude: # Ignore the following rules for all resources - Azure.KeyVault.PurgeProtect - -# Suppression ignores rules for a specific Azure resource by name. -# suppression: -# Azure.KeyVault.PurgeProtect: -# - '*min*' From 0318422b43226d4671089dfb9ddee07f6a635a5c Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 13 Dec 2022 20:18:45 +0100 Subject: [PATCH 111/133] update trigger --- .github/workflows/platform.librarycheck.psrule.yml | 13 ++++++++----- .../pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 6 +++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/platform.librarycheck.psrule.yml b/.github/workflows/platform.librarycheck.psrule.yml index 1bd6bfa0de..c354a2fa57 100644 --- a/.github/workflows/platform.librarycheck.psrule.yml +++ b/.github/workflows/platform.librarycheck.psrule.yml @@ -3,11 +3,14 @@ name: '.Platform: Library PSRule pre-flight validation' on: workflow_dispatch: schedule: - - cron: '0 0 * * *' - # push: - # branches: - # - users/erikag/psrule-check-onschedule - + - cron: "0 12 * * 0" + displayName: Weekly Sunday Update + branches: + include: + - main + push: + branches: + - users/erikag/psrule-check-onschedule env: variablesPath: 'settings.yml' diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 39657ce5fe..042ca8bfb8 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -53,7 +53,7 @@ function Set-PSRuleOutput { # Set output content # ###################### - # Header //TBD: Remove? + # Header $header = [System.Collections.ArrayList]@( '# PSRule pre-flight validation summary ', '' @@ -93,7 +93,7 @@ function Set-PSRuleOutput { $content.TargetName = $content.TargetName.replace('/home/runner/work/ResourceModules/ResourceModules/modules/', '') } - # Build hyperlinks to PSrule documentation for the rules + # Build hyperlinks to PSRule documentation for the rules $TemplatesBaseUrl = 'https://azure.github.io/PSRule.Rules.Azure/en/rules' try { $PSRuleReferenceUrl = '{0}/{1}' -f $TemplatesBaseUrl, $content.RuleName @@ -132,7 +132,7 @@ function Set-PSRuleOutput { $content.TargetName = $content.TargetName.replace('/home/runner/work/ResourceModules/ResourceModules/modules/', '') } - # Build hyperlinks to PSrule documentation for the rules + # Build hyperlinks to PSRule documentation for the rules $TemplatesBaseUrl = 'https://azure.github.io/PSRule.Rules.Azure/en/rules' try { $PSRuleReferenceUrl = '{0}/{1}' -f $TemplatesBaseUrl, $content.RuleName From 699604b2ba945e06faa6002306fb187b2f742763 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Tue, 13 Dec 2022 20:20:33 +0100 Subject: [PATCH 112/133] update trigger --- .github/workflows/platform.librarycheck.psrule.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/platform.librarycheck.psrule.yml b/.github/workflows/platform.librarycheck.psrule.yml index c354a2fa57..969d7e14f1 100644 --- a/.github/workflows/platform.librarycheck.psrule.yml +++ b/.github/workflows/platform.librarycheck.psrule.yml @@ -3,11 +3,7 @@ name: '.Platform: Library PSRule pre-flight validation' on: workflow_dispatch: schedule: - - cron: "0 12 * * 0" - displayName: Weekly Sunday Update - branches: - include: - - main + - cron: '0 12 * * 0' # Weekly Sunday Update push: branches: - users/erikag/psrule-check-onschedule From f49932b58be1be6ee13d16e78b7df9080c5a703f Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Sun, 15 Jan 2023 20:51:05 +0100 Subject: [PATCH 113/133] docs --- .../The CI environment - Pipeline design.md | 44 ++++++++++++++++++ .../media/CIEnvironment/PSRuleSummary.png | Bin 0 -> 84352 bytes 2 files changed, 44 insertions(+) create mode 100644 docs/wiki/media/CIEnvironment/PSRuleSummary.png diff --git a/docs/wiki/The CI environment - Pipeline design.md b/docs/wiki/The CI environment - Pipeline design.md index 4be6ae2195..d86468e343 100644 --- a/docs/wiki/The CI environment - Pipeline design.md +++ b/docs/wiki/The CI environment - Pipeline design.md @@ -105,6 +105,7 @@ In addition to module pipelines, the repository includes several platform pipeli - [ReadMe pipeline](#readme-pipeline) - [Wiki pipeline](#wiki-pipeline) +- [PSRule Pre-Flight validation pipeline](#psrule-pre-flight-validation-pipeline) ## ReadMe pipeline @@ -121,3 +122,46 @@ Once triggered, the pipeline crawls through the library and updates the tables i The purpose of the wiki pipeline is to sync any files from the `docs/wiki` folder to the wiki repository. It is triggered each time changes are pushed to the `main` branch and only if files in the `docs/wiki` folder are altered. > **Note:** Any changes performed directly on the wiki via the UI will be overwritten by this pipeline. + +## PSRule Pre-Flight validation pipeline + +The purpose of the PSRule Pre-Flight validation pipeline is to validate Azure resources deployed by module validation pipeline tests, by leveraging [PSRule for Azure](https://azure.github.io/PSRule.Rules.Azure/about/). +PSRule for Azure is aligned to the [Azure Well-Architected Framework (WAF)](https://learn.microsoft.com/en-us/azure/architecture/framework/). Tests called rules check the configuration of Azure resources against WAF principles. + +The pipeline, currently only available as a [GitHub workflow](https://github.com/Azure/ResourceModules/blob/main/.github/workflows/platform.librarycheck.psrule.yml), runs weekly on the whole library, providing as output the list of non compliant resources and corresponding failing rules, if any. + +### Configuration settings + +PSRule options set for the CARML repository are configured in the [ps-rule.yaml](https://github.com/Azure/ResourceModules/blob/main/ps-rule.yaml) file. + +Documentation for all configuration options is available at the following links: +- https://aka.ms/ps-rule/options +- https://aka.ms/ps-rule-azure/options + +### Baselines + +A [baseline](https://azure.github.io/PSRule.Rules.Azure/working-with-baselines/) is a standard PSRule artifact that combines rules and configuration. This pipeline uses the default baseline to analyze module test resources. + +For the list of all rules included see [Azure.Default baseline](https://azure.github.io/PSRule.Rules.Azure/en/baselines/Azure.Default/). +To view a list of rules by Azure resources see [Rules by resource](https://azure.github.io/PSRule.Rules.Azure/en/rules/resource/). + +### Exclusions and suppression rules + +Not all baseline rules may be valid for some of the test Azure resources deployed by the module validation pipelines. + +For example, resources deployed by the min tests, aim to validate only the required input parameters for each module. +Therefore, optional features such as diagnostic settings are not configured in those tests. Since enabling logging is a general recommendation for most of the resources supporting them, missing diagnostic settings usually trigger incopliance of PSRule checks, e.g., [Azure.KeyVault.Logs](https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.KeyVault.Logs/). For this reason, these checks are excluded from being evaluated for resources deployed by min tests. + +The following configuration files host exclusions and suppression rules: + +- [.ps-rule\dep-suppress.Rule.yaml](https://github.com/Azure/ResourceModules/blob/main/.ps-rule/dep-suppress.Rule.yaml): for resources deployed as dependencies +- [.ps-rule\min-suppress.Rule.yaml](https://github.com/Azure/ResourceModules/blob/main/.ps-rule/min-suppress.Rule.yaml): for resources deployed by the min tests +- [ps-rule.yaml](https://github.com/Azure/ResourceModules/blob/main/ps-rule.yaml): lists exclusions valid for all resources under the option [Rule.Exclude](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Options/#ruleexclude) + +### Output + +To better outline failed rules and allow fixing incompliant resources quickly, the pipeline leverages the script [utilities\pipelines\PSRuleValidation\Set-PSRuleOutput.ps1](https://github.com/Azure/ResourceModules/blob/main/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1) to aggregate PSRule output into Custom Markdown content and display it to the Actions run summary page. + +PSRule Summary + + diff --git a/docs/wiki/media/CIEnvironment/PSRuleSummary.png b/docs/wiki/media/CIEnvironment/PSRuleSummary.png new file mode 100644 index 0000000000000000000000000000000000000000..4c7e6a8e5e432ec310f972bc1891712b32623f3b GIT binary patch literal 84352 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

6S2m)f#`js6=i7@X3Iwd?{w`h@3hwAKH-!SpE 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 O(ALtsmalm`_&)$I+BF#f literal 0 HcmV?d00001 From f00e1d49dbb0f401f24260898159cffdea084370 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Sun, 15 Jan 2023 20:53:35 +0100 Subject: [PATCH 114/133] remove branch push --- .github/workflows/platform.librarycheck.psrule.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/platform.librarycheck.psrule.yml b/.github/workflows/platform.librarycheck.psrule.yml index 969d7e14f1..2077147595 100644 --- a/.github/workflows/platform.librarycheck.psrule.yml +++ b/.github/workflows/platform.librarycheck.psrule.yml @@ -4,9 +4,6 @@ on: workflow_dispatch: schedule: - cron: '0 12 * * 0' # Weekly Sunday Update - push: - branches: - - users/erikag/psrule-check-onschedule env: variablesPath: 'settings.yml' From 20aab71b8b8794d4beb028eac53e1b891431151c Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:34:29 +0100 Subject: [PATCH 115/133] Update docs/wiki/The CI environment - Pipeline design.md Co-authored-by: Alexander Sehr --- docs/wiki/The CI environment - Pipeline design.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/wiki/The CI environment - Pipeline design.md b/docs/wiki/The CI environment - Pipeline design.md index d86468e343..131c7a4137 100644 --- a/docs/wiki/The CI environment - Pipeline design.md +++ b/docs/wiki/The CI environment - Pipeline design.md @@ -128,7 +128,7 @@ The purpose of the wiki pipeline is to sync any files from the `docs/wiki` folde The purpose of the PSRule Pre-Flight validation pipeline is to validate Azure resources deployed by module validation pipeline tests, by leveraging [PSRule for Azure](https://azure.github.io/PSRule.Rules.Azure/about/). PSRule for Azure is aligned to the [Azure Well-Architected Framework (WAF)](https://learn.microsoft.com/en-us/azure/architecture/framework/). Tests called rules check the configuration of Azure resources against WAF principles. -The pipeline, currently only available as a [GitHub workflow](https://github.com/Azure/ResourceModules/blob/main/.github/workflows/platform.librarycheck.psrule.yml), runs weekly on the whole library, providing as output the list of non compliant resources and corresponding failing rules, if any. +The pipeline, currently only available as a [GitHub workflow](https://github.com/Azure/ResourceModules/blob/main/.github/workflows/platform.librarycheck.psrule.yml), runs weekly on the whole library, providing as output the list of non-compliant resources and corresponding failing rules, if any. ### Configuration settings From 7e3d2a4696dc95081d16becf7a07b606161cd10e Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:34:46 +0100 Subject: [PATCH 116/133] Update .github/workflows/platform.librarycheck.psrule.yml Co-authored-by: Alexander Sehr --- .github/workflows/platform.librarycheck.psrule.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/platform.librarycheck.psrule.yml b/.github/workflows/platform.librarycheck.psrule.yml index 2077147595..cf84924ab9 100644 --- a/.github/workflows/platform.librarycheck.psrule.yml +++ b/.github/workflows/platform.librarycheck.psrule.yml @@ -105,4 +105,4 @@ jobs: Write-Output '::endgroup::' - name: Output to GitHub job summaries if: always() - run: cat '${{ env.modulesPath }}/PSRule-output.md' >> $GITHUB_STEP_SUMMARY + run: Get-Content '${{ env.modulesPath }}/PSRule-output.md' >> $GITHUB_STEP_SUMMARY From 4af74b7fa1c3c8216cc601fad19c4bcbd0def55b Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:35:01 +0100 Subject: [PATCH 117/133] Update utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 Co-authored-by: Alexander Sehr --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 042ca8bfb8..9bf35b81fb 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -28,7 +28,7 @@ function Set-PSRuleOutput { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory)] - [String] $inputFilePath, + [String] $InputFilePath, [Parameter(Mandatory = $false)] [string] $outputFilePath = './output.md', From 5351bad52818c5c80aec54c583a0d677632dc223 Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:35:13 +0100 Subject: [PATCH 118/133] Update utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 Co-authored-by: Alexander Sehr --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 9bf35b81fb..26eec11284 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -31,7 +31,7 @@ function Set-PSRuleOutput { [String] $InputFilePath, [Parameter(Mandatory = $false)] - [string] $outputFilePath = './output.md', + [string] $OutputFilePath = './output.md', [Parameter(Mandatory = $false)] [switch] $skipPassedRulesReport From 9ed2269b6291a125b0128d7a7d5b334740af8b2e Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:35:25 +0100 Subject: [PATCH 119/133] Update utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 Co-authored-by: Alexander Sehr --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 26eec11284..1d523d9e0c 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -34,7 +34,7 @@ function Set-PSRuleOutput { [string] $OutputFilePath = './output.md', [Parameter(Mandatory = $false)] - [switch] $skipPassedRulesReport + [switch] $SkipPassedRulesReport ) ########################################### From 96c8c33f1742e8cb280aafe2f1a67ddf754c99c8 Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:36:26 +0100 Subject: [PATCH 120/133] Update utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 Co-authored-by: Alexander Sehr --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 1d523d9e0c..fb11592e66 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -137,7 +137,7 @@ function Set-PSRuleOutput { try { $PSRuleReferenceUrl = '{0}/{1}' -f $TemplatesBaseUrl, $content.RuleName $null = Invoke-WebRequest -Uri $PSRuleReferenceUrl - $resourceLink = '[' + $content.RuleName + '](' + $PSRuleReferenceUrl + ')' + $resourceLink = '[{0}]({1})' -f $content.RuleName, $PSRuleReferenceUrl } catch { Write-Warning "Unable to build url for $content.RuleName" $resourceLink = $content.RuleName From 047feae9660f17cdbaa73dae53c45bc63ffe77df Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:36:37 +0100 Subject: [PATCH 121/133] Update utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 Co-authored-by: Alexander Sehr --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index fb11592e66..8b5b590aeb 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -98,7 +98,7 @@ function Set-PSRuleOutput { try { $PSRuleReferenceUrl = '{0}/{1}' -f $TemplatesBaseUrl, $content.RuleName $null = Invoke-WebRequest -Uri $PSRuleReferenceUrl - $resourceLink = '[' + $content.RuleName + '](' + $PSRuleReferenceUrl + ')' + $resourceLink = '[{0}]({1})' -f $content.RuleName, $PSRuleReferenceUrl } catch { Write-Warning "Unable to build url for $content.RuleName" $resourceLink = $content.RuleName From 33d5776efd7473a679d3ae95e7bb565f85c8373f Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:37:26 +0100 Subject: [PATCH 122/133] Update utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 Co-authored-by: Alexander Sehr --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 8b5b590aeb..8986ec39c7 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -62,7 +62,7 @@ function Set-PSRuleOutput { if ($failedRules.Count -eq 0) { # No failure content - $noFailuresContent = ('## :rocket: All {0} rules passed, YAY! :rocket:' -f $results.Count) + $noFailuresContent = ('## :rocket: All [{0}] rules passed, YAY! :rocket:' -f $results.Count) Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $noFailuresContent } else { # Failure content From 03952173299f003387e3daecdc5ff727a7d49da1 Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:37:50 +0100 Subject: [PATCH 123/133] Update utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 Co-authored-by: Alexander Sehr --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 8986ec39c7..2d6d50eb59 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -139,7 +139,7 @@ function Set-PSRuleOutput { $null = Invoke-WebRequest -Uri $PSRuleReferenceUrl $resourceLink = '[{0}]({1})' -f $content.RuleName, $PSRuleReferenceUrl } catch { - Write-Warning "Unable to build url for $content.RuleName" + Write-Warning 'Unable to build url for rule [{0}]' -f $content.RuleName) $resourceLink = $content.RuleName } $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) From 0cf84d2571dd15110e4121952fe0318f1ac5bf8a Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:38:12 +0100 Subject: [PATCH 124/133] Update utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 Co-authored-by: Alexander Sehr --- utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 index 2d6d50eb59..39454ca719 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 @@ -100,7 +100,7 @@ function Set-PSRuleOutput { $null = Invoke-WebRequest -Uri $PSRuleReferenceUrl $resourceLink = '[{0}]({1})' -f $content.RuleName, $PSRuleReferenceUrl } catch { - Write-Warning "Unable to build url for $content.RuleName" + Write-Warning ('Unable to build url for rule [{0}]' -f $content.RuleName) $resourceLink = $content.RuleName } $failContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) From 272469a96292b5a17db5226bdd177e9d5a2313c4 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 16 Jan 2023 15:47:45 +0100 Subject: [PATCH 125/133] update comment --- .github/workflows/platform.librarycheck.psrule.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/platform.librarycheck.psrule.yml b/.github/workflows/platform.librarycheck.psrule.yml index 2077147595..13e7b57dd8 100644 --- a/.github/workflows/platform.librarycheck.psrule.yml +++ b/.github/workflows/platform.librarycheck.psrule.yml @@ -3,7 +3,7 @@ name: '.Platform: Library PSRule pre-flight validation' on: workflow_dispatch: schedule: - - cron: '0 12 * * 0' # Weekly Sunday Update + - cron: '0 12 * * 0' # Weekly Sunday Analysis env: variablesPath: 'settings.yml' From 5b89b7a8a4470f0e1507a08b6d2ae51485c9ec4c Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:49:49 +0100 Subject: [PATCH 126/133] Update docs/wiki/The CI environment - Pipeline design.md Co-authored-by: Alexander Sehr --- docs/wiki/The CI environment - Pipeline design.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/wiki/The CI environment - Pipeline design.md b/docs/wiki/The CI environment - Pipeline design.md index 131c7a4137..bf934e8e76 100644 --- a/docs/wiki/The CI environment - Pipeline design.md +++ b/docs/wiki/The CI environment - Pipeline design.md @@ -126,7 +126,7 @@ The purpose of the wiki pipeline is to sync any files from the `docs/wiki` folde ## PSRule Pre-Flight validation pipeline The purpose of the PSRule Pre-Flight validation pipeline is to validate Azure resources deployed by module validation pipeline tests, by leveraging [PSRule for Azure](https://azure.github.io/PSRule.Rules.Azure/about/). -PSRule for Azure is aligned to the [Azure Well-Architected Framework (WAF)](https://learn.microsoft.com/en-us/azure/architecture/framework/). Tests called rules check the configuration of Azure resources against WAF principles. +PSRule for Azure is aligned to the [Well-Architected Framework (WAF)](https://learn.microsoft.com/en-us/azure/architecture/framework/). Tests, called _Rules_, check the configuration of Azure resources against WAF principles. The pipeline, currently only available as a [GitHub workflow](https://github.com/Azure/ResourceModules/blob/main/.github/workflows/platform.librarycheck.psrule.yml), runs weekly on the whole library, providing as output the list of non-compliant resources and corresponding failing rules, if any. From 3f260fbf83b720ae291981aff9ec3c2f4e5a71b5 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 16 Jan 2023 16:26:18 +0100 Subject: [PATCH 127/133] update wiki --- docs/wiki/The CI environment - Pipeline design.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/wiki/The CI environment - Pipeline design.md b/docs/wiki/The CI environment - Pipeline design.md index bf934e8e76..4a4dde8293 100644 --- a/docs/wiki/The CI environment - Pipeline design.md +++ b/docs/wiki/The CI environment - Pipeline design.md @@ -11,6 +11,7 @@ This section provides an overview of the design principles applied to the CARML - [Platform pipelines](#platform-pipelines) - [ReadMe pipeline](#readme-pipeline) - [Wiki pipeline](#wiki-pipeline) + - [PSRule Pre-Flight validation pipeline](#psrule-pre-flight-validation-pipeline) --- @@ -140,7 +141,7 @@ Documentation for all configuration options is available at the following links: ### Baselines -A [baseline](https://azure.github.io/PSRule.Rules.Azure/working-with-baselines/) is a standard PSRule artifact that combines rules and configuration. This pipeline uses the default baseline to analyze module test resources. +A [baseline](https://azure.github.io/PSRule.Rules.Azure/working-with-baselines/) is a standard PSRule artifact that combines rules and configuration. The PSRule Pre-Flight validation pipeline uses the default baseline to analyze module test resources. For the list of all rules included see [Azure.Default baseline](https://azure.github.io/PSRule.Rules.Azure/en/baselines/Azure.Default/). To view a list of rules by Azure resources see [Rules by resource](https://azure.github.io/PSRule.Rules.Azure/en/rules/resource/). @@ -152,11 +153,13 @@ Not all baseline rules may be valid for some of the test Azure resources deploye For example, resources deployed by the min tests, aim to validate only the required input parameters for each module. Therefore, optional features such as diagnostic settings are not configured in those tests. Since enabling logging is a general recommendation for most of the resources supporting them, missing diagnostic settings usually trigger incopliance of PSRule checks, e.g., [Azure.KeyVault.Logs](https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.KeyVault.Logs/). For this reason, these checks are excluded from being evaluated for resources deployed by min tests. -The following configuration files host exclusions and suppression rules: +PSRule allows skipping rules on two levels: -- [.ps-rule\dep-suppress.Rule.yaml](https://github.com/Azure/ResourceModules/blob/main/.ps-rule/dep-suppress.Rule.yaml): for resources deployed as dependencies -- [.ps-rule\min-suppress.Rule.yaml](https://github.com/Azure/ResourceModules/blob/main/.ps-rule/min-suppress.Rule.yaml): for resources deployed by the min tests -- [ps-rule.yaml](https://github.com/Azure/ResourceModules/blob/main/ps-rule.yaml): lists exclusions valid for all resources under the option [Rule.Exclude](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Options/#ruleexclude) +- **Exclusions**: Can be leveraged to exclude specific baseline rules from being evaluated for any resource. + - [ps-rule.yaml](https://github.com/Azure/ResourceModules/blob/main/ps-rule.yaml): Lists the name of specific rules to exclude under the option [Rule.Exclude](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Options/#ruleexclude) +- **Suppression Groups**: PSRule can use [Suppression Groups](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_SuppressionGroups/) to suppress rules based on a condition. Suppression groups can be leveraged when some of the rules in the baseline are not relevant under specific conditions, e.g., only for specific resources. They are stored in the `.ps-rule` repo root folder in `.yaml` format. In particular: + - [.ps-rule\dep-suppress.Rule.yaml](https://github.com/Azure/ResourceModules/blob/main/.ps-rule/dep-suppress.Rule.yaml): Lists rules to be ignored for resources deployed as dependencies + - [.ps-rule\min-suppress.Rule.yaml](https://github.com/Azure/ResourceModules/blob/main/.ps-rule/min-suppress.Rule.yaml): Lists rules to be ignored for resources deployed by the min tests ### Output From a149f56793ef4173fde5689de94de3298e63dbe5 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 16 Jan 2023 16:27:46 +0100 Subject: [PATCH 128/133] function name --- .../platform.librarycheck.psrule.yml | 4 +-- .../The CI environment - Pipeline design.md | 2 +- ...eOutput.ps1 => Set-PSRuleGitHubOutput.ps1} | 28 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) rename utilities/pipelines/PSRuleValidation/{Set-PSRuleOutput.ps1 => Set-PSRuleGitHubOutput.ps1} (90%) diff --git a/.github/workflows/platform.librarycheck.psrule.yml b/.github/workflows/platform.librarycheck.psrule.yml index cc3a91fb55..ba3990eaab 100644 --- a/.github/workflows/platform.librarycheck.psrule.yml +++ b/.github/workflows/platform.librarycheck.psrule.yml @@ -90,7 +90,7 @@ jobs: Write-Output '::group::Parse CSV content' # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'PSRuleValidation' 'Set-PSRuleOutput.ps1') + . (Join-Path $env:GITHUB_WORKSPACE 'utilities' 'pipelines' 'PSRuleValidation' 'Set-PSRuleGitHubOutput.ps1') # Populate parameter input $ParameterInput = @{ @@ -100,7 +100,7 @@ jobs: } # Invoke function - $null = Set-PSRuleOutput @ParameterInput + $null = Set-PSRuleGitHubOutput @ParameterInput Write-Output '::endgroup::' - name: Output to GitHub job summaries diff --git a/docs/wiki/The CI environment - Pipeline design.md b/docs/wiki/The CI environment - Pipeline design.md index 4a4dde8293..22bbefdfa0 100644 --- a/docs/wiki/The CI environment - Pipeline design.md +++ b/docs/wiki/The CI environment - Pipeline design.md @@ -163,7 +163,7 @@ PSRule allows skipping rules on two levels: ### Output -To better outline failed rules and allow fixing incompliant resources quickly, the pipeline leverages the script [utilities\pipelines\PSRuleValidation\Set-PSRuleOutput.ps1](https://github.com/Azure/ResourceModules/blob/main/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1) to aggregate PSRule output into Custom Markdown content and display it to the Actions run summary page. +To better outline failed rules and allow fixing incompliant resources quickly, the pipeline leverages the script [utilities\pipelines\PSRuleValidation\Set-PSRuleGitHubOutput.ps1](https://github.com/Azure/ResourceModules/blob/main/utilities/pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1) to aggregate PSRule output into Custom Markdown content and display it to the Actions run summary page. PSRule Summary diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1 similarity index 90% rename from utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 rename to utilities/pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1 index 39454ca719..a0bea02de8 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1 @@ -15,16 +15,16 @@ Optional. The path to the formatted .md file to be created. Optional. Whether to add the detail of passed PSRule to the output markdown file or to limit the list to the failed ones. .EXAMPLE -Set-PSRuleOutput -inputFilePath 'C:/PSRule-output.csv' +Set-PSRuleGitHubOutput -inputFilePath 'C:/PSRule-output.csv' Generate a markdown file 'output.md' in the current folder, out of the 'C:/PSRule-output.csv' input, listing all passed and failed rules. .EXAMPLE -Set-PSRuleOutput -inputFilePath 'C:/PSRule-output.csv' -outputFilePath 'C:/PSRule-output.md' -skipPassedRulesReport +Set-PSRuleGitHubOutput -inputFilePath 'C:/PSRule-output.csv' -outputFilePath 'C:/PSRule-output.md' -skipPassedRulesReport Generate a markdown file 'C:/PSRule-output.md', out of the 'C:/PSRule-output.csv' input, listing only the failed rules. #> -function Set-PSRuleOutput { +function Set-PSRuleGitHubOutput { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory)] @@ -140,17 +140,17 @@ function Set-PSRuleOutput { $resourceLink = '[{0}]({1})' -f $content.RuleName, $PSRuleReferenceUrl } catch { Write-Warning 'Unable to build url for rule [{0}]' -f $content.RuleName) - $resourceLink = $content.RuleName - } - $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) - + $resourceLink = $content.RuleName } - $passContent += [System.Collections.ArrayList]@( - '', - '

', - '' - ) - # Append to output - Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $passContent + $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) + } + $passContent += [System.Collections.ArrayList]@( + '', + '
', + '' + ) + # Append to output + Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $passContent +} } From ce27d3132019782ebab302a4d80b0177a718629d Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 16 Jan 2023 16:29:35 +0100 Subject: [PATCH 129/133] test on push --- .github/workflows/platform.librarycheck.psrule.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/platform.librarycheck.psrule.yml b/.github/workflows/platform.librarycheck.psrule.yml index ba3990eaab..0c48998b48 100644 --- a/.github/workflows/platform.librarycheck.psrule.yml +++ b/.github/workflows/platform.librarycheck.psrule.yml @@ -4,6 +4,10 @@ on: workflow_dispatch: schedule: - cron: '0 12 * * 0' # Weekly Sunday Analysis + push: + branches: + - users/erikag/psrule-check-onschedule + env: variablesPath: 'settings.yml' From 19fd44513a912e01b9d94d98b2fce9d8135189b3 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 16 Jan 2023 17:22:28 +0100 Subject: [PATCH 130/133] fix typo --- .../Set-PSRuleGitHubOutput.ps1 | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1 index a0bea02de8..f07052d295 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1 @@ -139,18 +139,18 @@ function Set-PSRuleGitHubOutput { $null = Invoke-WebRequest -Uri $PSRuleReferenceUrl $resourceLink = '[{0}]({1})' -f $content.RuleName, $PSRuleReferenceUrl } catch { - Write-Warning 'Unable to build url for rule [{0}]' -f $content.RuleName) - $resourceLink = $content.RuleName - } - $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) + Write-Warning 'Unable to build url for rule [{0}]' -f $content.RuleName + $resourceLink = $content.RuleName + } + $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) + } + $passContent += [System.Collections.ArrayList]@( + '', + '
', + '' + ) + # Append to output + Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $passContent } - $passContent += [System.Collections.ArrayList]@( - '', - '
', - '' - ) - # Append to output - Out-File -FilePath $outputFilePath -Append -NoClobber -InputObject $passContent -} } From 0ceb81d63f9437965583e9248ddb14911ddbd2f5 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 16 Jan 2023 17:42:03 +0100 Subject: [PATCH 131/133] format resource name --- .../pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utilities/pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1 b/utilities/pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1 index f07052d295..6fd90f51d8 100644 --- a/utilities/pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1 +++ b/utilities/pipelines/PSRuleValidation/Set-PSRuleGitHubOutput.ps1 @@ -103,7 +103,7 @@ function Set-PSRuleGitHubOutput { Write-Warning ('Unable to build url for rule [{0}]' -f $content.RuleName) $resourceLink = $content.RuleName } - $failContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) + $failContent += ('| {0} | `{1}` | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) } $failContent += [System.Collections.ArrayList]@( '', @@ -142,7 +142,7 @@ function Set-PSRuleGitHubOutput { Write-Warning 'Unable to build url for rule [{0}]' -f $content.RuleName $resourceLink = $content.RuleName } - $passContent += ('| {0} | {1} | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) + $passContent += ('| {0} | `{1}` | {2} | ' -f $resourceLink, $content.TargetName, $content.Synopsis) } $passContent += [System.Collections.ArrayList]@( From 7acd9331c21ae0e3a0df907e706b6146e326cd0f Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 16 Jan 2023 17:51:07 +0100 Subject: [PATCH 132/133] gh env --- .github/workflows/platform.librarycheck.psrule.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/platform.librarycheck.psrule.yml b/.github/workflows/platform.librarycheck.psrule.yml index 0c48998b48..87ab623d96 100644 --- a/.github/workflows/platform.librarycheck.psrule.yml +++ b/.github/workflows/platform.librarycheck.psrule.yml @@ -109,4 +109,5 @@ jobs: Write-Output '::endgroup::' - name: Output to GitHub job summaries if: always() - run: Get-Content '${{ env.modulesPath }}/PSRule-output.md' >> $GITHUB_STEP_SUMMARY + shell: pwsh + run: Get-Content '${{ env.modulesPath }}/PSRule-output.md' >> $env:GITHUB_STEP_SUMMARY From b11de3e42248d37342f540904c3978539ccce3c9 Mon Sep 17 00:00:00 2001 From: Erika Gressi Date: Mon, 16 Jan 2023 18:12:38 +0100 Subject: [PATCH 133/133] removed test branch from trigger --- .github/workflows/platform.librarycheck.psrule.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/platform.librarycheck.psrule.yml b/.github/workflows/platform.librarycheck.psrule.yml index 87ab623d96..20362bef06 100644 --- a/.github/workflows/platform.librarycheck.psrule.yml +++ b/.github/workflows/platform.librarycheck.psrule.yml @@ -4,10 +4,6 @@ on: workflow_dispatch: schedule: - cron: '0 12 * * 0' # Weekly Sunday Analysis - push: - branches: - - users/erikag/psrule-check-onschedule - env: variablesPath: 'settings.yml'