{"id":1954,"date":"2024-08-21T06:19:47","date_gmt":"2024-08-21T13:19:47","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/identity\/?p=1954"},"modified":"2024-08-21T06:19:47","modified_gmt":"2024-08-21T13:19:47","slug":"secure-azure-deployments-with-psrule","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/identity\/secure-azure-deployments-with-psrule\/","title":{"rendered":"Securing your Azure deployments with PSRule"},"content":{"rendered":"<p>We love automation! With Bicep, you can effortlessly define the Azure infrastructure you need for your application. And with the <a href=\"https:\/\/learn.microsoft.com\/azure\/developer\/azure-developer-cli\/overview\">Azure Developer CLI (azd)<\/a>, 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 <a href=\"https:\/\/azure.github.io\/Azure-Verified-Modules\/\">Azure Verified Modules<\/a> and a <a href=\"https:\/\/azure.github.io\/awesome-azd\/?tags=msft&amp;tags=bicep\">template gallery<\/a>, containing example applications that you can use as starting points for defining your infrastructure.<\/p>\n<p>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\u2014what\u2019s a good way to see if you\u2019re setting all the right options?<\/p>\n<p>Today, I&#8217;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.<\/p>\n<h2>Introducing PSRule for Azure<\/h2>\n<p><a href=\"https:\/\/azure.github.io\/PSRule.Rules.Azure\/about\/\">PSRule for Azure<\/a> 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.<\/p>\n<p>The tool is particularly useful for validating IaC templates against best practices, such as those practices outlined in the <a href=\"https:\/\/learn.microsoft.com\/azure\/architecture\/framework\/\">Well-Architected Framework (WAF)<\/a>. 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.<\/p>\n<p>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.<\/p>\n<h2>Deep dive \u2013 Managed Identities\u202f<\/h2>\n<p>To show how PSRule works, I\u2019ll use a scenario we tackled recently ourselves at Microsoft. We\u2019re on a company-wide drive to remove passwords and move to non-phishable credentials where possible. As part of this effort, we\u2019ve removed passwords and certificates from code samples and replaced them with Managed Identities.<\/p>\n<p>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:<\/p>\n<ul>\n<li>You don&#8217;t need to manage individual credentials. Credentials aren\u2019t even accessible to individual users. <\/li>\n<li>You can use managed identities to authenticate to any resource that supports Microsoft Entra authentication, including your own applications. <\/li>\n<li>You don\u2019t pay any extra &#8211; managed identities are a platform feature that can be used at no extra cost. <\/li>\n<\/ul>\n<p>PSRule has a set of tests, as part of the <a href=\"https:\/\/azure.github.io\/PSRule.Rules.Azure\/en\/rules\/module\/#se05-identity-and-access-management\">Identity and Access Management pillar<\/a>, 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 \u2018<a href=\"https:\/\/azure.github.io\/PSRule.Rules.Azure\/en\/rules\/Azure.AppService.ManagedIdentity\/\">App Service apps use a Managed Identity<\/a>\u2019. 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.<\/p>\n<p><center>\n  <img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/identity\/wp-content\/uploads\/sites\/74\/2024\/08\/PSRule-Bicep-configuration.png\" alt=\"PSRule Bicep configuration\" \/>\n<\/center><\/p>\n<p>Now that we know what PSRule is and what it does, let&#8217;s see how to integrate PSRule for Azure into GitHub Actions, in particular checking the <a href=\"https:\/\/azure.github.io\/PSRule.Rules.Azure\/en\/baselines\/Azure.Pillar.Security\/\">Azure Security Pillar<\/a>.<\/p>\n<h2>Checking GitHub Security with PSRule for Azure<\/h2>\n<p>To show how to integrate this into your code, we&#8217;ll use <a href=\"https:\/\/github.com\/Azure-Samples\/openai-chat-app-entra-auth-builtin\">a sample that we recently developed<\/a> for our <a href=\"https:\/\/developer.microsoft.com\/en-us\/reactor\/series\/s-1355\/\">Reactor series<\/a> 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 <a href=\"https:\/\/learn.microsoft.com\/azure\/container-apps\/authentication\">built-in authentication feature of Azure Container Apps<\/a>, which supports both Microsoft Entra ID and Microsoft Entra External ID.<\/p>\n<p>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 <a href=\"https:\/\/learn.microsoft.com\/azure\/container-apps\/overview\">Azure Container Apps<\/a> using the <a href=\"https:\/\/learn.microsoft.com\/azure\/developer\/azure-developer-cli\/overview\">Azure Developer CLI<\/a>.<\/p>\n<p>We haven&#8217;t added any validation of the Bicep templates used, so it&#8217;s a great starting point for showing all the steps to add PSRule for Azure.<\/p>\n<p>We already have a GitHub action <code>azure-bicep-validate.yaml<\/code> in the <code>.github\/workflows<\/code> 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 <code>main<\/code>, or any pull request which is being merged into <code>main<\/code>.<\/p>\n<pre><code>name: Validate bicep scripts \non: \n\u202f workflow_dispatch: \n\u202f push: \n\u202f \u202f branches: \n\u202f \u202f \u202f - main \n\u202f pull_request: \n\u202f \u202f branches: \n\u202f \u202f \u202f - main \n\njobs: \n\u202f build: \n\u202f \u202f runs-on: ubuntu-latest \n\u202f \u202f steps: \n\u202f \u202f \u202f - name: Checkout \n\u202f \u202f \u202f \u202f uses: actions\/checkout@v4 \n\n\u202f \u202f \u202f - name: Azure CLI script \n\u202f \u202f \u202f \u202f uses: azure\/CLI@v2 \n\u202f \u202f \u202f \u202f with: \n\u202f \u202f \u202f \u202f \u202f inlineScript: az bicep build -f infra\/main.bicep \n<\/code><\/pre>\n<p>We will add a second job into this workflow, <code>psrule<\/code>, which will use the <code>microsoft\/ps-rule<\/code> action to run our <code>Azure.Pillar.Security<\/code> baseline and upload them as code, scanning results to GitHub Advanced Security so they appear on the Security tab in GitHub.<\/p>\n<pre><code>\u202f psrule: \n\u202f \u202f runs-on: ubuntu-latest \n\u202f \u202f permissions: \n\u202f \u202f \u202f security-events: write \n\u202f \u202f steps: \n\u202f \u202f \u202f - name: Checkout \n\u202f \u202f \u202f \u202f uses: actions\/checkout@v4 \n\n\u202f \u202f \u202f - name: Run PSRule analysis \n\u202f \u202f \u202f \u202f uses: microsoft\/ps-rule@v2.9.0 \n\u202f \u202f \u202f \u202f with: \n\u202f \u202f \u202f \u202f \u202f modules: PSRule.Rules.Azure \n\u202f \u202f \u202f \u202f \u202f baseline: Azure.Pillar.Security \n\u202f \u202f \u202f \u202f \u202f inputPath: infra\/*.test.bicep \n\u202f \u202f \u202f \u202f \u202f outputFormat: Sarif \n\u202f \u202f \u202f \u202f \u202f outputPath: reports\/ps-rule-results.sarif \n\u202f \u202f \u202f \u202f \u202f summary: true \n\u202f \u202f \u202f \u202f continue-on-error: true \n\n\u202f \u202f \u202f \u202f env: \n\u202f \u202f \u202f \u202f \u202f PSRULE_CONFIGURATION_AZURE_BICEP_FILE_EXPANSION: 'true' \n\u202f \u202f \u202f \u202f \u202f PSRULE_CONFIGURATION_AZURE_BICEP_FILE_EXPANSION_TIMEOUT: '30' \n\n\u202f \u202f \u202f - name: Upload results to security tab \n\u202f \u202f \u202f \u202f uses: github\/codeql-action\/upload-sarif@v3 \n\u202f \u202f \u202f \u202f with:\n\u202f \u202f \u202f \u202f \u202f sarif_file: reports\/ps-rule-results.sarif \n<\/code><\/pre>\n<p>In this job, you can see we are setting the <code>inputPath<\/code> to <code>\u2018infra\/*.test.bicep\u2019<\/code>. This calls our test harness <code>\u2018main.test.bicep\u2019<\/code> that wraps the <code>\u2018main.bicep\u2019<\/code> 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.<\/p>\n<pre><code>\/\/ This file is for doing static analysis and contains sensible defaults \n\/\/ for PSRule to minimise false-positives and provide the best results. \n\n\/\/ This file is not intended to be used as a runtime configuration file. \n\ntargetScope = 'subscription' \n\nparam location string = 'swedencentral' \n\nmodule main 'main.bicep' = { \n\u202f name: 'main' \n\u202f params: { \n\u202f \u202f location: location \n\u202f \u202f openAiResourceLocation: location \n\u202f \u202f authTenantId: '00000000-0000-0000-0000-000000000000' \n\u202f \u202f name: 'chatapp' \n\u202f }\n} \n<\/code><\/pre>\n<p>Now, if we commit these changes to a branch and create a Pull Request to <code>main<\/code>, the GitHub action will run. It&#8217;ll take a few seconds and if you click on the action, you&#8217;ll be able to see the output from PSRule, like below:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/identity\/wp-content\/uploads\/sites\/74\/2024\/08\/PSRule-result-summary.png\" alt=\"PSRule output in GitHub action\" \/><\/p>\n<p>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.\u202fFor 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 \u2013 <code>Azure.KeyVault.Firewall<\/code>, where we accept connections from clients on any network. The <a href=\"https:\/\/azure.github.io\/PSRule.Rules.Azure\/en\/rules\/Azure.KeyVault.Firewall\/\">details page for this error<\/a> shows us exactly what to do &#8211; set the <code>properties.networkAcls.defaultAction<\/code> property to <code>Deny<\/code>.<\/p>\n<h2>Merging the PR to get GitHub Security notifications<\/h2>\n<p>It&#8217;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 <code>main<\/code> branch. Once this is done, the errors from running PSRule against the <code>main<\/code> branch now appear in the GitHub Security tab.\u202fIf you navigate there and select &#8216;Code Scanning&#8217;, you can see the full list of alerts with links directly into the code, showing which Bicep resource is causing the issue.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/identity\/wp-content\/uploads\/sites\/74\/2024\/08\/PSRule-in-GitHub-Security.png\" alt=\"PSRule in GitHub Security page\" \/><\/p>\n<h2>Summary and next steps<\/h2>\n<p>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 <a href=\"https:\/\/github.com\/Azure-Samples\/openai-chat-app-entra-auth-builtin\/pull\/38\">Pull Request<\/a> I created while writing this post.<\/p>\n<p>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:<\/p>\n<ul>\n<li>The Azure <a href=\"https:\/\/learn.microsoft.com\/azure\/architecture\/framework\/\">Well-Architected Framework<\/a> and <a href=\"https:\/\/azure.github.io\/Azure-Verified-Modules\/\">Azure Verified Modules<\/a> <\/li>\n<li>Using the <a href=\"https:\/\/learn.microsoft.com\/azure\/developer\/azure-developer-cli\/overview\">Azure Developer CLI<\/a> and the <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/azure-resource-manager\/bicep\/overview?tabs=bicep\">Bicep language for deploying Azure resources<\/a> <\/li>\n<li><a href=\"https:\/\/azure.github.io\/PSRule.Rules.Azure\/about\/\">PSRule for Azure<\/a> for checking your Infrastructure as Code before or after deploying to Azure <\/li>\n<\/ul>\n<p>To learn more and test out features in the Microsoft Entra portfolio, visit our\u202f<a href=\"https:\/\/aka.ms\/dev\/ms-entra\">developer center<\/a>. Make sure you subscribe to the\u202f<a href=\"https:\/\/aka.ms\/devblog\/ms-entra\">Identity developer blog<\/a>\u202ffor more insights and to keep up with the latest on all things Identity. And, follow us on\u202f<a href=\"https:\/\/www.youtube.com\/@MicrosoftSecurity\/playlists\">YouTube<\/a>\u202ffor video overviews, tutorials, and deep dives.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>PSRule for Azure is a powerful tool designed to validate your Azure Infrastructure as Code (IaC). PSRule runs checks based on the Well-Architected Framework, helping you ensure your Azure solutions follow security best practices. See how to integrate PSRule into your workflow, and catch security issues in your IaC templates early.<\/p>\n","protected":false},"author":115019,"featured_media":1957,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[48],"tags":[61,63,14,16,46,50,62,4],"class_list":["post-1954","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials","tag-azure","tag-bicep","tag-devops","tag-entra","tag-entra-id","tag-identity","tag-managed-identities","tag-security"],"acf":[],"blog_post_summary":"<p>PSRule for Azure is a powerful tool designed to validate your Azure Infrastructure as Code (IaC). PSRule runs checks based on the Well-Architected Framework, helping you ensure your Azure solutions follow security best practices. See how to integrate PSRule into your workflow, and catch security issues in your IaC templates early.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/identity\/wp-json\/wp\/v2\/posts\/1954","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/identity\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/identity\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/identity\/wp-json\/wp\/v2\/users\/115019"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/identity\/wp-json\/wp\/v2\/comments?post=1954"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/identity\/wp-json\/wp\/v2\/posts\/1954\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/identity\/wp-json\/wp\/v2\/media\/1957"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/identity\/wp-json\/wp\/v2\/media?parent=1954"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/identity\/wp-json\/wp\/v2\/categories?post=1954"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/identity\/wp-json\/wp\/v2\/tags?post=1954"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}