Custom PSRule rules let you test Azure Bicep modules against your own organisation rules before deployment. This practical guide explains when to use them, how to structure the `.ps-rule` folder, how to configure `ps-rule.yaml`, and how to run the checks in Azure DevOps.
Custom PSRule Rules for Azure Bicep: Shift-Left Policy Checks in Azure DevOps
PSRule for Azure is useful out of the box, but custom rules let you test your own organisation standards before a Bicep module reaches deployment.
This guide shows where custom rules fit, how to structure the `.ps-rule` folder, how to configure Bicep expansion, and how to run the checks in an Azure DevOps pipeline.
Quick answer
Use custom PSRule rules when the built-in Azure best-practice rules are not enough and you need to enforce your own naming, tagging, SKU, location, security or governance standards in the pipeline.
Why use custom PSRule rules?
In a previous article, I covered how to integrate PSRule into an Azure DevOps pipeline to validate Bicep modules against Azure best practices. This follow-up focuses on the next step: adding your own custom checks so the pipeline can catch organisation-specific policy problems before deployment.
PSRule sits in a useful place between a syntax linter and Azure Policy. The Bicep linter helps with Bicep code quality. Azure Policy controls what can exist in Azure. PSRule helps test the expanded resource configuration before deployment, which makes it useful for CI/CD validation and pull request feedback.
| Tool | What it checks | When it runs | Why it matters |
|---|---|---|---|
| Bicep linter | Bicep syntax, style and authoring issues. | During development or build validation. | Good first layer, but it does not fully replace resource configuration checks. |
| PSRule.Rules.Azure | Azure resource configuration against built-in rules and baselines. | Before deployment, usually in CI/CD. | Gives fast feedback on Azure best-practice issues. |
| Custom PSRule rules | Your own organisation rules, such as tags, naming, regions or allowed SKUs. | Before deployment, usually in pull request or build validation. | Moves internal compliance checks left into the engineering workflow. |
| Azure Policy | Runtime Azure governance for deployed or deployable resources. | At deployment time and continuously after deployment. | Still required for real enforcement in Azure. |
Recommended repository structure
The official PSRule for Azure guidance recommends storing standalone custom rules in a root-level `.ps-rule` folder and using file names ending in `.Rule.ps1`, `.Rule.yaml` or `.Rule.jsonc`. This avoids discovery problems in build agents, especially on Linux-based pipelines.
repo-root/
├── modules/
│ ├── storage-account/
│ │ ├── main.bicep
│ │ └── main.test.bicepparam
│ └── app-service/
│ ├── main.bicep
│ └── main.test.bicepparam
├── .ps-rule/
│ └── Org.Azure.Tags.Rule.yaml
├── ps-rule.yaml
└── azure-pipelines.yml
Create a custom PSRule rule
A YAML rule should use the current PSRule resource format: `apiVersion`, `kind`, `metadata` and `spec`. This is cleaner than older simplified examples because it matches the documented PSRule schema.
This example checks that expanded Azure resources have an `Environment` tag. In a real repository, you may restrict the check further by resource type or use selectors to avoid resources that do not support tags.
---
# Synopsis: Require an Environment tag on Azure resources.
apiVersion: github.com/microsoft/PSRule/v1
kind: Rule
metadata:
name: Org.Azure.Tags.Environment
displayName: Require Environment tag
description: Azure resources should include an Environment tag.
spec:
recommend: Add an Environment tag such as dev, test, prod, or shared.
condition:
field: tags.Environment
exists: true
Configure `ps-rule.yaml` for Bicep expansion
Custom rules need PSRule to understand the expanded Azure resources inside your Bicep files. For Bicep source files, enable `AZURE_BICEP_FILE_EXPANSION`. For local standalone rules, configure type binding so rules can match Azure resource objects.
# ps-rule.yaml
configuration:
AZURE_BICEP_FILE_EXPANSION: true
AZURE_PARAMETER_FILE_EXPANSION: true
AZURE_BICEP_FILE_EXPANSION_TIMEOUT: 15
binding:
targetType:
- resourceType
- type
include:
module:
- PSRule.Rules.Azure
- `AZURE_BICEP_FILE_EXPANSION` lets PSRule expand Bicep source files for analysis.
- `AZURE_PARAMETER_FILE_EXPANSION` helps when you use ARM JSON parameter files.
- `AZURE_BICEP_FILE_EXPANSION_TIMEOUT` gives larger Bicep files more time to compile during analysis.
- `binding.targetType` helps standalone custom rules match expanded Azure resource objects.
- `include.module` keeps the built-in Azure rules available alongside your custom rules.
Run PSRule in Azure DevOps
You can run PSRule in Azure DevOps by using the PSRule Azure DevOps extension, or by calling PowerShell directly. The extension provides tasks such as `ps-rule-install@2` and `ps-rule-assert@2`.
Option 1: Azure DevOps task example
steps:
- checkout: self
- task: ps-rule-install@2
displayName: Install PSRule.Rules.Azure
inputs:
module: PSRule.Rules.Azure
- task: ps-rule-assert@2
displayName: Run PSRule for Azure
inputs:
modules: PSRule.Rules.Azure
inputPath: '$(System.DefaultWorkingDirectory)'
option: '$(System.DefaultWorkingDirectory)/ps-rule.yaml'
outputFormat: NUnit3
outputPath: '$(Build.ArtifactStagingDirectory)/ps-rule-results.xml'
continueOnError: true
- task: PublishTestResults@2
displayName: Publish PSRule results
inputs:
testRunner: NUnit
testResultsFiles: '$(Build.ArtifactStagingDirectory)/ps-rule-results.xml'
testRunTitle: PSRule
condition: succeededOrFailed()
Option 2: PowerShell example
If you prefer fewer pipeline-specific dependencies, call PSRule from PowerShell. This is often easier to debug locally because developers can run the same command on their machine.
Install-Module PSRule.Rules.Azure -Scope CurrentUser -Force
Assert-PSRule `
-Path . `
-Module PSRule.Rules.Azure `
-Option ./ps-rule.yaml `
-Format File `
-OutputFormat NUnit3 `
-OutputPath ./reports/ps-rule-results.xml
Useful custom PSRule use cases
Common mistakes and troubleshooting
| Problem | Likely cause | What to check |
|---|---|---|
| Custom rules are ignored | Missing type binding, wrong folder name, wrong file suffix or rules not included. | Use `.ps-rule/`, file names ending in `.Rule.yaml`, and configure `binding.targetType`. |
| Bicep files are not analysed | Bicep expansion is not enabled or the pipeline is not using file format processing. | Enable `AZURE_BICEP_FILE_EXPANSION` and check your task/PowerShell input settings. |
| Rule works locally but not in CI | Path or casing problem on Linux build agents. | Use lower-case `.ps-rule` and avoid hard-coded Windows path assumptions. |
| Too many failures | You enabled too many checks before teams were ready. | Start with warning mode or `continueOnError`, then introduce blocking gates gradually. |
| Developers ignore results | The output is hard to read or not tied to pull requests. | Publish results, keep rules practical, and document how to fix each failure. |
Final thoughts
Custom PSRule rules are useful when your Azure platform standards need to be visible earlier in the development workflow. Instead of waiting for Azure Policy to reject a deployment, you can give engineers a clear signal during pull request validation.
The best implementation is gradual. Start with built-in `PSRule.Rules.Azure` rules, then add a small set of custom rules for the highest-value checks: tags, allowed regions, public access and naming. Once teams trust the feedback, you can make the pipeline stricter.
FAQ
Do custom PSRule rules replace Azure Policy?
No. Custom PSRule rules are useful for early feedback in CI/CD, but Azure Policy should still be used for enforcement and ongoing compliance inside Azure.
Where should custom PSRule rules be stored?
For standalone repository rules, use a root-level `.ps-rule` folder and rule files ending in `.Rule.ps1`, `.Rule.yaml` or `.Rule.jsonc`.
Why are my custom PSRule rules not running?
The common causes are missing `binding.targetType`, incorrect rule file naming, wrong folder casing, Bicep expansion not enabled, or the rule module/path not being included in the pipeline.
Should PSRule fail the pipeline immediately?
Usually not at the start. Begin by publishing results and allowing the pipeline to continue. Once the team understands the failures and fixes, move important rules to a blocking quality gate.
Can PSRule scan Bicep parameter files?
Yes, PSRule for Azure supports expansion options for Bicep source files and parameter files. Configure expansion in `ps-rule.yaml` and test the exact behaviour in your repository structure.
Useful references
- PSRule for Azure: Using custom rules
- PSRule for Azure: Configuring expansion
- PSRule for Azure: Creating your pipeline
- PSRule Azure DevOps extension
- Enhancing Bicep Module Testing with PSRule
- How to Create and Test Bicep Modules for Azure Bicep Registry
Note: PSRule syntax and Azure DevOps task inputs can change. Always confirm against the current PSRule and PSRule.Rules.Azure documentation before using this in a production pipeline.
