diff --git a/README.md b/README.md index f0d87a9..342605a 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ We aim to do this by creating real example templates that are easy to digest and If you find this repository useful, please save the repository by hitting the ⭐ [![MegaLinter](https://github.com/riosengineer/Bicepify/actions/workflows/mega-linter.yml/badge.svg)](https://github.com/riosengineer/Bicepify/actions/workflows/mega-linter.yml) ![Contributors](https://img.shields.io/github/contributors/RiosEngineer/Bicepify?color=dark-green) ![GitHub Repo stars](https://img.shields.io/github/stars/riosengineer/bicepify) -![Static Badge](https://img.shields.io/badge/Bicep-Learning-blue?logo=microsoftazure&color=%230078D4) ![Static Badge](https://img.shields.io/badge/Bicep-DevOps-blue?logo=azuredevops&color=%230078D4) +![Static Badge](https://img.shields.io/badge/Bicep-Learning-blue?logo=microsoftazure&color=%230078D4&link=https%3A%2F%2Fgithub.com%2Friosengineer%2FBicepify%2Ftree%2Fmain%2Fbicep-examples) ![Static Badge](https://img.shields.io/badge/Bicep-Azure_DevOps-blue?logo=azuredevops&color=%230078D4&link=https%3A%2F%2Fgithub.com%2Friosengineer%2FBicepify%2Ftree%2Fmain%2Fbicep-cicd-examples) ![Static Badge](https://img.shields.io/badge/Bicep-GitHub%20Actions-blue?logo=GitHubActions&logoColor=white&color=%230078D4&link=https%3A%2F%2Fgithub.com%2Friosengineer%2FBicepify%2Ftree%2Fmain%2Fbicep-cicd-examples) + ## 🧬 New to Bicep? Getting started @@ -43,6 +44,8 @@ Where possible, examples will be complimented by a blog post giving a deeper div ## 📎 Useful links +[Rios Engineer - All Things Azure blog](https://rios.engineer/) + [What is Bicep](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep) [Bicep tools](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/install) diff --git a/bicep-cicd-examples/module-tests-with-psrule/.scripts/bicep-readme.ps1 b/bicep-cicd-examples/module-tests-with-psrule/.scripts/bicep-readme.ps1 new file mode 100644 index 0000000..b34498d --- /dev/null +++ b/bicep-cicd-examples/module-tests-with-psrule/.scripts/bicep-readme.ps1 @@ -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. _.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 \ No newline at end of file diff --git a/bicep-cicd-examples/module-tests-with-psrule/README.md b/bicep-cicd-examples/module-tests-with-psrule/README.md new file mode 100644 index 0000000..a3fdeb7 --- /dev/null +++ b/bicep-cicd-examples/module-tests-with-psrule/README.md @@ -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. diff --git a/bicep-cicd-examples/module-tests-with-psrule/azure-devops-psrule-ci.yaml b/bicep-cicd-examples/module-tests-with-psrule/azure-devops-psrule-ci.yaml new file mode 100644 index 0000000..ee47606 --- /dev/null +++ b/bicep-cicd-examples/module-tests-with-psrule/azure-devops-psrule-ci.yaml @@ -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" diff --git a/bicep-cicd-examples/module-tests-with-psrule/bicep/modules/storageAccount/.tests/storageAccount.tests.bicep b/bicep-cicd-examples/module-tests-with-psrule/bicep/modules/storageAccount/.tests/storageAccount.tests.bicep new file mode 100644 index 0000000..1c28388 --- /dev/null +++ b/bicep-cicd-examples/module-tests-with-psrule/bicep/modules/storageAccount/.tests/storageAccount.tests.bicep @@ -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' + } +} diff --git a/bicep-cicd-examples/module-tests-with-psrule/bicep/modules/storageAccount/README.md b/bicep-cicd-examples/module-tests-with-psrule/bicep/modules/storageAccount/README.md new file mode 100644 index 0000000..f848a43 --- /dev/null +++ b/bicep-cicd-examples/module-tests-with-psrule/bicep/modules/storageAccount/README.md @@ -0,0 +1,114 @@ +# Storage Account Module + +Storage Account Bicep standard for deployments + +Storage Account Bicep Module + +## Parameters + +Parameter name | Required | Description +-------------- | -------- | ----------- +location | Yes | Azure region of the deployment +tags | Yes | Tags to add to the resources +storageName | Yes | Name of the storage account +storagePleBlobName | Yes | Name of the storage blob private link endpoint +storagePleFileName | Yes | Name of the storage file private link endpoint +subnetId | Yes | Resource ID of the subnet +virtualNetworkId | Yes | Resource ID of the virtual network +storageSkuName | Yes | Storage SKU + +### location + +![Parameter Setting](https://img.shields.io/badge/parameter-required-orange?style=flat-square) + +Azure region of the deployment + +### tags + +![Parameter Setting](https://img.shields.io/badge/parameter-required-orange?style=flat-square) + +Tags to add to the resources + +### storageName + +![Parameter Setting](https://img.shields.io/badge/parameter-required-orange?style=flat-square) + +Name of the storage account + +### storagePleBlobName + +![Parameter Setting](https://img.shields.io/badge/parameter-required-orange?style=flat-square) + +Name of the storage blob private link endpoint + +### storagePleFileName + +![Parameter Setting](https://img.shields.io/badge/parameter-required-orange?style=flat-square) + +Name of the storage file private link endpoint + +### subnetId + +![Parameter Setting](https://img.shields.io/badge/parameter-required-orange?style=flat-square) + +Resource ID of the subnet + +### virtualNetworkId + +![Parameter Setting](https://img.shields.io/badge/parameter-required-orange?style=flat-square) + +Resource ID of the virtual network + +### storageSkuName + +![Parameter Setting](https://img.shields.io/badge/parameter-required-orange?style=flat-square) + +Storage SKU + +- Allowed values: `Standard_LRS`, `Standard_ZRS`, `Standard_GRS`, `Standard_GZRS`, `Standard_RAGRS`, `Standard_RAGZRS`, `Premium_LRS`, `Premium_ZRS` + +## Outputs + +Name | Type | Description +---- | ---- | ----------- +storageId | string | + +## Snippets + +### Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "template": "bicep/modules/storageAccount/storageAccount.json" + }, + "parameters": { + "location": { + "value": "" + }, + "tags": { + "value": {} + }, + "storageName": { + "value": "" + }, + "storagePleBlobName": { + "value": "" + }, + "storagePleFileName": { + "value": "" + }, + "subnetId": { + "value": "" + }, + "virtualNetworkId": { + "value": "" + }, + "storageSkuName": { + "value": "" + } + } +} +``` diff --git a/bicep-cicd-examples/module-tests-with-psrule/bicep/modules/storageAccount/metadata.json b/bicep-cicd-examples/module-tests-with-psrule/bicep/modules/storageAccount/metadata.json new file mode 100644 index 0000000..2f0a9ef --- /dev/null +++ b/bicep-cicd-examples/module-tests-with-psrule/bicep/modules/storageAccount/metadata.json @@ -0,0 +1,5 @@ +{ + "itemDisplayName": "Storage Account Module.", + "description": "Storage Account Bicep Module", + "summary": "Storage Account Bicep standard for deployments" +} \ No newline at end of file diff --git a/bicep-cicd-examples/module-tests-with-psrule/bicep/modules/storageAccount/storageAccount.bicep b/bicep-cicd-examples/module-tests-with-psrule/bicep/modules/storageAccount/storageAccount.bicep new file mode 100644 index 0000000..9508229 --- /dev/null +++ b/bicep-cicd-examples/module-tests-with-psrule/bicep/modules/storageAccount/storageAccount.bicep @@ -0,0 +1,224 @@ +targetScope = 'resourceGroup' + +// Creates a storage account, private endpoints and DNS zones +@description('Azure region of the deployment') +param location string + +@description('Tags to add to the resources') +param tags object + +@description('Name of the storage account') +param storageName string + +@description('Name of the storage blob private link endpoint') +param storagePleBlobName string + +@description('Name of the storage file private link endpoint') +param storagePleFileName string + +@description('Resource ID of the subnet') +param subnetId string + +@description('Resource ID of the virtual network') +param virtualNetworkId string + +@allowed([ + 'Standard_LRS' + 'Standard_ZRS' + 'Standard_GRS' + 'Standard_GZRS' + 'Standard_RAGRS' + 'Standard_RAGZRS' + 'Premium_LRS' + 'Premium_ZRS' +]) + +@description('Storage SKU') +param storageSkuName string + +var storageNameCleaned = replace(storageName, '-', '') + +var blobPrivateDnsZoneName = 'privatelink.blob.${environment().suffixes.storage}' + +var filePrivateDnsZoneName = 'privatelink.file.${environment().suffixes.storage}' + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' = { + name: storageNameCleaned + location: location + tags: tags + sku: { + name: storageSkuName + } + kind: 'StorageV2' + properties: { + accessTier: 'Hot' + allowBlobPublicAccess: false + allowCrossTenantReplication: false + allowSharedKeyAccess: true + encryption: { + keySource: 'Microsoft.Storage' + requireInfrastructureEncryption: false + services: { + blob: { + enabled: true + keyType: 'Account' + } + file: { + enabled: true + keyType: 'Account' + } + queue: { + enabled: true + keyType: 'Service' + } + table: { + enabled: true + keyType: 'Service' + } + } + } + isHnsEnabled: false + isNfsV3Enabled: false + keyPolicy: { + keyExpirationPeriodInDays: 7 + } + largeFileSharesState: 'Disabled' + minimumTlsVersion: 'TLS1_2' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + } + supportsHttpsTrafficOnly: true + } +} + +resource storage_blob 'Microsoft.Storage/storageAccounts/blobServices@2021-04-01' = { + name: 'default' + parent: storage + properties: { + deleteRetentionPolicy: { + enabled: true + days: 7 + } + containerDeleteRetentionPolicy: { + enabled: true + days: 7 + } + } +} + +resource storagePrivateEndpointBlob 'Microsoft.Network/privateEndpoints@2022-01-01' = { + name: storagePleBlobName + location: location + tags: tags + properties: { + privateLinkServiceConnections: [ + { + name: storagePleBlobName + properties: { + groupIds: [ + 'blob' + ] + privateLinkServiceId: storage.id + privateLinkServiceConnectionState: { + status: 'Approved' + description: 'Auto-Approved' + actionsRequired: 'None' + } + } + } + ] + subnet: { + id: subnetId + } + } +} + +resource storagePrivateEndpointFile 'Microsoft.Network/privateEndpoints@2022-01-01' = { + name: storagePleFileName + location: location + tags: tags + properties: { + privateLinkServiceConnections: [ + { + name: storagePleFileName + properties: { + groupIds: [ + 'file' + ] + privateLinkServiceId: storage.id + privateLinkServiceConnectionState: { + status: 'Approved' + description: 'Auto-Approved' + actionsRequired: 'None' + } + } + } + ] + subnet: { + id: subnetId + } + } +} + +resource blobPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: blobPrivateDnsZoneName + location: 'global' +} + +resource privateEndpointDns 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-01-01' = { + name: '${storagePrivateEndpointBlob.name}/blob-PrivateDnsZoneGroup' + properties:{ + privateDnsZoneConfigs: [ + { + name: blobPrivateDnsZoneName + properties:{ + privateDnsZoneId: blobPrivateDnsZone.id + } + } + ] + } +} + +resource blobPrivateDnsZoneVnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + name: '${blobPrivateDnsZone.name}/${uniqueString(storage.id)}' + location: 'global' + properties: { + registrationEnabled: false + virtualNetwork: { + id: virtualNetworkId + } + } +} + +resource filePrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: filePrivateDnsZoneName + location: 'global' +} + +resource filePrivateEndpointDns 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-01-01' = { + name: '${storagePrivateEndpointFile.name}/flie-PrivateDnsZoneGroup' + properties:{ + privateDnsZoneConfigs: [ + { + name: filePrivateDnsZoneName + properties:{ + privateDnsZoneId: filePrivateDnsZone.id + } + } + ] + } +} + +resource filePrivateDnsZoneVnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + name: '${filePrivateDnsZone.name}/${uniqueString(storage.id)}' + location: 'global' + properties: { + registrationEnabled: false + virtualNetwork: { + id: virtualNetworkId + } + } +} + +output storageId string = storage.id diff --git a/bicep-cicd-examples/module-tests-with-psrule/github-action-psrule-ci.yaml b/bicep-cicd-examples/module-tests-with-psrule/github-action-psrule-ci.yaml new file mode 100644 index 0000000..dc4f9cb --- /dev/null +++ b/bicep-cicd-examples/module-tests-with-psrule/github-action-psrule-ci.yaml @@ -0,0 +1,39 @@ +# +# 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/checkout@v2.3.5 + + - name: Run PSRule analysis + + uses: Microsoft/ps-rule@v1.9.0 + with: + modules: 'PSRule.Rules.Azure' diff --git a/bicep-cicd-examples/module-tests-with-psrule/ps-rule.yaml b/bicep-cicd-examples/module-tests-with-psrule/ps-rule.yaml new file mode 100644 index 0000000..3c5460e --- /dev/null +++ b/bicep-cicd-examples/module-tests-with-psrule/ps-rule.yaml @@ -0,0 +1,41 @@ +configuration: + # Enable automatic expansion of bicep source files. + AZURE_BICEP_FILE_EXPANSION: true + # Enable automatic expansion of param files + AZURE_PARAMETER_FILE_EXPANSION: true + +execution: + # Ignore warnings for resources and objects that don't have any rules. + unprocessedObject: Ignore + +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" + +include: + module: + - PSRule.Rules.Azure + - PSRule.Rules.CAF + - PSRule.Rules.Kubernetes + +rule: + exclude: + # Ignore the following rules for all objects + - Azure.Resource.UseTags + +output: + culture: ["en-GB"]