Bicep modules with PSRule – Testing, documentation, CI Pipeline & examples
Tags: azuredevops, azurebicep, psrule, azure
Published: 2023-10-22 14:17:01

---
title: Bicep modules with PSRule – Testing, documentation, CI Pipeline & examples
published: true
date: 2023-09-11 14:05:06 UTC
tags: AzureDevOps,AzureBicep,PSRule,Azure
canonical_url: https://rios.engineer/bicep-modules-with-psrule-testing-documentation-ci-pipeline-examples/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nyj3am6sg3l1w6lbbzm0.png
---

Testing your Bicep modules with PSRule is a great way to identify that your code is meeting Azure best practice.

But how do you actually test your Bicep modules with PSRule properly?

How can you structure your Git repository?

How can I configure PSRule to test the modules? Can I document what the modules do, and their params?

I’ll go into depth on this below ! 😄

All of the content shared here is uploaded to my GitHub repository which can be found here https://github.com/riosengineer/bicep-module-tests/tree/main.

## The problem

PSRule cannot run reliably against your actual Bicep templates. Errors like _`"The parameter named 'someParam' was not set or a defaultValue was defined."`_ may be familiar if you’ve stumbled across this post.

Why? PSRule needs actual parameter values in order to expand and run the ruleset to determine what could be deployed. The PSRule creator Bernie White explains this well in this GitHub issue: https://github.com/microsoft/PSRule/discussions/1256

With this in mind, it’s best to try and not run PSRule directly against your Bicep templates to get a more reliable CI pipeline experience.

## Repository structure

[Reference Docs](https://azure.github.io/PSRule.Rules.Azure/using-bicep/#testing-bicep-modules)

```
.
└── bicep/
    ├── modules/
    │ ├── storageAccount/
    │ │ ├── storageAccount.bicep
    │ │ ├── metadata.json
    │ │ └── README.md
    │ └── .tests/
    │ └── storageAccount.tests.bicep
    └── prod/
        ├── main.bicep
        └── prd.bicepparam
```

By structuring the repository like the above example we’re going to be able to target PSRule to run against the ‘.tests’ files to get a consistent scan output on our Azure best practices.

This will mean we can get PSRule to scan the module and tell us if there’s any best practice gaps we may want to consider adding to the module.

You can see we have a sub-modules folder for in-line Bicep modules. We also have a separate folder for our actual deployments which reference our modules as seen in the example above. This means we can shift a lot of the scanning onto the tests file and off the templates that may be problematic, this is because the modules will be called by the main templates for deployments.

### Tests file

The test file will comprise of only the required parameters with actual values populated. As mentioned earlier, this will ensure PSRule can fully scan the code and analyse it for us.

The test file needs to be within it’s own sub-folder of the module like the above structure hierarchy.

Here’s an example test Bicep module file:

<svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewbox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewbox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg>
```
// 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

### Bicep Module(s)

The Bicep modules are located within an inline ‘modules’ folder, separating out each module like so:

```
└── bicep/
    └── modules/
        ├── storageAccount/
        ├── storage.bicep
        └── metadata.json

```

### Metadata

A great way to display the parameters of the module, along with all relevant metadata & descriptions is to have a readme present in the module folder. This helps other people understand the module, and it’s parameters. This metadata json file sits within the modules/storageAccount folder (as seen above in the structure).

Here’s an example of what the `metadata.json` file looks like:

<svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewbox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewbox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg>
```
    {
        "itemDisplayName": "Storage Account Module.",
        "description": "Storage Account Bicep Module",
        "summary": "Storage Account Bicep standard for deployments"
    }
```
JSON

With this JSON file we can run an absolutely AWESOME readme script created by Microsoft MVP Tao Yang which auto-generates a markdown file with all the information. Tao’s blog on how this works is here: [Generating README for Bicep Files – Managing Cloud and Datacenter by Tao Yang (tyang.org)](https://blog.tyang.org/2023/02/02/generating-readme-for-bicep/)

I would highly recommend you head over there and check it out.

Here’s a sample output from my example module:

![Bicep readme markdown](https://rios.engineer/wp-content/uploads/2023/09/bicep-readme-616x1024.png)

## Azure DevOps Pipeline

Next is the ADO pipeline, you’ll need to install the [PSRule extension](https://marketplace.visualstudio.com/items?itemName=bewhite.ps-rule) into your Organisation if you haven’t got it already for this to work. Additionally, you’ll likely need a build validation policy so this will trigger on PR automatically. I’ve written a guide about that before [here](https://rios.engineer/the-ultimate-bicep-validation-pipeline-guide-ado-edition/#create-the-azure-devops-build-validation-policy).

In my example Repository in GitHub you can find the structure on all of this so far, and also the pipeline file like so:

[bicep-module-tests/bicep-ci.yaml at main · riosengineer/bicep-module-tests (github.com)](https://github.com/riosengineer/bicep-module-tests/blob/main/bicep-ci.yaml)

<svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewbox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewbox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg>
```
---
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

This file needs to be part of your build validation so you can run PSRule when changes are made to existing modules, or new ones added to the repository.

### PSRule module scan failure

Amending my tests file to have `storageSkuName: 'Standard_LRS' `we can see the pipeline gives us a best practice recommendation from PSRule:

![Bicep module test Azure DevOps failure](https://rios.engineer/wp-content/uploads/2023/09/ps-rule-pipeline-example-failure-1024x739.png)

### PSRule module scan success

However, when changing the value to `storageSkuName: 'Standard_GRS'` we can see the pipeline is happy with the Bicep module:

![Bicep module Azure DevOps pipeline](https://rios.engineer/wp-content/uploads/2023/09/ps-rule-pipeline-example-success-1024x749.png)

## Helpful resources

[Using Bicep source – PSRule for Azure](https://azure.github.io/PSRule.Rules.Azure/using-bicep/#testing-bicep-modules)

[Testing Bicep Code Using PSRule in Azure Pipeline – Managing Cloud and Datacenter by Tao Yang (tyang.org)](https://blog.tyang.org/2022/03/20/azure-pipeline-psrule-bicep-test)

[Azure/PSRule.Rules.Azure-quickstart: Sample code you can use to quickly start using PSRule for Azure. (github.com)](https://github.com/Azure/PSRule.Rules.Azure-quickstart)

The post [Bicep modules with PSRule – Testing, documentation, CI Pipeline & examples](https://rios.engineer/bicep-modules-with-psrule-testing-documentation-ci-pipeline-examples/) appeared first on [Rios Engineer](https://rios.engineer).