This is my first time getting involved with a great initiative called Azure Spring Clean by Thomas Thornton & Joe Carlyle – which I’m excited to be apart of!
For those unaware, the event promotes and encourages well managed Azure tenants. This is done yearly in March where across 5 days there are 5 different base topics, of which different content is published. You can read and find out more here.
In this post I’ll detail how an awesome tool called PSRule, created by Microsoft’s Bernie White can be used to guard against Azure Bicep misconfigurations, Azure best practice and align to the pillars of the Microsoft Well-Architected & Cloud Adoption Frameworks.
What’s PSRule?
PSRule for Azure is an open source project that features a curated list of pre-built tests with complimentary documentation that help us configure Azure resources.
The tests allow us to scan and check our Azure Bicep template before we deploy to ensure they are following Azure best practices, including the Cloud Adoption Framework (CAF) and Well-Architected Framework (WAF) alignment. In addition,
PSRule for Azure features hundreds of rules for various resources and their configurations which can be found here. In addition, direct integration to your DevOps processes using GitHub or Azure DevOps with minimal configuration.
PSRule offers a ton of flexibility with the base YAML configuration file so we are able to ignore specific rules, files, folders and much more.
Getting started – Azure best practice with Bicep
Local scan
We can install PSRule locally via the PowerShell gallery, alternatively if you use VSCode there is an extension which can be found here which can be used via this guide.
A local scan can be a great way to check your templates without waiting for continuous integration pipelines to tell you what’s wrong, therefore shifting left the check for quicker identification and remediation. Alternatively if your team is small and still trying to adopt CI/CD and DevOps practices then this is a good mode 1 to grow from.
Install
Install-Module -Name 'PSRule' -Repository PSGallery -Scope CurrentUser
Install-Module -Name 'PSRule.Rules.Azure' -Repository PSGallery -Scope CurrentUser -Force
winget install -e --id Microsoft.Bicep
PowerShellCreate a file ‘ps-rule.yaml‘ and place this example configuration within your root directory, or repository.
configuration:
# Enable automatic expansion of bicep source files.
AZURE_BICEP_FILE_EXPANSION: true
# Enable automatic expansion of param files
AZURE_PARAMETER_FILE_EXPANSION: true
# Set timeout for expanding Bicep source files.
AZURE_BICEP_FILE_EXPANSION_TIMEOUT: 15
execution:
# Ignore warnings for resources and objects that don't have any rules.
unprocessedObject: Ignore
ruleSuppressed: Ignore
input:
pathIgnore:
# Ignore common files that don't need analysis.
- "**/bicepconfig.json"
- "*.md"
- "*.ps1"
include:
module:
- PSRule.Rules.Azure
- PSRule.Rules.CAF
- PSRule.Rules.Kubernetes
rule:
exclude:
# Ignore the following rules for all objects
- Azure.Resource.UseTags
- Azure.ACR.MinSku
output:
culture: ["en-GB"]
YAMLRunning the scan
We have a two main options to run the scan, with a detailed output or a summary. We can specify the input path to our bicep file we want to scan.
Invoke method:
Invoke-PSRule -InputPath .\main.bicep -As Detail
Invoke-PSRule -InputPath .\main.bicep -As Summary
or Assert method:
Assert-PSRule -InputPath .\main.bicep -As Detail
Assert-PSRule -InputPath .\main.bicep -As Summary
PowerShellMore info on these two methods and their inputs here, they are similar features however.
Output example
~ Assert-PSRule -InputPath .\main.bicep -As Detail
____ _____ ____ __
/ __ \/ ___// __ \__ __/ /__
/ /_/ /\__ \/ /_/ / / / / / _ \
/ ____/___/ / _, _/ /_/ / / __/
/_/ /____/_/ |_|\__,_/_/\___/
Using PSRule v2.9.0
Using PSRule.Rules.Azure v1.32.1
----------------------------
Explore documentation: https://aka.ms/ps-rule
Contribute and find source: https://github.com/microsoft/PSRule
Report issues: https://github.com/microsoft/PSRule/issues
PSRule.Rules.Azure: https://aka.ms/ps-rule-azure
----------------------------
-> appinsights-name : Microsoft.Insights/components [3/3]
[PASS] Azure.Resource.AllowedRegions (AZR-000167)
[PASS] Azure.AppInsights.Workspace (AZR-000069)
[PASS] Azure.AppInsights.Name (AZR-000070)
-> 864e505983a1f-webVnet : Microsoft.Resources/deployments [7/7]
[PASS] Azure.Deployment.OutputSecretValue (AZR-000279)
[PASS] Azure.Deployment.AdminUsername (AZR-000284)
[PASS] Azure.Deployment.SecureParameter (AZR-000408)
[PASS] Azure.Deployment.SecureValue (AZR-000316)
[PASS] Azure.Template.ParameterStrongType (AZR-000227)
[PASS] Azure.Template.ExpressionLength (AZR-000228)
[PASS] Azure.Deployment.Name (AZR-000359)
-> webapp-vnet : Microsoft.Network/virtualNetworks [4/5]
[PASS] Azure.Resource.AllowedRegions (AZR-000167)
[FAIL] Azure.VNET.UseNSGs (AZR-000263)
| Template: main.bicep:522:13
| RECOMMEND:
| Consider assigning a network security group (NSG) to each virtual network
| subnet.
| REASON:
| - The subnet (webapp-snet) has no NSG associated.
| HELP:
| - https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.VNET.UseNSGs/
PowerShellIn the output, we can see the PSRule scan has highlighted that the virtual network has no network security group associated to it. It then provides a reason and a help link directly to the PSRule rule.
The awesome part about the scan output here is that not only do we get a nice report on an Azure recommendation, but the documentation links directly to how we can remediate this particular suggestion – if we wish.
The help URL provides an Azure Bicep snippet that will enable this rule to pass on the next run, giving us a really simple way to retrospectively go back and amend our Azure Bicep template to correct any security, compliance or best practice configurations that were missed.
On the help documentation it’ll show us the Well-Architected Framework pillar alignment that this particular rule is referencing:
You can view all rules by pillar from here.
Pipeline integrations
Similarly, it’s super simple to integrate to your Azure DevOps pipeline or GitHub Actions. Making sure we have the ps-rule.yaml file in the repository root.
GitHub Action
If you’re unfamiliar with how to setup a GitHub Action, all you’ll need to do is create a file within .github/workflows like psrule-pipeline.yaml and paste the below YAML, saving this.
#
# Analyze repository with PSRule for Azure
#
# For PSRule for Azure documentation see:
# https://aka.ms/ps-rule-azure
# For action details see:
# https://aka.ms/ps-rule-action
name: PSRule analysis
# Run for main or PRs against main
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
analyze:
name: Analyze repository
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/[email protected]
- name: Run PSRule analysis
uses: Microsoft/[email protected]
with:
modules: 'PSRule.Rules.Azure, PSRule.Rules.CAF, PSRule.Rules.Kubernetes'
YAMLAzure DevOps Pipeline
To set this up, create a file within your root Azure DevOps GIT repository like psrule-pipeline.yaml and paste the code in, committing to the repository.
Once committed, you can setup a build policy so all code being merged into the main branch can get scanned. I have blogged a guide on how to this: The ultimate Bicep validation pipeline guide: ADO Edition – Rios Engineer which will go into much more detail than is in scope for this article.
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"
YAMLOutput example
Summarised – Main benefits of using PSRule with Azure Bicep
✅ Framework alignments for both WAF & CAF
✅ Azure best practices rules
✅ Helps guard against misconfigurations or user error when creating Bicep templates (e.g. forgot to associate NSG to a subnet)
✅ Enables consistent configuration for your Bicep deployments
✅ Can be used for Azure Policy as well
Quick start lab – GitHub Repository Example
I’ve put together an extensive GitHub repository that details a full repository structure, with examples and YAML files for both Azure DevOps and GitHub for you to take and lab or use as a base to start:
Bicepify/bicep-cicd-examples/module-tests-with-psrule at main · riosengineer/Bicepify (github.com)
I’ve also previously written other blogs that cover some more in-depth guides on these subjects which may be of interest:
Bicep modules with PSRule – Testing, documentation, CI Pipeline & examples – Rios Engineer
The ultimate Bicep validation pipeline guide: ADO Edition – Rios Engineer
Hey! Great post. Did you ever get to work PSRule against your own Azure Policy (Custom ruleset)? If yes, would you mind sharing how you were able to get it done?
Ive been trying to work on testing my bicep against my Azure Policy (custom policies) so that we can pre-test before deploying the bicep modules to the environment.
Thanks in advance,
Hey,
I didn’t test it fully with Azure Policy yet. But I have used some custom rule sets before so the concept should be the same.
Is there a particular area that is troublesome or causing errors?
If you want some references the ALZ Bicep repository on GitHub has some custom rule sets that may help you see how things plug together? https://github.com/Azure/ALZ-Bicep/tree/main/.ps-rule
You have to create a .ps-rule folder in the repo root and put all rules in here (also check out https://azure.github.io/PSRule.Rules.Azure/customization/using-custom-rules/ if you haven’t seen this already).