We love automation! With Bicep, you can effortlessly define the Azure infrastructure you need for your application. And with the Azure Developer CLI (azd), you can easily deploy both infrastructure and application code at the same time. Microsoft provides standard reusable components to be used with the Azure Developer CLI, such as Azure Verified Modules and a template gallery, containing example applications that you can use as starting points for defining your infrastructure.
While this approach allows you and your team to deploy applications quickly and consistently, you might not necessarily be a security expert. So, how can you be sure that your Bicep templates, or any sample templates you use as a starting point, are following best practices? There are lots of different knobs to tweak on Azure resources to make sure they are secure in the deployment—what’s a good way to see if you’re setting all the right options?
Today, I’m going to show you how you can automatically check that your Azure infrastructure is configured according to security best practices and how you can fit it in as part of your GitHub CI/CD workflows.
Introducing PSRule for Azure
PSRule for Azure is a tool designed to help you test and validate your Azure Infrastructure as Code (IaC). It provides a set of pre-built checks and remediation steps to help you ensure that your Azure solutions are configured correctly. These checks can be applied both before and after deployment to Azure, allowing you to validate the configuration of Azure resources defined in ARM templates or Bicep code.
The tool is particularly useful for validating IaC templates against best practices, such as those practices outlined in the Well-Architected Framework (WAF). This includes checking for cost, security, reliability, operational excellence, and performance. By integrating PSRule for Azure into your CI/CD pipeline, you can catch issues early in the development process and improve the quality of your IaC templates.
PSRule ships with over 400 tests, based on the Well-Architected Framework. You can also define your own tests, either declaratively in YAML or JSON, or with PowerShell.
Deep dive – Managed Identities 
To show how PSRule works, I’ll use a scenario we tackled recently ourselves at Microsoft. We’re on a company-wide drive to remove passwords and move to non-phishable credentials where possible. As part of this effort, we’ve removed passwords and certificates from code samples and replaced them with Managed Identities.
Managed identities provide an automatically managed identity in Microsoft Entra ID for applications to use when connecting to resources that support Microsoft Entra authentication, such as Azure services like AI Search and Storage. Applications can use managed identities to obtain Microsoft Entra tokens without having to manage any credentials. When you use Managed Identities:
- You don’t need to manage individual credentials. Credentials aren’t even accessible to individual users.
- You can use managed identities to authenticate to any resource that supports Microsoft Entra authentication, including your own applications.
- You don’t pay any extra – managed identities are a platform feature that can be used at no extra cost.
PSRule has a set of tests, as part of the Identity and Access Management pillar, that will check if Azure services created via Bicep are using Managed Identities to authenticate to other Azure resources without storing credentials. For example, you can look at the details of the rule for ‘App Service apps use a Managed Identity’. Firstly, it describes why it is important to use Managed Identities, linking us to more documentation if we need more detail. Then it gives the exact Bicep configuration to enable it in our own Bicep templates. Finally, it even tells us that there is a pre-built Azure Verified Module we could use as part of our template rather than writing it from scratch.
Now that we know what PSRule is and what it does, let’s see how to integrate PSRule for Azure into GitHub Actions, in particular checking the Azure Security Pillar.
Checking GitHub Security with PSRule for Azure
To show how to integrate this into your code, we’ll use a sample that we recently developed for our Reactor series on securing your AI Applications. This is a Python application that uses Azure OpenAI to generate responses to user messages and uses Microsoft Entra for user authentication. The user sign-in functionality uses the built-in authentication feature of Azure Container Apps, which supports both Microsoft Entra ID and Microsoft Entra External ID.
The repository includes all the infrastructure and configuration needed to set up Microsoft Entra user authentication, provision Azure OpenAI resources (with keyless access), and deploy the app to Azure Container Apps using the Azure Developer CLI.
We haven’t added any validation of the Bicep templates used, so it’s a great starting point for showing all the steps to add PSRule for Azure.
We already have a GitHub action azure-bicep-validate.yaml
in the .github/workflows
template. As you see below, this will build the Bicep files which also runs the Bicep linter over the files. This runs on any code pushed to main
, or any pull request which is being merged into main
.
name: Validate bicep scripts
on:
  workflow_dispatch:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Azure CLI script
        uses: azure/CLI@v2
        with:
          inlineScript: az bicep build -f infra/main.bicep
We will add a second job into this workflow, psrule
, which will use the microsoft/ps-rule
action to run our Azure.Pillar.Security
baseline and upload them as code, scanning results to GitHub Advanced Security so they appear on the Security tab in GitHub.
  psrule:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Run PSRule analysis
        uses: microsoft/ps-rule@v2.9.0
        with:
          modules: PSRule.Rules.Azure
          baseline: Azure.Pillar.Security
          inputPath: infra/*.test.bicep
          outputFormat: Sarif
          outputPath: reports/ps-rule-results.sarif
          summary: true
        continue-on-error: true
        env:
          PSRULE_CONFIGURATION_AZURE_BICEP_FILE_EXPANSION: 'true'
          PSRULE_CONFIGURATION_AZURE_BICEP_FILE_EXPANSION_TIMEOUT: '30'
      - name: Upload results to security tab
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: reports/ps-rule-results.sarif
In this job, you can see we are setting the inputPath
to ‘infra/*.test.bicep’
. This calls our test harness ‘main.test.bicep’
that wraps the ‘main.bicep’
entry point and hardcodes parameters like name, region, and features, that we want to use. This allows us to test specific scenarios when the Bicep code supports multiple deployment options. You can see the test harness below, which sets some required default values.
// This file is for doing static analysis and contains sensible defaults
// for PSRule to minimise false-positives and provide the best results.
// This file is not intended to be used as a runtime configuration file.
targetScope = 'subscription'
param location string = 'swedencentral'
module main 'main.bicep' = {
  name: 'main'
  params: {
    location: location
    openAiResourceLocation: location
    authTenantId: '00000000-0000-0000-0000-000000000000'
    name: 'chatapp'
  }
}
Now, if we commit these changes to a branch and create a Pull Request to main
, the GitHub action will run. It’ll take a few seconds and if you click on the action, you’ll be able to see the output from PSRule, like below:
You can see that we ran 102 rules in total and we currently have 7 issues. These are mostly around networking and logging and are all things we would want to fix before we deploy this application into production. For each error, there is a link which takes us to a details page on the PSRule website where we can see what we need to do in our Bicep code to remediate the issue. For example, we have an error with Azure KeyVault – Azure.KeyVault.Firewall
, where we accept connections from clients on any network. The details page for this error shows us exactly what to do – set the properties.networkAcls.defaultAction
property to Deny
.
Merging the PR to get GitHub Security notifications
It’s useful to see the output during the GitHub Action run, but we want to have these as Security alerts on the repository where any developer or admin can easily find them. We have already configured this in the GitHub action, but to see them in the GitHub Security tab, we need to merge the PR to the main
branch. Once this is done, the errors from running PSRule against the main
branch now appear in the GitHub Security tab. If you navigate there and select ‘Code Scanning’, you can see the full list of alerts with links directly into the code, showing which Bicep resource is causing the issue.
Summary and next steps
In this blog post, we have shown you how to use PSRule for Azure to check your Bicep deployment code against the Security pillar of the Well-Architected Framework. To ensure that you stay secure, we outlined how to call PSRule from a GitHub Action, so you get notified about any new security issues introduced by Pull Requests. If you want to check out all the changes we made, you can look at the Pull Request I created while writing this post.
You should now have all the knowledge you need to get started securing your own deployments. For more information on best practices for deploying to Azure, see:
- The Azure Well-Architected Framework and Azure Verified Modules
- Using the Azure Developer CLI and the Bicep language for deploying Azure resources
- PSRule for Azure for checking your Infrastructure as Code before or after deploying to Azure
To learn more and test out features in the Microsoft Entra portfolio, visit our developer center. Make sure you subscribe to the Identity developer blog for more insights and to keep up with the latest on all things Identity. And, follow us on YouTube for video overviews, tutorials, and deep dives.
Is the upload sarif option also available for Azure DevOps (with GitHub Advanced Security)?
The upload SARIF option is available with Azure DevOps, but it’s a separate extension you can install from the marketplace with or without GitHub Advanced Security.
See further details here: https://microsoft.github.io/PSRule/v2/analysis-output/#azure-devops-scans-tab