Azure Spring Clean: Azure best practice for Bicep with PSRule

Photo of author

Dan Rios

6 min read

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
PowerShell

Create a file ‘ps-rule.yaml‘ and place this example configuration within your root directory, or repository.

Note the modules in the YAML file. We’re able to pull in the Cloud Adoption Framework ruleset to the scans as well as Azure best practices and Kubernetes best practices.
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"]
YAML

Running 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
PowerShell

More 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/
PowerShell

In 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:

PSRule security pillar rule

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'
YAML

Azure 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"
YAML

Output example

ps-rule-ado-output

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

2 thoughts on “Azure Spring Clean: Azure best practice for Bicep with PSRule”

  1. 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,

    Reply

Leave a comment


Skip to content