-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Example folder for testing bicep modules with PSRule, documentation, pipeline & Git folder structure example. Fixing linting Adding GH Actions YAML Added GH actions YAML CI file example. Update github-action-psrule-ci.yaml
- Loading branch information
1 parent
0f63f4b
commit c64e896
Showing
10 changed files
with
697 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
bicep-cicd-examples/module-tests-with-psrule/.scripts/bicep-readme.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
#requires -Modules @{ModuleName='PSDocs';ModuleVersion='0.9.0'}, @{ModuleName='PSDocs.Azure';ModuleVersion='0.3.0'} | ||
# Make sure the below modules are installed: | ||
# Install-Module PSDocs | ||
# Install-Module PSDocs.Azure | ||
# If you have done that before, proceed onto the next steps: | ||
# Create a new metadata.json in the relevant modules folder and fill out some details | ||
# Then to generate ReadMe run the below command example BEFORE PR | ||
# .scripts\bicep-readme.ps1 -templatePath file.bicep -Verbose | ||
<# | ||
=============================================== | ||
AUTHOR: Tao Yang | ||
DATE: 02/02/2023 | ||
NAME: generateBicepReadme.ps1 | ||
VERSION: 1.0.0 | ||
COMMENT: Generate Readme.md for Bicep template | ||
=============================================== | ||
#> | ||
|
||
[CmdletBinding()] | ||
Param | ||
( | ||
[Parameter(Mandatory = $true, HelpMessage = "Template Path.")] | ||
[ValidateScript({ test-path $_ -PathType leaf })][String]$templatePath | ||
) | ||
|
||
#region functions | ||
Function ValidateMetadata { | ||
[CmdletBinding()] | ||
[outputType([boolean])] | ||
Param | ||
( | ||
[Parameter(Mandatory = $true)][String]$metadataPath | ||
) | ||
$bValidMetadata = $true | ||
$requiredAttributes = 'itemDisplayName', 'description', 'summary' | ||
If (Test-Path $metadataPath) { | ||
try { | ||
$metadata = Get-Content $metadataPath -Raw | convertFrom-Json | ||
Foreach ($attribute in $requiredAttributes) { | ||
if (!$($metadata.$attribute) -or $($($metadata.$attribute).length) -eq 0) { | ||
Write-Error "Metadata is missing attribute: $attribute or $attribute is empty" | ||
$bValidMetadata = $false | ||
break | ||
} | ||
} | ||
} catch { | ||
$bValidMetadata = $false | ||
} | ||
} else { | ||
$bValidMetadata = $false | ||
} | ||
$bValidMetadata | ||
} | ||
|
||
Function BicepBuild { | ||
[CmdletBinding()] | ||
Param | ||
( | ||
[Parameter(Mandatory = $true)][String]$bicepPath | ||
) | ||
$bicepDir = (Get-Item -Path $bicepPath).DirectoryName; | ||
$bicepFileBaseName = (Get-Item -Path $bicepPath).BaseName | ||
$outputFile = Join-Path $bicepDir "$bicepFileBaseName`.json" | ||
Write-Verbose "Building ARM template $outputFile from Bicep file $bicepPath" | ||
az bicep Build --file $bicepPath --outfile $outputFile | ||
$outputFile | ||
} | ||
#endregion | ||
#region main | ||
$docName = "README" | ||
$currentDir = $PWD | ||
Write-Output "Current working directory '$currentDir'" | ||
$templateDir = (Get-Item -Path $templatePath).DirectoryName; | ||
Write-Verbose "Template Directory '$templateDir'" | ||
set-location $templateDir | ||
Write-Verbose "Detecting git root directory" | ||
$gitRootDir = invoke-expression 'git rev-parse --show-toplevel' -ErrorAction SilentlyContinue | ||
if ($gitRootDir.length -gt 0) { | ||
Write-Verbose "Git root directory '$gitRootDir'" | ||
} else { | ||
Write-Verbose "Not a git repository" | ||
} | ||
set-location $currentDir | ||
Import-Module PSDocs.Azure; | ||
|
||
$metadataPath = Join-Path $templateDir 'metadata.json'; | ||
Write-Verbose "metadata.json file path: '$metadataPath'" | ||
#Validate metadata' | ||
$ValidMetaData = ValidateMetadata -metadataPath $metadataPath | ||
|
||
#Convert Bicep to ARM template if input file is bicep | ||
$removeARM = $false | ||
if ((get-item $templatePath).Extension -ieq '.bicep') { | ||
Write-Verbose "Comping bicep file $templatePath to ARM template" | ||
$tempPath = BicepBuild -bicepPath $templatePath | ||
$removeARM = $true | ||
} else { | ||
$tempPath = $templatePath | ||
} | ||
Write-Verbose "Generating Document for $tempPath" | ||
if ($ValidMetaData) { | ||
Get-AzDocTemplateFile -Path $tempPath | ForEach-Object { | ||
# Generate a standard name of the markdown file. i.e. <name>_<version>.md | ||
$template = Get-Item -Path $_.TemplateFile; | ||
|
||
# Generate markdown | ||
Invoke-PSDocument -Module PSDocs.Azure -OutputPath $templateDir -InputObject $template.FullName -InstanceName $docName -Culture en-GB | ||
|
||
} | ||
} else { | ||
Throw "Invalid metadata in $metadataPath" | ||
Exit -1 | ||
} | ||
$outputFilePath = join-path $templateDir "$docName`.md" | ||
If (Test-Path $outputFilePath) { | ||
if ($gitRootDir.length -gt 0) { | ||
# replace the git root dir with a relative path in generated markdown file | ||
Write-Verbose "replacing git root dir '$gitRootDir' with '.' in generated markdown file '$outputFilePath'" | ||
(Get-Content $outputFilePath -Raw) -replace $gitRootDir, '.' | Set-Content $outputFilePath | ||
} | ||
|
||
Write-Output "Generated document '$outputFilePath'" | ||
} else { | ||
Write-Error "'$outputFilePath' not found" | ||
} | ||
#Remove temp ARM template if required | ||
if ($removeARM) { | ||
Write-Verbose "Remove temp ARM template $tempPath" | ||
Remove-Item -Path $tempPath -Force | ||
} | ||
#endregion |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
# Bicep Test Modules - Folder structure & examples | ||
|
||
## [https://rios.engineer/bicep-modules-with-psrule-testing-documentation-ci-pipeline-examples/](https://rios.engineer/bicep-modules-with-psrule-testing-documentation-ci-pipeline-examples/) | ||
|
||
## Introduction | ||
|
||
In this example you can get some insight on how to: | ||
|
||
- Structure your Git repository FOR Bicep modules | ||
- How to create a 'test' file to scan PSRule with | ||
- How you can create documentation for your Bicep modules | ||
- Why you should consider testing in this method vs scanning your templates directly | ||
|
||
For a deeper dive on some of the above bullet points I would recommend reading the blog post attached. | ||
|
||
## What is PSRule? | ||
|
||
PSRule for Azure helps you identify and remediate issues surrounding Azure best practices in your Azure Bicep code. This results in improved quality of solutions deploy to Azure and better IaC templates that align to the [Well-Architected Framework](https://learn.microsoft.com/en-us/azure/well-architected/). | ||
|
||
## Modules Folder Structure - Example | ||
|
||
The folder structure should resemble the following for the CI pipeline and PSRule to work as intended. With this layout, we can use the `ps-rule.yaml` configuration file to scan only the `*.tests.bicep` files as part of the CI pipeline as the tests file will have the required parameter defined. This results in a more reliable CI experience. | ||
|
||
```json | ||
└── bicep/ | ||
└── modules/ | ||
├── storageAccount/ | ||
│ └── .tests/ | ||
│ └── storageAccount.tests.bicep | ||
├── storage.bicep | ||
└── metadata.json | ||
``` | ||
|
||
## Bicep Modules test file - Example | ||
|
||
The `.tests.bicep` file will have the required parameters defined for PSRule to scan the module code. The module is called in-line from the `\modules` folder in this directory, for example: | ||
|
||
```javascript | ||
// Test with only required parameters | ||
module test_required_params '../storageAccount.bicep' = { | ||
name: 'test_required_params' | ||
params: { | ||
location: 'uksouth' | ||
tags: { | ||
Demo: 'Rios Engineer Blog' | ||
} | ||
storageName: 'riosengineerst001' | ||
storageSkuName: 'Standard_GRS' | ||
storagePleBlobName: 'someBlob' | ||
storagePleFileName: 'someFileshare' | ||
subnetId: 'test' | ||
virtualNetworkId: 'test' | ||
} | ||
} | ||
``` | ||
|
||
## Bicep Module documentation - Example | ||
|
||
The `metadata.json` file is to contain some basic summary information and description of the module. This is so the PowerShell script in `.scripts\` can be ran and uses this metadata to create a `README` file for the module (example README.md output in the `\storageAccount` folder). | ||
|
||
```json | ||
|
||
{ | ||
"itemDisplayName": "Storage Account Module.", | ||
"description": "Storage Account Bicep Module", | ||
"summary": "Storage Account Bicep standard for deployments" | ||
} | ||
|
||
``` | ||
|
||
## Continuous Integration pipeline | ||
|
||
There is an example CI pipeline `azure-devops-psrule-ci.yaml` and `github-actions-psrule-ci.yaml` files which you can review and use as part of your build validation pipelines for either Azure DevOps or GitHub. For ADO once the file is created within the repository, you can use it as the [build pipeline](https://learn.microsoft.com/en-us/azure/devops/repos/git/branch-policies?view=azure-devops&tabs=browser#build-validation). | ||
|
||
For GitHub Actions, you can follow the documentation if you need a step-by-step [here](https://docs.github.com/en/actions/quickstart). | ||
|
||
## PSRule configuration | ||
|
||
Lastly, the `ps-rule.yaml` file defines our PSRule scan configuration. Using this file we can define the tests file path for scanning. | ||
|
||
Snippet taken from the file: | ||
|
||
```yaml | ||
input: | ||
pathIgnore: | ||
# Ignore common files that don't need analysis. | ||
- "**/bicepconfig.json" | ||
- "*.yaml" | ||
- "*.yml" | ||
- "*.md" | ||
- "*.ps1" | ||
|
||
# Exclude Bicep module files. | ||
- "bicep/modules/**/*.bicep" | ||
|
||
# Exclude JSON module files. | ||
- "bicep/modules/**/*.json" | ||
|
||
# Include bicep files from modules. | ||
- "!bicep/modules/**/.tests/*.bicep" | ||
``` | ||
This enables the test files to be scanned for Azure best practice. As the tests file have the required parameters defined PSRule can run the scan against the values. | ||
There is a lot of fantastic documentation around this [here](https://azure.github.io/PSRule.Rules.Azure/setup/configuring-options/). | ||
There is more detailed information in the attached blog post with a full demo on this setup. |
16 changes: 16 additions & 0 deletions
16
bicep-cicd-examples/module-tests-with-psrule/azure-devops-psrule-ci.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
|
||
trigger: none | ||
name: $(SourceBranchName)_$(date:yyyyMMdd)$(rev:.r) | ||
pool: | ||
vmImage: "ubuntu-latest" | ||
|
||
jobs: | ||
- job: 'PSRuleAnalysis' | ||
displayName: "Run PSRule analysis" | ||
steps: | ||
- task: bewhite.ps-rule.assert.ps-rule-assert@2 | ||
inputs: | ||
source: "$(System.DefaultWorkingDirectory)/ps-rule.yaml" | ||
modules: PSRule.Rules.Azure, PSRule.Rules.CAF, PSRule.Rules.Kubernetes | ||
outputFormat: Sarif | ||
outputPath: "reports/ps-rule-results.sarif" |
16 changes: 16 additions & 0 deletions
16
...s/module-tests-with-psrule/bicep/modules/storageAccount/.tests/storageAccount.tests.bicep
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Test with only required parameters | ||
module test_required_params '../storageAccount.bicep' = { | ||
name: 'test_required_params' | ||
params: { | ||
location: 'uksouth' | ||
tags: { | ||
Demo: 'Rios Engineer Blog' | ||
} | ||
storageName: 'riosengineerst001' | ||
storageSkuName: 'Standard_GRS' | ||
storagePleBlobName: 'someBlob' | ||
storagePleFileName: 'someFileshare' | ||
subnetId: 'test' | ||
virtualNetworkId: 'test' | ||
} | ||
} |
Oops, something went wrong.