Skip to content

Commit

Permalink
feat: Added post-deployment test support (#1036)
Browse files Browse the repository at this point in the history
## Description

> **Note:** Test file changes for key vault are just for illustration
purposes

- Introduced 2 additional steps to deploy action that will look for
Pester test files anywhere in a e2e test's folder (or nested folder, if
desired), run the contained tests and publish the results
- If no test was found, the publish results step will be skipped
- By default, the action will pass in one parameter to the test files
that MUST be implemented: `[hashtable] $TestInputData`. This parameter
will automatically contain the test file outputs + the path to the test
file. For example
  ```json
  {
    "TestInputData": {
"ModuleTestFolderPath":
"/home/runner/work/bicep-registry-modules/bicep-registry-modules/avm/res/key-vault/vault/tests/e2e/private-endpoint",
      "DeploymentOutputs": {
        "resourceId": {
          "type": "string",
          "value": "/subscriptions/1111111/resourcegroups/(...)"
        } 
      }
    }
  }
  ```
- Example
[run](https://github.com/AlexanderSehr/bicep-registry-modules/actions/runs/7944560531/job/21690223560)

| Pipeline |
| - |
|
[![avm.res.key-vault.vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml/badge.svg?branch=users%2Falsehr%2FpostDeploymentTest&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml)
|

---------

Co-authored-by: Erika Gressi <[email protected]>
Co-authored-by: ChrisSidebotham-MSFT <[email protected]>
  • Loading branch information
3 people authored Mar 5, 2024
1 parent 14ed0f1 commit 5b2380a
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 10 deletions.
98 changes: 91 additions & 7 deletions .github/actions/templates/avm-validateModuleDeployment/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,9 @@ runs:
Write-Output ('{0}={1}' -f 'deploymentNames', ($res.deploymentNames | ConvertTo-Json -Compress)) >> $env:GITHUB_OUTPUT
# Populate further outputs
$deploymentOutputHashTable = @{}
$deploymentOutput = $res.deploymentOutput | ConvertTo-Json -Depth 99 -Compress
Write-Output ('{0}={1}' -f 'deploymentOutput', $deploymentOutput) >> $env:GITHUB_OUTPUT
foreach ($outputKey in $res.deploymentOutput.Keys) {
Write-Output ('{0}={1}' -f 'outputKey', $res.deploymentOutput[$outputKey].Value) >> $env:GITHUB_OUTPUT
$deploymentOutputHashTable.add($outputKey, $res.deploymentOutput[$outputKey].Value)
}
$deploymentOutput = $deploymentOutputHashTable | ConvertTo-Json -Compress -Depth 100
Write-Verbose "Deployment output: $deploymentOutput" -Verbose
if ($res.ContainsKey('exception')) {
Expand All @@ -281,6 +276,95 @@ runs:
Write-Output '::endgroup::'
# [Post-Deployment test] task(s)
# ------------------------------
- name: "Run post-deployment Pester tests"
id: pester_run_step
shell: pwsh
run: |
# Grouping task logs
Write-Output '::group::Run Pester tests'
# Load used functions
. (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'pipelines' 'staticValidation' 'compliance' 'Set-PesterGitHubOutput.ps1')
# Set repo root path
$repoRootPath = $env:GITHUB_WORKSPACE
$moduleTestFilePath = Join-Path $env:GITHUB_WORKSPACE '${{ inputs.templateFilePath }}'
$moduleTestFolderPath = Split-Path $moduleTestFilePath
$moduleTestFolderName = Split-Path $moduleTestFolderPath -Leaf
$deploymentOutputs = '${{ steps.deploy_step.outputs.deploymentOutput }}' | ConvertFrom-Json -AsHashTable
# --------------------- #
# Invoke Pester test(s) #
# --------------------- #
$pesterConfiguration = @{
Run = @{
Container = New-PesterContainer -Path $moduleTestFolderPath -Data @{
TestInputData = @{
DeploymentOutputs = $deploymentOutputs # Passing in in case we want to directly validate data via the deployment's output
ModuleTestFolderPath = $moduleTestFolderPath # Passing in in case we want to access any data in the test folder / files
}
}
PassThru = $true
}
Output = @{
Verbosity = 'Detailed'
}
}
Write-Verbose 'Invoke test with' -Verbose
$foundTestPaths = $pesterConfiguration.Run.Container | Where-Object { -not [String]::IsNullOrEmpty($_.Item.FullName) } | Foreach-Object { ($_.Item.FullName -split '[\\|\/]tests[\\|\/]e2e[\\|\/]')[1] }
if($foundTestPaths.Count -gt 0) {
Write-Verbose ('Path(s): {0}' -f ($foundTestPaths | Convertto-Json)) -Verbose
Write-Verbose ('Data: {0}' -f ($pesterConfiguration.Run.Container.Data[0] | ConvertTo-Json -Depth 3)) -Verbose
$testResults = Invoke-Pester -Configuration $pesterConfiguration
# ----------------------------------------- #
# Create formatted Pester Test Results File #
# ----------------------------------------- #
$functionInput = @{
PesterTestResults = $testResults
OutputFilePath = Join-Path $env:GITHUB_WORKSPACE 'avm' "$moduleTestFolderName-Pester-output.md"
GitHubRepository = $env:GITHUB_REPOSITORY
BranchName = $env:GITHUB_REF
Title = 'Pester post-deployment validation summary'
}
Write-Verbose 'Invoke Pester formatting function with' -Verbose
Write-Verbose ($functionInput | ConvertTo-Json -Depth 0 | Out-String) -Verbose
Set-PesterGitHubOutput @functionInput -Verbose
Write-Output ('{0}={1}' -f 'formattedPesterResultsPath', $functionInput.outputFilePath) >> $env:GITHUB_OUTPUT
} else {
Write-Verbose 'Found no Pester test files (*.test.ps1) in test folder' -Verbose
Write-Output ('{0}={1}' -f 'formattedPesterResultsPath', '') >> $env:GITHUB_OUTPUT
}
- name: "Output to GitHub job summaries"
if: steps.pester_run_step.outputs.formattedPesterResultsPath != ''
shell: pwsh
run: |
# Grouping task logs
Write-Output '::group::Output to GitHub job summaries'
$mdPesterOutputFilePath = '${{ steps.pester_run_step.outputs.formattedPesterResultsPath }}'
if (-not (Test-Path $mdPesterOutputFilePath)) {
Write-Warning ('Input file [{0}] not found. Please check if the previous task threw an error and try again.' -f $mdPesterOutputFilePath)
} else {
Get-Content $mdPesterOutputFilePath >> $env:GITHUB_STEP_SUMMARY
Write-Verbose ('Successfully printed out file [{0}] to Job Summaries' -f $mdPesterOutputFilePath) -Verbose
}
Write-Output '::endgroup::'
# [Deployment removal] task(s)
# ----------------------------
- name: "Remove deployed resources"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,5 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem'
enablePurgeProtection: false
}
}]

output resourceId string = testDeployment[1].outputs.resourceId
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
######################################
## Additional post-deployment tests ##
######################################
##
## You can add any custom post-deployment validation tests you want here, or add them spread accross multiple test files in the test case folder.
##
###########################

param (
[Parameter(Mandatory = $false)]
[hashtable] $TestInputData = @{}
)

Describe 'Validate private endpoint deployment' {

Context 'Validate sucessful deployment' {

It "Private endpoints should be deployed in resource group" {

$keyVaultResourceId = $TestInputData.DeploymentOutputs.resourceId.Value
$testResourceGroup = ($keyVaultResourceId -split '\/')[4]
$deployedPrivateEndpoints = Get-AzPrivateEndpoint -ResourceGroupName $testResourceGroup
$deployedPrivateEndpoints.Count | Should -BeGreaterThan 0
}

It 'Private endpoint should have role assignment' {

$keyVaultResourceId = $TestInputData.DeploymentOutputs.resourceId.Value
$testResourceGroup = ($keyVaultResourceId -split '\/')[4]
$deployedPrivateEndpoints = Get-AzPrivateEndpoint -ResourceGroupName $testResourceGroup

$firstPrivateEndpointResourceId = $deployedPrivateEndpoints[0].Id
$firstPrivateEndpointName = ($firstPrivateEndpointResourceId -split '\/')[-1]

$roleAssignments = Get-AzRoleAssignment -ResourceName $firstPrivateEndpointName -ResourceType 'Microsoft.Network/privateEndpoints' -ResourceGroupName $testResourceGroup
$roleAssignments.Count | Should -BeGreaterThan 0
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
######################################
## Additional post-deployment tests ##
######################################
##
## You can add any custom post-deployment validation tests you want here, or add them spread accross multiple test files in the test case folder.
##
###########################

param (
[Parameter(Mandatory = $false)]
[hashtable] $TestInputData = @{}
)

Describe 'Validate Key Vault' {

It 'Public endpoint should be disabled' {

$keyVaultResourceId = $TestInputData.DeploymentOutputs.resourceId.Value

$deployedResource = Get-AzResource -ResourceId $keyVaultResourceId

$deployedResource.Properties.publicNetworkAccess | Should -Be 'Disabled'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ For example: 'Azure/ResourceModules'
Optional. The branch the pipeline was triggered from. If provided it will be used to generate a URL to the exact line of the test.
For example: 'users/carml/testBranch'
.PARAMETER Title
Optional. The title / header the exported markdown should have. For example: 'Post-deployment test validation summary'
.EXAMPLE
Set-PesterGitHubOutput -PesterTestResults @{...}
Expand All @@ -75,7 +78,10 @@ function Set-PesterGitHubOutput {
[string] $GitHubRepository,

[Parameter(Mandatory = $false)]
[string] $BranchName
[string] $BranchName,

[Parameter(Mandatory = $false)]
[string] $Title = 'Pester validation summary'
)

$passedTests = $PesterTestResults.Passed
Expand All @@ -94,7 +100,7 @@ function Set-PesterGitHubOutput {

# Header
$fileContent = [System.Collections.ArrayList]@(
'# Pester validation summary ',
"# $Title ",
''
)

Expand All @@ -118,7 +124,6 @@ function Set-PesterGitHubOutput {
)

if ($failedTests.Count -gt 0) {
Write-Verbose 'Adding failed tests'
$fileContent += [System.Collections.ArrayList]@(
'| Name | Error | Source |',
'| :-- | :-- | :-- |'
Expand Down

0 comments on commit 5b2380a

Please sign in to comment.