<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Bicep on Andrew Wilson's Blog</title><link>https://andrewilson.co.uk/tags/bicep/</link><description>Recent content in Bicep on Andrew Wilson's Blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sat, 09 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://andrewilson.co.uk/tags/bicep/index.xml" rel="self" type="application/rss+xml"/><item><title>Unit Testing Bicep Logic with BicepConsoleTTK</title><link>https://andrewilson.co.uk/post/2026/05/bicepconsolettk/</link><pubDate>Sat, 09 May 2026 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2026/05/bicepconsolettk/</guid><description>Problem Space In most Infrastructure as Code teams, Bicep quality checks start to look mature as soon as linting and deployment validation are in place. In practice, there is still a blind spot: logic-level testing of exported functions, types, and variables.
Most teams validate by deploying to a subscription and checking outcomes afterwards. That is useful, but it is also slow and expensive when what you actually want to verify is pure logic.</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>In most Infrastructure as Code teams, Bicep quality checks start to look mature as soon as linting and deployment validation are in place. In practice, there is still a blind spot: logic-level testing of exported functions, types, and variables.</p>
<p>Most teams validate by deploying to a subscription and checking outcomes afterwards. That is useful, but it is also slow and expensive when what you actually want to verify is pure logic. A shared Bicep library changes, everything still compiles, and then a downstream module fails later in a deployment pipeline because a naming function or constructor behaviour subtly changed. That is exactly the type of issue we normally catch early in application development with unit tests.</p>
<p>If you maintain shared Bicep libraries, this gap hurts quickly:</p>
<ul>
<li>Naming functions drift without anyone noticing</li>
<li>Type constructors change and break downstream modules</li>
<li>Refactors feel risky because feedback loops are too long</li>
</ul>
<p>I wanted a way to unit test Bicep logic directly, without deploying anything.</p>
<h2 id="introducing-bicepconsolettk">Introducing BicepConsoleTTK</h2>
<p>To solve that problem, I created <strong>BicepConsoleTTK</strong> (Bicep Console Test Tool Kit).</p>
<p>It is a Pester-based framework that executes Bicep expressions through the Bicep console REPL, so you can assert outputs in fast, repeatable unit tests.</p>
<p>Repository:</p>
<p><a href="https://github.com/Andrew-D-Wilson/bicep-console-test-framework">
  <img src="https://img.shields.io/badge/GitHub-Bicep--Console--TTK-181717?logo=github" alt="GitHub Repository">

</a>
<a href="https://www.powershellgallery.com/packages/BicepConsoleTTK">
  <img src="https://img.shields.io/powershellgallery/v/BicepConsoleTTK?logo=powershell&amp;label=PowerShell%20Gallery" alt="PowerShell Gallery">

</a></p>
<p>At a high level, the toolkit gives you two commands:</p>
<ul>
<li><code>Import-Bicep</code>: reads one or more Bicep files and extracts the exports you ask for</li>
<li><code>Invoke-BicepExpression</code>: runs those declarations plus your expression in <code>bicep console</code> and returns the result</li>
</ul>
<h2 id="why-this-approach-works">Why This Approach Works</h2>
<p>This lets you test Bicep logic in isolation, before template deployment.</p>
<p>Practical outcomes:</p>
<ul>
<li>Faster feedback during development</li>
<li>More confidence when refactoring shared functions</li>
<li>Cleaner CI pipelines for template libraries</li>
<li>Better separation between unit tests (logic) and integration tests (deployment)</li>
</ul>
<h2 id="features">Features</h2>
<ul>
<li>Familiar import syntax (named imports and wildcard imports)</li>
<li>Multi-file import composition with preserved order</li>
<li>Deduplication when the same member is imported multiple ways</li>
<li>Setup declarations for multi-step scenarios</li>
<li>Pipeline input support</li>
<li>Cleaner Bicep console error reporting</li>
<li>CI/CD-friendly, non-interactive execution</li>
</ul>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>PowerShell 5.1 (Desktop) or 7+</li>
<li>Pester 5.x</li>
<li>Bicep CLI 0.42.1+</li>
<li><code>bicep</code> available on your <code>PATH</code></li>
</ul>
<h2 id="installation">Installation</h2>
<h3 id="from-powershell-gallery">From PowerShell Gallery</h3>
<p><a href="https://www.powershellgallery.com/packages/BicepConsoleTTK">
  <img src="https://img.shields.io/powershellgallery/v/BicepConsoleTTK?logo=powershell&amp;label=PowerShell%20Gallery" alt="PowerShell Gallery">

</a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Install-Module</span> -Name BicepConsoleTTK -Repository PSGallery
</span></span></code></pre></div><p>In your test file:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>BeforeAll {
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">Import-Module</span> BicepConsoleTTK -Force
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="from-source">From Source</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>git clone https://github.com/<span style="color:#8be9fd;font-style:italic">Andrew-D</span>-Wilson/<span style="color:#8be9fd;font-style:italic">bicep-console</span>-test-framework.git
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Import-Module</span> <span style="color:#f1fa8c">&#34;</span><span style="color:#8be9fd;font-style:italic">$PSScriptRoot</span><span style="color:#f1fa8c">/../src/BicepConsoleTTK&#34;</span> -Force
</span></span></code></pre></div><h2 id="copilot-integration">Copilot Integration</h2>
<p>This repository ships two complementary artefacts that teach GitHub Copilot how to write BicepConsoleTTK tests.</p>
<h3 id="agent-skill-recommended">Agent Skill (Recommended)</h3>
<p>The <code>.github/skills/bicepconsolettk/SKILL.md</code> file is a GitHub Copilot agent skill. When Copilot is working in agent mode, it automatically loads this skill whenever the task is related to writing Bicep tests, injecting the full authoring guide into its context.</p>
<p>The skill is discovered automatically from <code>.github/skills/</code> in any repository that contains it.</p>
<h3 id="vs-code-instructions-file">VS Code Instructions File</h3>
<p>The <code>bicepconsolettk.instructions.md</code> file is a VS Code Copilot custom instructions file.</p>
<p>To use it in your own repository:</p>
<ol>
<li>Copy the file to <code>.github/instructions/bicepconsolettk.instructions.md</code></li>
<li>Keep <code>applyTo: &quot;**/*.Tests.ps1&quot;</code> so it is scoped to Pester test files</li>
<li>Ask Copilot to generate or refactor tests in your <code>*.Tests.ps1</code> files</li>
</ol>
<p>With both artefacts in place, Copilot gets better guidance in both agent-driven workflows and editor-scoped instruction workflows.</p>
<h2 id="usage">Usage</h2>
<h3 id="1-import-exports-from-bicep-files">1. Import exports from Bicep files</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$imports</span> = <span style="color:#8be9fd;font-style:italic">Import-Bicep</span> @(
</span></span><span style="display:flex;"><span>	<span style="color:#f1fa8c">&#34;import {coreParams, newCoreParams} from &#39;</span><span style="color:#8be9fd;font-style:italic">$PSScriptRoot</span><span style="color:#f1fa8c">/../shared/Types.bicep&#39;&#34;</span>,
</span></span><span style="display:flex;"><span>	<span style="color:#f1fa8c">&#34;import {basicResource}             from &#39;</span><span style="color:#8be9fd;font-style:italic">$PSScriptRoot</span><span style="color:#f1fa8c">/../shared/NamingFunctions.bicep&#39;&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h3 id="2-evaluate-an-expression">2. Evaluate an expression</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$result</span> = <span style="color:#8be9fd;font-style:italic">Invoke-BicepExpression</span> -b <span style="color:#8be9fd;font-style:italic">$imports</span> -e <span style="color:#f1fa8c">&#34;newCoreParams(&#39;uksouth&#39;, &#39;uks&#39;, &#39;prod&#39;, &#39;myapp&#39;)&#34;</span>
</span></span></code></pre></div><h3 id="3-use-setup-declarations-when-needed">3. Use setup declarations when needed</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$setupDeclarations</span> = @(
</span></span><span style="display:flex;"><span>	<span style="color:#f1fa8c">&#34;var projectNameStart = &#39;hello&#39;&#34;</span>,
</span></span><span style="display:flex;"><span>	<span style="color:#f1fa8c">&#34;var projectNameComplete = &#39;</span><span style="color:#f1fa8c">`$</span><span style="color:#f1fa8c">{projectNameStart}world&#39;&#34;</span>,
</span></span><span style="display:flex;"><span>	<span style="color:#f1fa8c">&#34;var coreParameters coreParams = newCoreParams(&#39;uksouth&#39;, &#39;uks&#39;, &#39;dev&#39;, projectNameComplete)&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$result</span> = <span style="color:#8be9fd;font-style:italic">Invoke-BicepExpression</span> -b <span style="color:#8be9fd;font-style:italic">$imports</span> -s <span style="color:#8be9fd;font-style:italic">$setupDeclarations</span> -e <span style="color:#f1fa8c">&#34;basicResource(&#39;aks&#39;, coreParameters)&#34;</span>
</span></span></code></pre></div><h2 id="example-pester-test">Example Pester Test</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>BeforeAll {
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">Import-Module</span> BicepConsoleTTK -Force
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Describe <span style="color:#f1fa8c">&#34;Naming Functions&#34;</span> {
</span></span><span style="display:flex;"><span>	BeforeAll {
</span></span><span style="display:flex;"><span>		<span style="color:#8be9fd;font-style:italic">$script:imports</span> = <span style="color:#8be9fd;font-style:italic">Import-Bicep</span> @(
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;import {coreParams, newCoreParams} from &#39;</span><span style="color:#8be9fd;font-style:italic">$PSScriptRoot</span><span style="color:#f1fa8c">/../shared/Types.bicep&#39;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;import {basicResource, csResource} from &#39;</span><span style="color:#8be9fd;font-style:italic">$PSScriptRoot</span><span style="color:#f1fa8c">/../shared/NamingFunctions.bicep&#39;&#34;</span>
</span></span><span style="display:flex;"><span>		)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	It <span style="color:#f1fa8c">&#34;basicResource includes abbreviation, project, environment and location&#34;</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#8be9fd;font-style:italic">$result</span> = <span style="color:#8be9fd;font-style:italic">Invoke-BicepExpression</span> -b <span style="color:#8be9fd;font-style:italic">$script:imports</span> `
</span></span><span style="display:flex;"><span>			-e <span style="color:#f1fa8c">&#34;basicResource(&#39;aks&#39;, newCoreParams(&#39;uksouth&#39;, &#39;uks&#39;, &#39;dev&#39;, &#39;myapp&#39;))&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#8be9fd;font-style:italic">$result</span> | Should -Be <span style="color:#f1fa8c">&#34;&#39;aks-myapp-dev-uksouth&#39;&#34;</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Run tests with:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Invoke-Pester</span> -Path ./tests
</span></span></code></pre></div><h2 id="how-it-works-short-version">How It Works (Short Version)</h2>
<p><code>Import-Bicep</code> parses import strings, resolves file paths, and extracts exported declarations from source files.</p>
<p><code>Invoke-BicepExpression</code> sends those declarations and your expression into <code>bicep console</code>, captures output, and translates console noise into readable exceptions when failures occur.</p>
<p>That gives you deterministic, fast unit tests focused on logic instead of deployment orchestration.</p>
<h2 id="where-this-fits-in-your-testing-strategy">Where This Fits in Your Testing Strategy</h2>
<p>Use BicepConsoleTTK for:</p>
<ul>
<li>Function output verification</li>
<li>Naming convention enforcement</li>
<li>Shared type constructor validation</li>
<li>Regression checks during refactoring</li>
</ul>
<p>Still keep deployment/integration tests for:</p>
<ul>
<li>Resource runtime behavior</li>
<li>Policy and RBAC interactions</li>
<li>End-to-end environment validation</li>
</ul>
<p>Both layers matter. This tool improves the unit-testing layer.</p>
<h2 id="in-short">In Short</h2>
<p>BicepConsoleTTK helps you test Bicep exports the same way you test application code: quickly, repeatedly, and early.</p>
<p>If your team relies on shared Bicep libraries, this can remove a lot of friction from your delivery pipeline while increasing confidence in every change.</p>
]]></content:encoded></item><item><title>Azure Key Vault | Access Policies Removed On Deployment</title><link>https://andrewilson.co.uk/post/2026/04/azure-key-vault-access-policy-removed-on-deploy/</link><pubDate>Thu, 30 Apr 2026 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2026/04/azure-key-vault-access-policy-removed-on-deploy/</guid><description>⚠️ NOTE
Microsoft guidance is clear that Azure RBAC should be used for data plane authorization moving forward, instead of legacy access policies
Azure role-based access control (Azure RBAC) vs. access policies (legacy) Provide access to Key Vault keys, certificates, and secrets with Azure role-based access control Problem Space When multiple applications share a single Azure Key Vault, access policy management can become an unexpected source of deployment risk → and the root cause is easy to miss.</description><content:encoded><![CDATA[<hr>
<p>⚠️ <strong>NOTE</strong></p>
<p><code>Microsoft guidance is clear that Azure RBAC should be used for data plane authorization moving forward, instead of legacy access policies</code></p>
<blockquote>
<ul>
<li><a href="https://learn.microsoft.com/azure/key-vault/general/rbac-access-policy">Azure role-based access control (Azure RBAC) vs. access policies (legacy)</a></li>
<li><a href="https://learn.microsoft.com/azure/key-vault/general/rbac-guide">Provide access to Key Vault keys, certificates, and secrets with Azure role-based access control</a></li>
</ul>
</blockquote>
<hr>
<h2 id="problem-space">Problem Space</h2>
<p>When multiple applications share a single Azure Key Vault, access policy management can become an unexpected source of deployment risk → and the root cause is easy to miss.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#6272a4">// BICEP</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">KeyVaultDeploy</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults@2025-05-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#f1fa8c">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">accessPolicies</span>: []
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The <code>accessPolicies</code> property on the <code>Microsoft.KeyVault/vaults</code> ARM resource is a required field. In a shared Key Vault scenario, where the vault is owned and deployed by one application, the natural thing to do is set this to an empty array as the application does not own the other applications access policies, so it does not declare them.</p>
<p>This is where the problem begins.</p>
<p>When ARM deploys the Key Vault resource with <code>accessPolicies</code> set to an empty array <code>[]</code>, it treats that as the desired state for the vault. Any access policies that existed previously (<em>including those belonging to other applications</em>) are removed.</p>
<p>Here is how the failure pattern plays out in practice:</p>
<ol>
<li><strong>Application A owns the Key Vault</strong> and deploys it with <code>accessPolicies: []</code>.</li>
<li><strong>Immediately after</strong>, Application A&rsquo;s IaC deploys its own access policies via a separate step (for example, using the <code>Microsoft.KeyVault/vaults/accessPolicies</code> child resource). Access is restored for Application A without apparent issue.</li>
<li><strong>Application B&rsquo;s access policies have been silently removed</strong> — it never re-deploys the key vault, only its own access policy step.</li>
<li><strong>Application B fails at runtime</strong> when attempting secret, key, or certificate operations — authorization errors with no obvious cause.</li>
<li><strong>Application B is re-deployed</strong>, its access policies are restored, and both applications work again — until the next time Application A&rsquo;s pipeline runs.</li>
</ol>
<p>This cycle repeats silently. Each Application A deployment is an outage for Application B, and the connection between the two is not obvious unless you know to look for it.</p>
<p>This can happen if you have the following setup:</p>
<ul>
<li>Multiple applications or pipelines sharing a Key Vault</li>
<li>The Key Vault instance deployed as part of one application&rsquo;s IaC</li>
<li>Access configured through Key Vault access policies</li>
<li>Each application&rsquo;s pipeline independently managing only its own policy entries</li>
</ul>
<h2 id="workarounds-still-using-access-policies">Workarounds (Still Using Access Policies)</h2>
<p>If moving to RBAC immediately is not possible, the following workarounds address the underlying coupling problem — but each comes with trade-offs.</p>
<h3 id="1-move-the-key-vault-to-a-core-resources-deployment">1. Move the Key Vault to a Core Resources Deployment</h3>
<p>Extract the Key Vault resource into a dedicated core infrastructure deployment, separate from any application pipeline. Application pipelines then only manage their own access policy entries against the pre-existing vault.</p>
<p>Benefits:</p>
<ul>
<li>The vault is no longer redeployed as part of application changes</li>
<li>Eliminates the empty <code>accessPolicies</code> overwrite problem</li>
<li>Makes ownership of the shared resource explicit</li>
</ul>
<p>Trade-off:</p>
<ul>
<li>When the core resources deployment runs (for environment rebuilds, configuration changes, or disaster recovery), all dependent applications will need to be re-deployed afterwards to restore their access policy entries. This creates an operational dependency that must be planned for and communicated clearly across teams.</li>
</ul>
<h3 id="2-provision-a-separate-key-vault-per-application">2. Provision a Separate Key Vault Per Application</h3>
<p>Give each application its own dedicated Key Vault, eliminating the shared resource entirely.</p>
<p>Benefits:</p>
<ul>
<li>No cross-application policy coupling — each key vault is fully owned by one application</li>
<li>Reduces blast radius of deployment mistakes</li>
<li>Cleaner isolation and tenancy boundaries</li>
</ul>
<p>Trade-offs:</p>
<ul>
<li>More key vaults to provision, monitor, rotate secrets in, and govern</li>
<li>Increases operational overhead for certificate and secret lifecycle management</li>
<li>Harder to get a unified view of secrets across the estate</li>
</ul>
<p>This option improves isolation but does not solve the root-cause pattern; it just limits the blast radius per key vault.</p>
<h2 id="the-real-solution-move-to-azure-rbac">The Real Solution: Move to Azure RBAC</h2>
<p>The correct long-term fix is to stop using access policies for Key Vault data plane authorization entirely and adopt Azure RBAC role assignments.</p>
<p>With RBAC:</p>
<ul>
<li>Role assignments are <strong>additive and independently managed</strong> — assigning a role for Application B does not affect Application A&rsquo;s assignments</li>
<li>Authorization state is managed through Azure&rsquo;s centralized RBAC model, consistent with the rest of the Azure platform</li>
<li>Microsoft explicitly recommends RBAC over access policies for new and migrating workloads</li>
</ul>
<p>The deployment pattern becomes:</p>
<ol>
<li>Deploy the Key Vault resource. The <code>accessPolicies</code> field becomes irrelevant.</li>
<li>Each application independently deploys <code>Microsoft.Authorization/roleAssignments</code> scoped to the vault for its own managed identity or service principal.</li>
<li>An application deployment wont affect another application&rsquo;s authorization.</li>
<li>A redeploy of the Key Vault resource won&rsquo;t remove existing application&rsquo;s authorizations.</li>
</ol>
<p>Practical migration approach:</p>
<ol>
<li>Enable the RBAC permission model on the Key Vault IaC (<code>enableRbacAuthorization: true</code>).</li>
<li>Map existing access policy permissions to the appropriate built-in Key Vault RBAC roles (for example <code>Key Vault Secrets User</code>, <code>Key Vault Certificates User</code>).
<ul>
<li>Assign roles to the required identities at the appropriate scope (Key Vault, Key Vault Secret/Certificate).</li>
</ul>
</li>
<li>Remove legacy access policy configuration from all IaC templates.</li>
<li>Deploy IaC and Validate application behavior against the new authorization model.</li>
</ol>
<p>Useful Microsoft documentation:</p>
<ul>
<li><a href="https://learn.microsoft.com/azure/key-vault/general/rbac-access-policy">RBAC vs access policy comparison and recommendation</a></li>
<li><a href="https://learn.microsoft.com/azure/key-vault/general/rbac-guide">RBAC implementation guidance for Key Vault</a></li>
</ul>
<h2 id="closing-thoughts">Closing Thoughts</h2>
<p>The empty <code>accessPolicies: []</code> pattern is easy to arrive at — the field is required, the application does not own the other policies, so an empty array seems reasonable. In a single-application setup it causes no problems. In a shared-vault, multi-application setup it causes silent, repeating outages that are hard to diagnose without knowing where to look.</p>
<p>The workarounds described here reduce the risk while a migration is planned, but neither fully resolves the underlying problem.</p>
<p>The right answer is Azure RBAC. Role assignments are independent, additive, and do not interfere with each other across application boundaries. Moving to RBAC removes this class of problem entirely.</p>
]]></content:encoded></item><item><title>Bicep Tips and Tricks | #10 | Authoring Practices</title><link>https://andrewilson.co.uk/post/2026/04/bicep-tips-and-tricks-authoring-practices/</link><pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2026/04/bicep-tips-and-tricks-authoring-practices/</guid><description>Problem Space As Bicep adoption grows, so does the complexity of the environments and teams using it. Without clear authoring practices, Bicep codebases can quickly become inconsistent, hard to maintain, and error-prone. In this post I wanted to share some practical authoring practices and anti-patterns to help you and your team write better Bicep code.
Why Define and Stick to Your Authoring Practices Defining and following authoring practices ensures:
Consistency across your codebase and team Easier onboarding for new contributors Fewer errors and less technical debt Improved maintainability and clarity Below are some key practices and common anti-patterns to consider.</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>As Bicep adoption grows, so does the complexity of the environments and teams using it. Without clear authoring practices, Bicep codebases can quickly become inconsistent, hard to maintain, and error-prone. In this post I wanted to share some practical authoring practices and anti-patterns to help you and your team write better Bicep code.</p>
<h2 id="why-define-and-stick-to-your-authoring-practices">Why Define and Stick to Your Authoring Practices</h2>
<p>Defining and following authoring practices ensures:</p>
<ul>
<li>Consistency across your codebase and team</li>
<li>Easier onboarding for new contributors</li>
<li>Fewer errors and less technical debt</li>
<li>Improved maintainability and clarity</li>
</ul>
<p>Below are some key practices and common anti-patterns to consider.</p>
<hr>
<h2 id="authoring-practices">Authoring Practices</h2>
<h3 id="1-folder-structure">1. Folder Structure</h3>
<p>Organise your Bicep files in a logical folder structure. For example, group modules by resource type or deployment context. This makes navigation and reuse easier:</p>
<pre tabindex="0"><code>├── modules/
│   ├── storage/
│   └── networking/
├── main.bicep
├── parameters/
</code></pre><h3 id="2-template-structure">2. Template Structure</h3>
<p>Keep your templates clean and modular. Use modules for reusable components, and keep your main entry point focused on orchestration. Avoid putting everything in a single file.</p>
<h3 id="3-well-defined-names">3. Well Defined Names</h3>
<p>Use clear, descriptive names for resources, parameters, and variables. Avoid abbreviations that aren’t widely understood. For example, prefer <code>storageAccountName</code> over <code>saName</code>.</p>
<h3 id="4-descriptions">4. Descriptions</h3>
<p>Add descriptions to parameters, outputs, and resources. This helps users understand intent and usage:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Configurable name for the application storage account&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">storageAccountName</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span></code></pre></div><h3 id="5-comments">5. Comments</h3>
<p>Use comments to explain why something is done, not what is done. Avoid echoing the code. Good comments provide context or rationale.</p>
<h3 id="6-constraints-and-metadata">6. Constraints and Metadata</h3>
<p>Leverage allowed values, min/max length, and metadata to enforce constraints and provide guidance:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">minLength</span>(<span style="color:#8be9fd;font-style:italic">3</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">maxLength</span>(<span style="color:#8be9fd;font-style:italic">24</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">allowed</span>([ <span style="color:#f1fa8c">&#39;dev&#39;</span> <span style="color:#f1fa8c">&#39;test&#39;</span> <span style="color:#f1fa8c">&#39;prod&#39;</span> ])
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span></code></pre></div><p>Make sure to use these where there is a specific need, over constraining parameters can equally cause issues.</p>
<h3 id="7-contracts">7. Contracts</h3>
<p>Define clear contracts for your modules: what parameters are required, what outputs are provided, and what assumptions are made. Document these in the module and in a README.</p>
<h3 id="8-readme">8. README</h3>
<p>Modules and major orchestrating templates should have a README explaining its purpose and usage. This is invaluable for onboarding and reuse.</p>
<h3 id="9-bicep-linting">9. Bicep Linting</h3>
<p>Use Bicep linting as part of your authoring workflow to catch issues early and enforce consistency. Define team linting rules in <code>bicepconfig.json</code>, run lint checks locally during development, and enforce them in CI to prevent low-quality or non-compliant templates from being merged.</p>
<h3 id="10-ai-generated-templates-and-conformance">10. AI-Generated Templates and Conformance</h3>
<p>If you are using AI to generate Bicep, treat your authoring practices as executable guardrails rather than optional guidance.</p>
<p>Start by defining a clear generation contract in your prompt and repository standards:</p>
<ul>
<li>Require a specific folder structure and file naming convention</li>
<li>Require module contracts (documented params/outputs and assumptions)</li>
<li>Require descriptions, meaningful names, and no dead code</li>
<li>Require lint-clean output before a template is considered complete</li>
</ul>
<p>Then enforce conformance automatically in CI:</p>
<ul>
<li>Validate formatting and linting on every pull request</li>
<li>Fail builds when lint rules are violated</li>
<li>Optionally add policy checks (for example, naming, locations, and SKUs)</li>
<li>Require human review for architectural decisions, not just syntax correctness</li>
</ul>
<p>Finally, use a feedback loop. When reviewers find repeated AI mistakes, update your prompt template, lint configuration, and module examples so the next generation cycle improves by default.</p>
<hr>
<h2 id="anti-patterns">Anti-Patterns</h2>
<h3 id="1-premature-abstraction">1. Premature Abstraction</h3>
<p>Don’t create modules or abstractions before you have a real need. Over-abstraction leads to unnecessary complexity and maintenance overhead.</p>
<h3 id="2-over-specification">2. Over-Specification</h3>
<p>Avoid making every resource parameter configurable if it’s not needed. Too many parameters can confuse users and make templates harder to use.</p>
<h3 id="3-echo-comments">3. Echo Comments</h3>
<p>Comments that simply restate the code add no value. For example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#6272a4">// Set the storage account name</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">storageAccountName</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span></code></pre></div><p>Instead, explain why a value is needed or any constraints.</p>
<h3 id="4-dead-code">4. Dead Code</h3>
<p>Remove unused parameters, variables, and resources. Dead code clutters templates and can cause confusion or errors.</p>
<h3 id="5-generic-copy-paste-documentation">5. Generic Copy-Paste Documentation</h3>
<p>Avoid boilerplate documentation that doesn’t reflect the actual template. Tailor docs and comments to the specific module or resource.</p>
<h3 id="6-poorly-defined-names">6. Poorly Defined Names</h3>
<p>Names like <code>var1</code> or <code>resource2</code> make templates hard to understand. Use meaningful, descriptive names everywhere.</p>
<hr>
<h2 id="conclusion">Conclusion</h2>
<p>Establishing and following authoring practices for Bicep will help your team deliver infrastructure as code that is robust, maintainable, and easy to understand. Avoid common anti-patterns, and invest in clarity and documentation.</p>
]]></content:encoded></item><item><title>Bicep Tips and Tricks | #9 | Prevent a Nasty Refactor with Function Namespaces</title><link>https://andrewilson.co.uk/post/2025/09/bicep-tips-and-tricks-function-namespaces/</link><pubDate>Wed, 17 Sep 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/09/bicep-tips-and-tricks-function-namespaces/</guid><description>Problem Space There have been few times where I have landed into this particular predicament whereby either by my own doing or through the use of another&amp;rsquo;s code base, a deep nested or thoroughly utilised (parameter/variable/or other defined item) has been created with the same name as a Bicep function. As by Murphy&amp;rsquo;s law, its only once you have reached this point of no return that you realise that your items name conflicts.</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>There have been few times where I have landed into this particular predicament whereby either by my own doing or through the use of another&rsquo;s code base, a deep nested or thoroughly utilised (parameter/variable/or other defined item) has been created with the same name as a Bicep function. As by Murphy&rsquo;s law, its only once you have reached this point of no return that you realise that your items name conflicts.</p>
<p>Now if you are like many in your unawares, your thoughts and actions will conclude to a singular one of <em>&rsquo;that sucks&hellip; followed by a nasty refactor</em>'.</p>
<h2 id="solution---namespaces">Solution - Namespaces</h2>
<p>Namespaces are a declarative scope in which identifiers such as the names of types, functions, and variables can be declared. These namespaces are used to organise code into logical groups and to prevent name collisions such as the one you are currently experiencing.</p>
<p>Thankfully, in <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions#namespaces-for-functions">Bicep there are two namespaces</a> where all functions are split, <code>az</code> and <code>sys</code>.</p>
<ul>
<li><strong>az</strong> | Contains functions that are specific to an Azure deployment such as:
<ul>
<li>deployment</li>
<li>environment</li>
<li>resourceGroup</li>
<li>subscription</li>
</ul>
</li>
<li><strong>sys</strong> | Contains functions that are used to construct values, and decorators for parameters and resource loops. This includes but is not limited to:
<ul>
<li>[<strong>Array</strong>] concat</li>
<li>[<strong>File</strong>] loadJsonContent</li>
<li>[<strong>Lambda</strong>] filter</li>
<li>[<strong>Logical</strong>] bool</li>
<li>[<strong>Numeric</strong>] int</li>
<li>[<strong>String</strong>] contains</li>
</ul>
</li>
</ul>
<p>To make use of these namespaces, simply add the namespace identifier in front of the function. The example below shows a real world example of this issue where a parameter name &lsquo;<em>environment</em>&rsquo; has been extensively utilised in this template and many others. This particular template is being used to deploy an AuthProvider in API Management, and the author wishes to utilise the environment function for the <code>authentication.loginEndpoint</code>. Without the use of the az namespace, the environment parameter would need to be refactored both in this template and wider templates for consistency.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">/******************************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Function</span> <span style="color:#8be9fd;font-style:italic">Namespace</span> <span style="color:#8be9fd;font-style:italic">Example</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Author</span>: <span style="color:#8be9fd;font-style:italic">Andrew</span> <span style="color:#8be9fd;font-style:italic">Wilson</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">******************************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The environment to deploy the resources to.&#39;</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">allowed</span>([
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;dev&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;test&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;prod&#39;</span>
</span></span><span style="display:flex;"><span>])
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;dev&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Create a new Auth Provider in APIM Credential Manager</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">AuthorizationProvider</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/authorizationProviders@2024-05-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">oauth2</span>: {
</span></span><span style="display:flex;"><span>      ...
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">grantTypes</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">clientCredentials</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">loginUri</span>: <span style="color:#8be9fd;font-style:italic">az</span>.<span style="color:#50fa7b">environment</span>().<span style="color:#8be9fd;font-style:italic">authentication</span>.<span style="color:#8be9fd;font-style:italic">loginEndpoint</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>As always, have a play, and happy Bicep-ing!</p>
]]></content:encoded></item><item><title>Bicep Tips and Tricks | #8 | Agnostic Templates Through Config Files</title><link>https://andrewilson.co.uk/post/2025/09/bicep-tips-and-tricks-agnostic-templates-through-config-files/</link><pubDate>Wed, 03 Sep 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/09/bicep-tips-and-tricks-agnostic-templates-through-config-files/</guid><description>Overview Building on our previous exploration of Typed Variables, today we&amp;rsquo;re diving into one of my favorite patterns for creating maintainable and reusable Bicep templates: the Shared Variable File Pattern. This approach transforms your templates from being tightly coupled to specific configurations into truly agnostic, environment-ready solutions.
The beauty of this pattern lies in its simplicity - by extracting configuration data into external JSON or YAML files, you can create templates that adapt without modification.</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>Building on our previous exploration of <a href="/post/2025/08/bicep-tips-and-tricks-typed-variables/">Typed Variables</a>, today we&rsquo;re diving into one of my favorite patterns for creating maintainable and reusable Bicep templates: the <strong>Shared Variable File Pattern</strong>. This approach transforms your templates from being tightly coupled to specific configurations into truly agnostic, environment-ready solutions.</p>
<p>The beauty of this pattern lies in its simplicity - by extracting configuration data into external JSON or YAML files, you can create templates that adapt without modification. When combined with typed variables, this approach becomes even more powerful, providing compile-time validation and enhanced developer experience.</p>
<h2 id="why-use-config-files">Why Use Config Files?</h2>
<p>Before diving into the implementation, let&rsquo;s understand why this pattern is so valuable:</p>
<ol>
<li><strong>Separation of Concerns</strong>: Keep your infrastructure logic separate from configuration data</li>
<li><strong>Reduced Template Complexity</strong>: Remove large, complex variable definitions from your templates for better readability</li>
<li><strong>Reusability</strong>: Share common configurations across multiple templates without duplication</li>
<li><strong>Team Collaboration</strong>: Non-technical team members can modify configurations without touching Bicep code</li>
</ol>
<h2 id="the-pattern-in-action">The Pattern in Action</h2>
<h3 id="traditional-approach-what-we-want-to-avoid">Traditional Approach (What We Want to Avoid)</h3>
<p>Traditionally, you might embed all configuration directly in your Bicep template:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">nsgRules</span> = [
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;AllowManagementEndpoint&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">description</span>: <span style="color:#f1fa8c">&#39;Management endpoint for Azure portal and PowerShell&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">sourceAddressPrefix</span>: <span style="color:#f1fa8c">&#39;ApiManagement&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">sourcePortRange</span>: <span style="color:#f1fa8c">&#39;*&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">destinationAddressPrefix</span>: <span style="color:#f1fa8c">&#39;VirtualNetwork&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">destinationPortRange</span>: <span style="color:#f1fa8c">&#39;3443&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">protocol</span>: <span style="color:#f1fa8c">&#39;Tcp&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">access</span>: <span style="color:#f1fa8c">&#39;Allow&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">priority</span>: <span style="color:#8be9fd;font-style:italic">100</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">direction</span>: <span style="color:#f1fa8c">&#39;Inbound&#39;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;AllowAzureInfrastructureLoadBalancer&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">description</span>: <span style="color:#f1fa8c">&#39;Azure Infrastructure Load Balancer&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">sourceAddressPrefix</span>: <span style="color:#f1fa8c">&#39;AzureLoadBalancer&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">sourcePortRange</span>: <span style="color:#f1fa8c">&#39;*&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">destinationAddressPrefix</span>: <span style="color:#f1fa8c">&#39;VirtualNetwork&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">destinationPortRange</span>: <span style="color:#f1fa8c">&#39;6390&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">protocol</span>: <span style="color:#f1fa8c">&#39;Tcp&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">access</span>: <span style="color:#f1fa8c">&#39;Allow&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">priority</span>: <span style="color:#8be9fd;font-style:italic">110</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">direction</span>: <span style="color:#f1fa8c">&#39;Inbound&#39;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#6272a4">// ... many more rules</span>
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>This approach has several downsides:</p>
<ul>
<li>Templates become bloated and hard to read</li>
<li>Changes require modifying Bicep code</li>
<li>No separation between infrastructure logic and configuration data</li>
</ul>
<h3 id="modern-approach-with-config-files">Modern Approach with Config Files</h3>
<p>Let&rsquo;s transform this using the shared variable file pattern combined with typed variables.</p>
<h4 id="step-1-create-your-config-file">Step 1: Create Your Config File</h4>
<p>Create the configuration file for your deployment:</p>
<p><strong>configs/nsg-rules.json</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;securityRules&#34;</span>: [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;AllowManagementEndpoint&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;properties&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;description&#34;</span>: <span style="color:#f1fa8c">&#34;Management endpoint for Azure portal and PowerShell&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;sourceAddressPrefix&#34;</span>: <span style="color:#f1fa8c">&#34;ApiManagement&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;sourcePortRange&#34;</span>: <span style="color:#f1fa8c">&#34;*&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;destinationAddressPrefix&#34;</span>: <span style="color:#f1fa8c">&#34;VirtualNetwork&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;destinationPortRange&#34;</span>: <span style="color:#f1fa8c">&#34;3443&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;protocol&#34;</span>: <span style="color:#f1fa8c">&#34;Tcp&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;access&#34;</span>: <span style="color:#f1fa8c">&#34;Allow&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;priority&#34;</span>: <span style="color:#bd93f9">100</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;direction&#34;</span>: <span style="color:#f1fa8c">&#34;Inbound&#34;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;AllowDeveloperAccess&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;properties&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;description&#34;</span>: <span style="color:#f1fa8c">&#34;Allow developer access from corporate network&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;sourceAddressPrefix&#34;</span>: <span style="color:#f1fa8c">&#34;10.0.0.0/8&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;sourcePortRange&#34;</span>: <span style="color:#f1fa8c">&#34;*&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;destinationAddressPrefix&#34;</span>: <span style="color:#f1fa8c">&#34;VirtualNetwork&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;destinationPortRange&#34;</span>: <span style="color:#f1fa8c">&#34;443&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;protocol&#34;</span>: <span style="color:#f1fa8c">&#34;Tcp&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;access&#34;</span>: <span style="color:#f1fa8c">&#34;Allow&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;priority&#34;</span>: <span style="color:#bd93f9">200</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;direction&#34;</span>: <span style="color:#f1fa8c">&#34;Inbound&#34;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h4 id="step-2-define-typed-variables">Step 2: Define Typed Variables</h4>
<p>Create a user-defined type to ensure your config files match the expected structure:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// User-Defined Types</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ******************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">sealed</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Defines the structure for NSG security rule properties.&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">nsgSecurityRuleProperties</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Description of the security rule.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">description</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Source address prefix or tag.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">sourceAddressPrefix</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Source port range.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">sourcePortRange</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Destination address prefix or tag.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">destinationAddressPrefix</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Destination port range.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">destinationPortRange</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Network protocol (Tcp, Udp, or *).&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">protocol</span>: <span style="color:#f1fa8c">&#39;Tcp&#39;</span> | <span style="color:#f1fa8c">&#39;Udp&#39;</span> | <span style="color:#f1fa8c">&#39;*&#39;</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Access type (Allow or Deny).&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">access</span>: <span style="color:#f1fa8c">&#39;Allow&#39;</span> | <span style="color:#f1fa8c">&#39;Deny&#39;</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Priority value (100-4096).&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">priority</span>: <span style="color:#8be9fd;font-style:italic">int</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Direction (Inbound or Outbound).&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">direction</span>: <span style="color:#f1fa8c">&#39;Inbound&#39;</span> | <span style="color:#f1fa8c">&#39;Outbound&#39;</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">sealed</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Defines the structure for NSG security rule.&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">nsgSecurityRule</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Name of the security rule.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Properties of the security rule.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: <span style="color:#8be9fd;font-style:italic">nsgSecurityRuleProperties</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">sealed</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Defines the structure for NSG configuration file.&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">nsgConfig</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Array of security rules.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">securityRules</span>: <span style="color:#8be9fd;font-style:italic">nsgSecurityRule</span>[]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h4 id="step-3-load-and-use-configuration">Step 3: Load and Use Configuration</h4>
<p>Now your Bicep template becomes clean and agnostic:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#6272a4">// Parameters</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// **********</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Environment to deploy to.&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Variables</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *********</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Load configuration with full type safety</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">nsgConf</span> <span style="color:#8be9fd;font-style:italic">nsgConfig</span> = <span style="color:#50fa7b">loadJsonContent</span>(<span style="color:#f1fa8c">&#39;./configs/nsg-rules.json&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Resources</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *********</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">networkSecurityGroup</span> <span style="color:#f1fa8c">&#39;Microsoft.Network/networkSecurityGroups@2024-01-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;nsg-apim&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#50fa7b">resourceGroup</span>().<span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">securityRules</span>: <span style="color:#8be9fd;font-style:italic">nsgConfig</span>.<span style="color:#8be9fd;font-style:italic">securityRules</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="advanced-techniques">Advanced Techniques</h2>
<h3 id="multi-component-configuration-files">Multi-Component Configuration Files</h3>
<p>You can organise multiple configuration aspects in a single file and load only what you need:</p>
<p><strong>configs/apim-config.json</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;networking&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;securityRules&#34;</span>: [...],
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;subnets&#34;</span>: [...]
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;apim&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;sku&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;Developer&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;capacity&#34;</span>: <span style="color:#bd93f9">1</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;policies&#34;</span>: [...]
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;monitoring&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;logAnalytics&#34;</span>: {...},
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;alerts&#34;</span>: [...]
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Load specific sections using JSONPath:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#6272a4">// Load only the networking configuration</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">networkingConfig</span> = <span style="color:#50fa7b">loadJsonContent</span>(<span style="color:#f1fa8c">&#39;./configs/apim-config.json&#39;</span>, <span style="color:#f1fa8c">&#39;networking&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Load only the APIM configuration</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">apimConfig</span> = <span style="color:#50fa7b">loadJsonContent</span>(<span style="color:#f1fa8c">&#39;./configs/apim-config.json&#39;</span>, <span style="color:#f1fa8c">&#39;apim&#39;</span>)
</span></span></code></pre></div><h2 id="best-practices">Best Practices</h2>
<ol>
<li><strong>Use Typed Variables</strong>: Define user-defined types for your configuration structures - this provides compile-time validation and excellent IntelliSense support</li>
<li><strong>Logical File Organisation</strong>: Create separate config files for different aspects (networking, security, monitoring) rather than one monolithic file</li>
<li><strong>Validate Early</strong>: Let typed variables catch configuration mismatches during authoring, not deployment</li>
<li><strong>Size Considerations</strong>: Remember that loaded content is included in the generated ARM template (4MB limit)</li>
<li><strong>Version Control</strong>: Keep config files in source control alongside your Bicep templates</li>
</ol>
<h2 id="real-world-benefits">Real-World Benefits</h2>
<p>This pattern has transformed how I approach Bicep development:</p>
<ul>
<li><strong>Faster Development</strong>: IntelliSense support with typed variables makes configuration editing a breeze</li>
<li><strong>Fewer Deployment Failures</strong>: Compile-time validation catches configuration errors before deployment</li>
<li><strong>Better Team Collaboration</strong>: Operations teams can modify configs without touching infrastructure code</li>
<li><strong>Easier Maintenance</strong>: Changes to configuration don&rsquo;t require Bicep code modifications</li>
</ul>
<h2 id="summary">Summary</h2>
<p>The shared variable file pattern, enhanced with typed variables, creates a powerful combination for building maintainable, agnostic Bicep templates. By separating configuration from infrastructure logic, you gain flexibility, reusability, and compile-time safety that makes your Infrastructure as Code truly robust. Happy Bicep-ing!</p>
]]></content:encoded></item><item><title>Bicep Tips and Tricks | #7 | From Static to Dynamic Config</title><link>https://andrewilson.co.uk/post/2025/08/bicep-tips-and-tricks-static-to-dynamic-config/</link><pubDate>Wed, 27 Aug 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/08/bicep-tips-and-tricks-static-to-dynamic-config/</guid><description>Overview One of my core goals when writing IaC templates is ensuring reusability of common components, resources, and in this case, configuration. More often than not, I see configuration that is broadly common between resources (except for one or two properties) being duplicated throughout templates. This duplication means that changing a single property value requires updates across the entire codebase—a change that&amp;rsquo;s not trivial to manage unless you&amp;rsquo;re well-versed with the codebase and understand all areas where the configuration is implemented.</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>One of my core goals when writing IaC templates is ensuring reusability of common components, resources, and in this case, configuration. More often than not, I see configuration that is broadly common between resources (except for one or two properties) being duplicated throughout templates. This duplication means that changing a single property value requires updates across the entire codebase—a change that&rsquo;s not trivial to manage unless you&rsquo;re well-versed with the codebase and understand all areas where the configuration is implemented.</p>
<p>This pattern becomes particularly problematic as your infrastructure grows in complexity. Consider scenarios where you need to update a common tag value, modify a naming convention, or adjust a configuration parameter across dozens of resources. Without proper abstraction, these seemingly simple changes can become error-prone maintenance nightmares.</p>
<p>In this post, I&rsquo;ll demonstrate two powerful Bicep techniques to transform static, duplicated configuration into dynamic, reusable patterns that will make your templates more maintainable and less prone to configuration drift.</p>
<h2 id="common-configuration-scenarios">Common Configuration Scenarios</h2>
<p>Before diving into solutions, let&rsquo;s examine two common scenarios where static configuration creates maintenance headaches:</p>
<h3 id="scenario-1-resource-tagging">Scenario 1: Resource Tagging</h3>
<p>Every Azure resource should be properly tagged for governance, cost tracking, and management. However, I frequently see templates where tags are copy-pasted across resources, creating maintenance debt:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">storageAccount</span> <span style="color:#f1fa8c">&#39;Microsoft.Storage/storageAccounts@2025-01-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#6272a4">// ... other properties</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">tags</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">BusinessUnit</span>: <span style="color:#f1fa8c">&#39;BicepTipsAndTricks&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Version</span>: <span style="color:#50fa7b">deployment</span>().<span style="color:#8be9fd;font-style:italic">properties</span>.<span style="color:#8be9fd;font-style:italic">template</span>.<span style="color:#8be9fd;font-style:italic">contentVersion</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">environment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Region</span>: <span style="color:#8be9fd;font-style:italic">region</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">ResourceName</span>: <span style="color:#f1fa8c">&#39;Storage Account&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#39;hidden-title&#39;</span>: <span style="color:#f1fa8c">&#39;Bicep Tips and Tricks Storage&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#6272a4">// ... other properties</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This approach works for a single resource, but imagine maintaining this across 50+ resources. When the business unit name changes or you need to add a new compliance tag, you&rsquo;re looking at a significant refactoring effort.</p>
<h3 id="scenario-2-complex-configuration-structures">Scenario 2: Complex Configuration Structures</h3>
<p>Complex JSON configurations, such as Consumption Logic App workflow definitions, often contain embedded values that need to vary between environments or deployments:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;definition&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;actions&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;Condition_-_Morning_or_Afternoon&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;actions&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;Terminate_-_Afternoon&#34;</span>: {
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;inputs&#34;</span>: {
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;runStatus&#34;</span>: <span style="color:#f1fa8c">&#34;Succeeded&#34;</span>
</span></span><span style="display:flex;"><span>                        },
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;runAfter&#34;</span>: {},
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Terminate&#34;</span>
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                },
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;else&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;actions&#34;</span>: {
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;Terminate_-_Morning&#34;</span>: {
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;inputs&#34;</span>: {
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;runStatus&#34;</span>: <span style="color:#f1fa8c">&#34;Succeeded&#34;</span>
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;runAfter&#34;</span>: {},
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Terminate&#34;</span>
</span></span><span style="display:flex;"><span>                        }
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                },
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;expression&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;and&#34;</span>: [
</span></span><span style="display:flex;"><span>                        {
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;greaterOrEquals&#34;</span>: [
</span></span><span style="display:flex;"><span>                                <span style="color:#f1fa8c">&#34;@utcNow(&#39;H:mm:ss&#39;)&#34;</span>,
</span></span><span style="display:flex;"><span>                                <span style="color:#f1fa8c">&#34;12:00:00&#34;</span> <span style="color:#6272a4">// ← Currently hard-coded static configuration
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>                            ]
</span></span><span style="display:flex;"><span>                        }
</span></span><span style="display:flex;"><span>                    ]
</span></span><span style="display:flex;"><span>                },
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;runAfter&#34;</span>: {},
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;If&#34;</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;outputs&#34;</span>: {},
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;parameters&#34;</span>: {},
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;triggers&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;Recurrence_-_Start&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;evaluatedRecurrence&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;frequency&#34;</span>: <span style="color:#f1fa8c">&#34;Day&#34;</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;interval&#34;</span>: <span style="color:#bd93f9">1</span>, <span style="color:#6272a4">// ← Currently hard-coded static configuration
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>                    <span style="color:#ff79c6">&#34;schedule&#34;</span>: {
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;hours&#34;</span>: [
</span></span><span style="display:flex;"><span>                            <span style="color:#f1fa8c">&#34;11&#34;</span>
</span></span><span style="display:flex;"><span>                        ]
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                },
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;recurrence&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;frequency&#34;</span>: <span style="color:#f1fa8c">&#34;Hour&#34;</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;interval&#34;</span>: <span style="color:#f1fa8c">&#34;2&#34;</span> <span style="color:#6272a4">// ← Currently hard-coded static configuration
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>                },
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Recurrence&#34;</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;parameters&#34;</span>: {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>In this Consumption Logic App workflow, the recurrence interval, evaluatedRecurrence interval, and condition time values are hard-coded. If you need different intervals or values for different environments (perhaps more frequent polling in production), you&rsquo;d need to maintain separate configuration files or manually edit values during deployment.</p>
<h2 id="my-recommended-approaches">My Recommended Approaches</h2>
<h3 id="approach-1-union-function-for-object-composition">Approach 1: Union Function for Object Composition</h3>
<p>The <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-object#union"><code>union()</code></a> function is perfect for combining base configuration with resource-specific overrides. This approach works exceptionally well for scenarios like tagging, where you have a core set of common properties and some resource-specific additions.</p>
<p><strong>The Problem:</strong> Without union, you end up repeating common tags across every resource:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#6272a4">// Bad: Repeated configuration</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">storageAccount</span> <span style="color:#f1fa8c">&#39;Microsoft.Storage/storageAccounts@2025-01-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">tags</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">BusinessUnit</span>: <span style="color:#f1fa8c">&#39;BicepTipsAndTricks&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Version</span>: <span style="color:#50fa7b">deployment</span>().<span style="color:#8be9fd;font-style:italic">properties</span>.<span style="color:#8be9fd;font-style:italic">template</span>.<span style="color:#8be9fd;font-style:italic">contentVersion</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">environment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Region</span>: <span style="color:#8be9fd;font-style:italic">region</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">ResourceName</span>: <span style="color:#f1fa8c">&#39;Storage Account&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#39;hidden-title&#39;</span>: <span style="color:#f1fa8c">&#39;Bicep Tips and Tricks Storage&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">keyVault</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults@2023-07-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">tags</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">BusinessUnit</span>: <span style="color:#f1fa8c">&#39;BicepTipsAndTricks&#39;</span>  <span style="color:#6272a4">// Duplicated!</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Version</span>: <span style="color:#50fa7b">deployment</span>().<span style="color:#8be9fd;font-style:italic">properties</span>.<span style="color:#8be9fd;font-style:italic">template</span>.<span style="color:#8be9fd;font-style:italic">contentVersion</span>  <span style="color:#6272a4">// Duplicated!</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">environment</span>  <span style="color:#6272a4">// Duplicated!</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Region</span>: <span style="color:#8be9fd;font-style:italic">region</span>  <span style="color:#6272a4">// Duplicated!</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">ResourceName</span>: <span style="color:#f1fa8c">&#39;Key Vault&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#39;hidden-title&#39;</span>: <span style="color:#f1fa8c">&#39;Bicep Tips and Tricks Key Vault&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>The Solution:</strong> Use union to combine base and specific configurations:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#6272a4">// Good: Centralized common configuration</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The environment to deploy to&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;dev&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The Azure region to deploy to&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">region</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;UkSouth&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">coreTags</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">BusinessUnit</span>: <span style="color:#f1fa8c">&#39;BicepTipsAndTricks&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">Version</span>: <span style="color:#50fa7b">deployment</span>().<span style="color:#8be9fd;font-style:italic">properties</span>.<span style="color:#8be9fd;font-style:italic">template</span>.<span style="color:#8be9fd;font-style:italic">contentVersion</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">environment</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">Region</span>: <span style="color:#8be9fd;font-style:italic">region</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Create resource-specific tag combinations</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">storageAccountTags</span> = <span style="color:#50fa7b">union</span>(<span style="color:#8be9fd;font-style:italic">coreTags</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">ResourceName</span>: <span style="color:#f1fa8c">&#39;Storage Account&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;hidden-title&#39;</span>: <span style="color:#f1fa8c">&#39;Bicep Tips and Tricks Storage&#39;</span>
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">keyVaultTags</span> = <span style="color:#50fa7b">union</span>(<span style="color:#8be9fd;font-style:italic">coreTags</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">ResourceName</span>: <span style="color:#f1fa8c">&#39;Key Vault&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;hidden-title&#39;</span>: <span style="color:#f1fa8c">&#39;Bicep Tips and Tricks Key Vault&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">DataClassification</span>: <span style="color:#f1fa8c">&#39;Confidential&#39;</span>  <span style="color:#6272a4">// Additional tag specific to Key Vault</span>
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">storageAccount</span> <span style="color:#f1fa8c">&#39;Microsoft.Storage/storageAccounts@2025-01-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;mystorageaccount&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">region</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">tags</span>: <span style="color:#8be9fd;font-style:italic">storageAccountTags</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">sku</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;Standard_LRS&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">kind</span>: <span style="color:#f1fa8c">&#39;StorageV2&#39;</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">keyVault</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults@2023-07-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;mykeyvault&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">region</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">tags</span>: <span style="color:#8be9fd;font-style:italic">keyVaultTags</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">sku</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">family</span>: <span style="color:#f1fa8c">&#39;A&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;standard&#39;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">tenantId</span>: <span style="color:#50fa7b">subscription</span>().<span style="color:#8be9fd;font-style:italic">tenantId</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>Key Benefits:</strong></p>
<ul>
<li><strong>Single source of truth</strong>: Common tags are defined once in <code>coreTags</code></li>
<li><strong>Easy maintenance</strong>: Update business unit name? Change it in one place</li>
<li><strong>Flexible</strong>: Each resource can still have unique tags</li>
</ul>
<h3 id="approach-2-token-replacement-for-complex-configurations">Approach 2: Token Replacement for Complex Configurations</h3>
<p>For complex JSON configurations that need dynamic values, token replacement provides an elegant solution. This approach is particularly powerful when working with imported JSON files that contain configuration.</p>
<p><strong>The Problem:</strong> Static values embedded in complex configurations:</p>
<p><strong>The Solution:</strong> Use token placeholders and replacement functions.</p>
<p>First, modify your JSON configuration file to use tokens:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;definition&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;actions&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;Condition_-_Morning_or_Afternoon&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;actions&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;Terminate_-_Afternoon&#34;</span>: {
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;inputs&#34;</span>: {
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;runStatus&#34;</span>: <span style="color:#f1fa8c">&#34;Succeeded&#34;</span>
</span></span><span style="display:flex;"><span>                        },
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;runAfter&#34;</span>: {},
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Terminate&#34;</span>
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                },
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;else&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;actions&#34;</span>: {
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;Terminate_-_Morning&#34;</span>: {
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;inputs&#34;</span>: {
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;runStatus&#34;</span>: <span style="color:#f1fa8c">&#34;Succeeded&#34;</span>
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;runAfter&#34;</span>: {},
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Terminate&#34;</span>
</span></span><span style="display:flex;"><span>                        }
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                },
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;expression&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;and&#34;</span>: [
</span></span><span style="display:flex;"><span>                        {
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;greaterOrEquals&#34;</span>: [
</span></span><span style="display:flex;"><span>                                <span style="color:#f1fa8c">&#34;@utcNow(&#39;H:mm:ss&#39;)&#34;</span>,
</span></span><span style="display:flex;"><span>                                <span style="color:#f1fa8c">&#34;__schedule_time__&#34;</span>  <span style="color:#6272a4">// ← Token for schedule time
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>                            ]
</span></span><span style="display:flex;"><span>                        }
</span></span><span style="display:flex;"><span>                    ]
</span></span><span style="display:flex;"><span>                },
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;runAfter&#34;</span>: {},
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;If&#34;</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;outputs&#34;</span>: {},
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;parameters&#34;</span>: {},
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;triggers&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;Recurrence_-_Start&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;evaluatedRecurrence&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;frequency&#34;</span>: <span style="color:#f1fa8c">&#34;Day&#34;</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;interval&#34;</span>: <span style="color:#bd93f9">1</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;schedule&#34;</span>: {
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;hours&#34;</span>: [
</span></span><span style="display:flex;"><span>                            <span style="color:#f1fa8c">&#34;__schedule_hour__&#34;</span>  <span style="color:#6272a4">// ← Token for schedule hour
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>                        ]
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                },
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;recurrence&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;frequency&#34;</span>: <span style="color:#f1fa8c">&#34;Hour&#34;</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;interval&#34;</span>: <span style="color:#f1fa8c">&#34;__interval__&#34;</span>  <span style="color:#6272a4">// ← Token for interval
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>                },
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Recurrence&#34;</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;parameters&#34;</span>: {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Then, create a robust token replacement system in your Bicep template:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#6272a4">// ** Imported Types and Functions **</span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Token Replacement Definition&#39;</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">sealed</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">TokenReplacement</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Token to be replaced&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">token</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Replacement value&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">replacement</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;String Tokens Replacement Function&#39;</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">stringTokensReplacement</span>(<span style="color:#8be9fd;font-style:italic">stringValue</span> <span style="color:#8be9fd;font-style:italic">string</span>, <span style="color:#8be9fd;font-style:italic">tokenReplacements</span> <span style="color:#8be9fd;font-style:italic">TokenReplacement</span>[]) <span style="color:#8be9fd;font-style:italic">string</span> =&gt;
</span></span><span style="display:flex;"><span>  <span style="color:#50fa7b">reduce</span>(<span style="color:#8be9fd;font-style:italic">tokenReplacements</span>, <span style="color:#8be9fd;font-style:italic">stringValue</span>, (<span style="color:#8be9fd;font-style:italic">current</span>, <span style="color:#8be9fd;font-style:italic">next</span>) =&gt; <span style="color:#50fa7b">replace</span>(<span style="color:#50fa7b">string</span>(<span style="color:#8be9fd;font-style:italic">current</span>), <span style="color:#8be9fd;font-style:italic">next</span>.<span style="color:#8be9fd;font-style:italic">token</span>, <span style="color:#8be9fd;font-style:italic">next</span>.<span style="color:#8be9fd;font-style:italic">replacement</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The interval for the Logic App trigger&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">logicAppInterval</span> <span style="color:#8be9fd;font-style:italic">int</span> = <span style="color:#8be9fd;font-style:italic">2</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The schedule hour for the Logic App trigger&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">scheduleHour</span> <span style="color:#8be9fd;font-style:italic">int</span> = <span style="color:#8be9fd;font-style:italic">11</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The schedule time for condition comparison&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">scheduleTime</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;12:00:00&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The environment for deployment&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;dev&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">logicAppConsumptionWorkflow</span> = <span style="color:#50fa7b">loadJsonContent</span>(<span style="color:#f1fa8c">&#39;./logicAppConsumptionWorkflow.json&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Define all token replacements in one place</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">tokenReplacements</span> = [
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">token</span>: <span style="color:#f1fa8c">&#39;__interval__&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">replacement</span>: <span style="color:#50fa7b">string</span>(<span style="color:#8be9fd;font-style:italic">logicAppInterval</span>)
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">token</span>: <span style="color:#f1fa8c">&#39;__schedule_hour__&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">replacement</span>: <span style="color:#50fa7b">string</span>(<span style="color:#8be9fd;font-style:italic">scheduleHour</span>)
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">token</span>: <span style="color:#f1fa8c">&#39;__schedule_time__&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">replacement</span>: <span style="color:#8be9fd;font-style:italic">scheduleTime</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Apply all token replacements</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">processedWorkflowDefinition</span> = <span style="color:#50fa7b">json</span>(<span style="color:#50fa7b">stringTokensReplacement</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#50fa7b">string</span>(<span style="color:#8be9fd;font-style:italic">logicAppConsumptionWorkflow</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">tokenReplacements</span>
</span></span><span style="display:flex;"><span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">logicApp</span> <span style="color:#f1fa8c">&#39;Microsoft.Logic/workflows@2023-12-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;my-dynamic-logic-app-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">environment</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#50fa7b">resourceGroup</span>().<span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">definition</span>: <span style="color:#8be9fd;font-style:italic">processedWorkflowDefinition</span>.<span style="color:#8be9fd;font-style:italic">definition</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">parameters</span>: <span style="color:#8be9fd;font-style:italic">processedWorkflowDefinition</span>.<span style="color:#8be9fd;font-style:italic">parameters</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>Key Benefits:</strong></p>
<ul>
<li><strong>Environment-specific values</strong>: Different intervals for dev/test/prod</li>
<li><strong>Centralized configuration</strong>: All replacements defined in one place</li>
<li><strong>Scalable</strong>: Easy to add new tokens as requirements grow</li>
<li><strong>Version control friendly</strong>: JSON templates remain clean and readable</li>
</ul>
<h2 id="summary">Summary</h2>
<p>Transforming static configuration into dynamic, reusable patterns is essential for maintainable Infrastructure as Code. Use the <code>union()</code> function when combining common configuration with resource-specific properties (like tags), and token replacement for complex JSON configurations that need dynamic values injected. Both approaches centralize configuration management, reduce duplication, and make your Bicep templates more maintainable across environments. The next time you find yourself copy-pasting configuration, choose the right pattern and eliminate that technical debt!</p>
]]></content:encoded></item><item><title>Bicep Tips and Tricks | #6 | Typed Variables</title><link>https://andrewilson.co.uk/post/2025/08/bicep-tips-and-tricks-typed-variables/</link><pubDate>Wed, 20 Aug 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/08/bicep-tips-and-tricks-typed-variables/</guid><description>Overview In late May this year, an exciting but semi overlooked feature was released, and I absolutely love it - Typed Variables!
Prior to this release, variable types were inferred through the value, which is fine for most statically defined content within a template, but there are cases that I will go through in this post where typing your variables really does make a big difference.
Why Use Typed Variables Before I get going, let me walk through why you should consider using typed variables:</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>In late <a href="https://github.com/Azure/bicep/releases/tag/v0.36.1">May this year</a>, an exciting but semi overlooked feature was released, and I absolutely love it - <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/variables#typed-variables">Typed Variables</a>!</p>
<p>Prior to this release, variable types were inferred through the value, which is fine for most statically defined content within a template, but there are cases that I will go through in this post where typing your variables really does make a big difference.</p>
<h2 id="why-use-typed-variables">Why Use Typed Variables</h2>
<p>Before I get going, let me walk through why you should consider using typed variables:</p>
<ol>
<li><strong>Fail-Fast Error Detection</strong>: Typed variables enable the Bicep compiler to validate assigned values against declared types during authoring and compilation. This helps catch mistakes early, reducing deployment failures.</li>
<li><strong>Clearer Intent</strong>: Declaring types communicates your intent directly in the code, making it obvious how each variable should be used and what kind of data it should hold.</li>
<li><strong>Enhanced IntelliSense Support</strong>: Editors like Visual Studio Code offer richer autocompletion and validation for typed variables, speeding up development and reducing errors.</li>
<li><strong>Safer Refactoring</strong>: When you change variable values or types, the compiler immediately flags mismatches, making refactoring safer and more predictable.</li>
</ol>
<h2 id="setup">Setup</h2>
<p>Defining a typed variable is as simple as the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#ff79c6">&lt;</span><span style="color:#8be9fd;font-style:italic">variable</span><span style="color:#ff79c6">-</span><span style="color:#8be9fd;font-style:italic">name</span><span style="color:#ff79c6">&gt;</span> <span style="color:#ff79c6">&lt;</span><span style="color:#8be9fd;font-style:italic">data</span><span style="color:#ff79c6">-</span><span style="color:#8be9fd;font-style:italic">type</span><span style="color:#ff79c6">&gt;</span> = <span style="color:#ff79c6">&lt;</span><span style="color:#8be9fd;font-style:italic">variable</span><span style="color:#ff79c6">-</span><span style="color:#8be9fd;font-style:italic">value</span><span style="color:#ff79c6">&gt;</span>
</span></span></code></pre></div><blockquote>
<p>⚠️ <strong>Note</strong>: Requires Bicep version <code>0.36.X</code> or later.</p>
</blockquote>
<p>Typed variables support both the standard set of base data types - string, int, bool, object, array - or for the more advanced, <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/user-defined-data-types">User-defined data types</a> and <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/user-defined-data-types#resource-derived-types">Resource-derived types</a>.</p>
<h2 id="my-top-two-use-cases">My Top Two Use Cases</h2>
<h3 id="1-clear-intent">1. Clear Intent</h3>
<p>The first major use case for typed variables is improving code clarity and maintainability, especially when working with functions that return complex objects. Without type declarations, it can be difficult to understand what a function returns or how to properly use the resulting variable.</p>
<p>Consider this example with a custom function:</p>
<blockquote>
<p>Un-Typed Variable</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Functions **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">myFunction</span>(<span style="color:#8be9fd;font-style:italic">param1</span> <span style="color:#8be9fd;font-style:italic">string</span>, <span style="color:#8be9fd;font-style:italic">param2</span> <span style="color:#8be9fd;font-style:italic">int</span>) <span style="color:#8be9fd;font-style:italic">object</span> =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">property1</span>: <span style="color:#8be9fd;font-style:italic">param1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">property2</span>: <span style="color:#8be9fd;font-style:italic">param2</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Un-typed - No IntelliSense on variable use or expectation of variable type.</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">variable</span> = <span style="color:#50fa7b">myFunction</span>(<span style="color:#f1fa8c">&#39;example&#39;</span>, <span style="color:#8be9fd;font-style:italic">42</span>)
</span></span></code></pre></div><p>In this un-typed scenario, several issues arise:</p>
<ul>
<li><strong>Unclear return type</strong>: The function returns a generic <code>object</code>, making it unclear what properties are available</li>
<li><strong>No IntelliSense support</strong>: When using <code>variable.</code>, your editor can&rsquo;t help you with autocompletion</li>
<li><strong>Hidden intent</strong>: Other developers (or future you) must examine the function implementation to understand what it returns</li>
<li><strong>Error-prone usage</strong>: Typos in property names won&rsquo;t be caught until deployment time</li>
</ul>
<p>Now let&rsquo;s see how typed variables solve these problems:</p>
<blockquote>
<p>Typed Variable</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// User Defined-Types</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *********************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">sealed</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Defines the structure for myFunction output.&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">myFunctionOutputType</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The first property of the output.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">property1</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The second property of the output.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">property2</span>: <span style="color:#8be9fd;font-style:italic">int</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Functions **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">myFunction</span>(<span style="color:#8be9fd;font-style:italic">param1</span> <span style="color:#8be9fd;font-style:italic">string</span>, <span style="color:#8be9fd;font-style:italic">param2</span> <span style="color:#8be9fd;font-style:italic">int</span>) <span style="color:#8be9fd;font-style:italic">myFunctionOutputType</span> =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">property1</span>: <span style="color:#8be9fd;font-style:italic">param1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">property2</span>: <span style="color:#8be9fd;font-style:italic">param2</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Typed - Includes IntelliSense, code clarity, and refactor safety on variable use.</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">typedVariable</span> <span style="color:#8be9fd;font-style:italic">myFunctionOutputType</span> = <span style="color:#50fa7b">myFunction</span>(<span style="color:#f1fa8c">&#39;example&#39;</span>, <span style="color:#8be9fd;font-style:italic">42</span>)
</span></span></code></pre></div><p>The typed approach delivers immediate improvements:</p>
<ul>
<li><strong>Crystal clear intent</strong>: The type definition explicitly documents what the function returns and what each property represents</li>
<li><strong>Enhanced developer experience</strong>: Full IntelliSense support when working with the variable, including property names and descriptions</li>
<li><strong>Compile-time safety</strong>: Any mismatch between the function&rsquo;s actual return value and the declared type will be caught during authoring and compilation</li>
<li><strong>Better maintainability</strong>: Changes to the function&rsquo;s return structure must be reflected in the type definition, ensuring consistency across the codebase</li>
<li><strong>Team collaboration</strong>: New team members can quickly understand the data structure without diving into function implementations</li>
</ul>
<p>This pattern is especially valuable in larger Bicep templates where functions might be defined in one section and used much later in the file, or using imported functions as discussed earlier in the series.</p>
<h3 id="2-file-functions">2. File functions</h3>
<p>One of the most powerful applications of typed variables is when working with Bicep&rsquo;s file functions such as <code>loadJsonContent()</code> and <code>loadYamlContent()</code>. Without typed variables, these functions return as an <code>Any object</code>, providing no compile-time validation for the loaded content.</p>
<p>Let&rsquo;s look at a practical example where we&rsquo;re loading configuration data from an external JSON file:</p>
<blockquote>
<p>JSON config file</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-JSON" data-lang="JSON"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>	<span style="color:#ff79c6">&#34;sku&#34;</span>: {
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;Standard&#34;</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">&#34;tier&#34;</span>: <span style="color:#f1fa8c">&#34;Standard&#34;</span>
</span></span><span style="display:flex;"><span>	},
</span></span><span style="display:flex;"><span>	<span style="color:#ff79c6">&#34;kind&#34;</span>: <span style="color:#f1fa8c">&#34;StorageV2&#34;</span>,
</span></span><span style="display:flex;"><span>	<span style="color:#ff79c6">&#34;properties&#34;</span>: {
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">&#34;supportsHttpsTrafficOnly&#34;</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>Un-Typed Variable</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">resourceConfig</span> = <span style="color:#50fa7b">loadJsonContent</span>(<span style="color:#f1fa8c">&#39;./Config/resource.config.json&#39;</span>)
</span></span></code></pre></div><p>With the un-typed approach above, <code>resourceConfig</code> is treated as a generic <code>object</code>. This means:</p>
<ul>
<li>No compile-time validation of the JSON structure</li>
<li>No documentation about what the configuration should contain</li>
</ul>
<p>Now let&rsquo;s see how typed variables transform this experience:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#6272a4">// User Defined-Types</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *********************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">sealed</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Defines the structure for importing a JSON File resource with SKU, kind, and properties.&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">resourceImportType</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The SKU details for the resource.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">sku</span>: {
</span></span><span style="display:flex;"><span>    @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The name of the SKU.&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>    @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The tier of the SKU (e.g., Standard, Premium).&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">tier</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The kind of the resource.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">kind</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The properties of the resource.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Indicates whether only HTTPS traffic is supported.&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">supportsHttpsTrafficOnly</span>: <span style="color:#8be9fd;font-style:italic">bool</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">typedResourceConfig</span> <span style="color:#8be9fd;font-style:italic">resourceImportType</span> = <span style="color:#50fa7b">loadJsonContent</span>(<span style="color:#f1fa8c">&#39;./Config/resource.config.json&#39;</span>)
</span></span></code></pre></div><p>With the typed variable approach, we gain several significant benefits:</p>
<ul>
<li><strong>Compile-time validation</strong>: If the JSON file doesn&rsquo;t match the defined structure, Bicep will catch this during authoring and compilation</li>
<li><strong>Rich IntelliSense</strong>: When you type <code>typedResourceConfig.</code>, your editor will show you the available properties with their descriptions</li>
<li><strong>Self-documenting code</strong>: The type definition serves as living documentation of the expected configuration structure</li>
<li><strong>Refactoring safety</strong>: If you change the type definition, all usages will be validated automatically</li>
</ul>
<p>This approach is particularly valuable when working with complex configuration files or when multiple team members need to understand the expected data structure.</p>
<h2 id="summary">Summary</h2>
<p>Typed variables are a game changer for Bicep development, offering compile-time validation, enhanced IntelliSense, and self-documenting code that makes your templates more robust and maintainable. Make sure to give them a try, and happy Bicep-ing!</p>
]]></content:encoded></item><item><title>Bicep Tips and Tricks | #5 | From Documentation to Deployment-Time Validation: Conditional Parameter Requirements</title><link>https://andrewilson.co.uk/post/2025/08/bicep-tips-and-tricks-conditional-mandatory-parameters/</link><pubDate>Wed, 13 Aug 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/08/bicep-tips-and-tricks-conditional-mandatory-parameters/</guid><description>Overview In Bicep templates, we sometimes encounter scenarios where certain parameters should be mandatory based on the value of another parameter. For example, when deploying to production environments, you might require additional configuration parameters that are optional for development environments.
One of the common ways in which I have seen this handled is through documentation, which can lead to deployment failures and inconsistent configurations across environments. This post explores how to implement fail-fast validation using Bicep&amp;rsquo;s built-in capabilities to enforce these conditional requirements at deployment time.</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>In Bicep templates, we sometimes encounter scenarios where certain parameters should be mandatory based on the value of another parameter. For example, when deploying to production environments, you might require additional configuration parameters that are optional for development environments.</p>
<p>One of the common ways in which I have seen this handled is through documentation, which can lead to deployment failures and inconsistent configurations across environments. This post explores how to implement fail-fast validation using Bicep&rsquo;s built-in capabilities to enforce these conditional requirements at deployment time.</p>
<h3 id="example-of-a-documentation-only-approach">Example of a Documentation-Only Approach</h3>
<p>Handling conditional parameter requirements through documentation alone:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Environment to deploy to&#39;</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">allowed</span>([
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;dev&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;test&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;prod&#39;</span>
</span></span><span style="display:flex;"><span>])
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Resource configuration - REQUIRED for prod environment!&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">resourceConfig</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Additional parameters that should be required in prod</span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Monitoring configuration - REQUIRED for prod environment!&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">monitoringConfig</span> <span style="color:#8be9fd;font-style:italic">object</span> = {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Backup configuration - REQUIRED for prod environment!&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">backupRetentionDays</span> <span style="color:#8be9fd;font-style:italic">int</span> = <span style="color:#8be9fd;font-style:italic">0</span>
</span></span></code></pre></div><p><strong>Problems with this approach:</strong></p>
<ul>
<li>No enforcement at deployment time</li>
<li>Deployments can succeed with missing critical configuration</li>
<li>Relies on human memory and documentation</li>
<li>Inconsistent environments due to missing parameters</li>
<li>Production issues from misconfiguration</li>
</ul>
<h2 id="recommended-approach-fail-fast-validation">Recommended Approach: Fail-Fast Validation</h2>
<p>I would recommend using the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-flow-control#fail">Bicep <code>fail()</code></a> function combined with conditional logic to validate parameters early in the deployment process. This approach provides immediate feedback and prevents deployments with invalid configurations, such as in the following examples:</p>
<ul>
<li>Production environments - Storage deployments requiring backup retention settings for production</li>
<li>Key Vault requiring network access rules when public access is disabled</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Environment to deploy to&#39;</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">allowed</span>([
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;dev&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;test&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;prod&#39;</span>
</span></span><span style="display:flex;"><span>])
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;&#39;&#39;Resource Configuration:
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">                - **Optional** When environment is dev / test.
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">                - **Required** When environment is prod.&#39;&#39;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">resourceConfig</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Resource Configuration Validation Result&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">resourceConfig_Result</span> = <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#ff79c6">==</span> <span style="color:#f1fa8c">&#39;prod&#39;</span> <span style="color:#ff79c6">&amp;&amp;</span> <span style="color:#50fa7b">empty</span>(<span style="color:#8be9fd;font-style:italic">resourceConfig</span>)
</span></span><span style="display:flex;"><span>  ? <span style="color:#50fa7b">fail</span>(<span style="color:#f1fa8c">&#39;resourceConfig is required to deploy this template in the prod Environment.&#39;</span>)
</span></span><span style="display:flex;"><span>  : {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">value</span>: <span style="color:#8be9fd;font-style:italic">resourceConfig</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">valueProvided</span>: <span style="color:#ff79c6">!</span><span style="color:#50fa7b">empty</span>(<span style="color:#8be9fd;font-style:italic">resourceConfig</span>)
</span></span><span style="display:flex;"><span>    }
</span></span></code></pre></div><blockquote>
<p>⚠️ <strong>Note</strong>: Make deployment failures informative and actionable, helping your team understand exactly what&rsquo;s needed to fix the issue.</p>
</blockquote>
<h3 id="why-this-approach-is-useful">Why this approach is useful</h3>
<ul>
<li><strong>Enforces deployment rules</strong>: Prevents deployment in &ldquo;prod&rdquo; if critical configuration is missing, ensuring required configuration is provided before any resources are created.</li>
<li><strong>Fail-fast validation</strong>: Catches configuration errors at the start of deployment, saving time and preventing partial deployments.</li>
<li><strong>Conditional resource creation</strong>: You can use the <code>valueProvided</code> property to conditionally deploy resources or set properties only when configuration is supplied.</li>
<li><strong>Parameter validation</strong>: Centralizes validation logic, making it easier to maintain and extend checks for other environments or parameters in the future.</li>
<li><strong>Compliance requirements</strong>: Ensures production environments always meet security and operational standards.</li>
</ul>
<h2 id="summary">Summary</h2>
<p>Conditional mandatory parameters in Bicep templates are a powerful way to enforce deployment standards and prevent configuration errors. Remember to make deployment failures informative and actionable, helping your team understand exactly what&rsquo;s needed to fix the issue. Hope this helps, and happy Bicep-ing!</p>
]]></content:encoded></item><item><title>Bicep Tips and Tricks | #4 | Shared Variables</title><link>https://andrewilson.co.uk/post/2025/08/bicep-tips-and-tricks-shared-variables/</link><pubDate>Wed, 06 Aug 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/08/bicep-tips-and-tricks-shared-variables/</guid><description>Overview This week is a simple one, but works wonders in maintainability and consistency.
There are often cases where you will need to define static values that don&amp;rsquo;t change frequently, if at all, and more importantly, you seem to be setting these up frequently for multiple templates. Here are some examples that I have seen:
Multi-Environment Deployments
Different environments (dev, staging, prod) that share common configuration but need environment-specific values:</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>This week is a simple one, but works wonders in maintainability and consistency.</p>
<p>There are often cases where you will need to define static values that don&rsquo;t change frequently, if at all, and more importantly, you seem to be setting these up frequently for multiple templates. Here are some examples that I have seen:</p>
<ol>
<li>
<p>Multi-Environment Deployments</p>
<p>Different environments (dev, staging, prod) that share common configuration but need environment-specific values:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">environmentConfig</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">dev</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">sku</span>: <span style="color:#f1fa8c">&#39;Basic&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">staging</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">sku</span>: <span style="color:#f1fa8c">&#39;Standard&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">prod</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">sku</span>: <span style="color:#f1fa8c">&#39;Premium&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
<li>
<p>Organisational Standards</p>
<p>When you need to enforce company-wide conventions and policies.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">mandatoryTags</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">costCenter</span>: <span style="color:#f1fa8c">&#39;IT-001&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">dataClassification</span>: <span style="color:#f1fa8c">&#39;internal&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">businessUnit</span>: <span style="color:#f1fa8c">&#39;engineering&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">version</span>: <span style="color:#50fa7b">deployment</span>().<span style="color:#8be9fd;font-style:italic">properties</span>.<span style="color:#8be9fd;font-style:italic">template</span>.<span style="color:#8be9fd;font-style:italic">contentVersion</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
<li>
<p>Object or Resource Configurations</p>
<p>When you have configurations that would be repeated across multiple templates.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">subnetConfigurations</span> = [
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;web-subnet&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">addressPrefix</span>: <span style="color:#f1fa8c">&#39;10.0.1.0/24&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">serviceEndpoints</span>: [<span style="color:#f1fa8c">&#39;Microsoft.Storage&#39;</span>, <span style="color:#f1fa8c">&#39;Microsoft.KeyVault&#39;</span>]
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;api-subnet&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">addressPrefix</span>: <span style="color:#f1fa8c">&#39;10.0.2.0/24&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">serviceEndpoints</span>: [<span style="color:#f1fa8c">&#39;Microsoft.Sql&#39;</span>, <span style="color:#f1fa8c">&#39;Microsoft.KeyVault&#39;</span>]
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// OR</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefId</span> = <span style="color:#f1fa8c">&#39;4633458b-17de-408a-b874-0445c86b69e6&#39;</span>
</span></span></code></pre></div></li>
</ol>
<p>You might think, &ldquo;Hey, the values don&rsquo;t change very often, so what&rsquo;s the big deal? So what if I set them up many times across my templates?&rdquo; Not to be a pessimist, but it&rsquo;s usually at this point that Murphy&rsquo;s Law comes into effect and the values will need to change! Would you not rather there be a single point where you can update these values and they ripple through your templates like a stone hitting water?</p>
<p>Well, you can. Simply put, <strong>Shared Variables</strong>.</p>
<h2 id="recommended-approach">Recommended Approach</h2>
<p>My recommended approach is to leverage <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-import">Bicep Imports</a>. Create a Common folder within your <code>IaC</code> directory, and then a <code>variables.bicep</code> file within:</p>
<pre tabindex="0"><code>IaC
  ↳ Common
    ↳ variables.bicep
  ↳ main.bicep
</code></pre><p>This will form the basis of your shared variables that can be used across your deployment templates.</p>
<p>Then find all the relevant candidates and move these across to the shared variables template. Include the <code>@export()</code> decorator to each variable; this is used to allow the variable to be imported into other Bicep files.</p>
<blockquote>
<p>For Example</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#ff79c6">/**********************************</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Shared</span> <span style="color:#8be9fd;font-style:italic">Variables</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">Author</span>: <span style="color:#8be9fd;font-style:italic">Andrew</span> <span style="color:#8be9fd;font-style:italic">Wilson</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">***********************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Shared Variable for environment configurations&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">environmentConfig</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">dev</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">sku</span>: <span style="color:#f1fa8c">&#39;Basic&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">staging</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">sku</span>: <span style="color:#f1fa8c">&#39;Standard&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">prod</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">sku</span>: <span style="color:#f1fa8c">&#39;Premium&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Shared Variable for tagging resources&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">mandatoryTags</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">costCenter</span>: <span style="color:#f1fa8c">&#39;IT-001&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">dataClassification</span>: <span style="color:#f1fa8c">&#39;internal&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">businessUnit</span>: <span style="color:#f1fa8c">&#39;engineering&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">version</span>: <span style="color:#50fa7b">deployment</span>().<span style="color:#8be9fd;font-style:italic">properties</span>.<span style="color:#8be9fd;font-style:italic">template</span>.<span style="color:#8be9fd;font-style:italic">contentVersion</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Shared Variable for resource configuration&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">subnetConfigurations</span> = [
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;web-subnet&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">addressPrefix</span>: <span style="color:#f1fa8c">&#39;10.0.1.0/24&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">serviceEndpoints</span>: [<span style="color:#f1fa8c">&#39;Microsoft.Storage&#39;</span>, <span style="color:#f1fa8c">&#39;Microsoft.KeyVault&#39;</span>]
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;api-subnet&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">addressPrefix</span>: <span style="color:#f1fa8c">&#39;10.0.2.0/24&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">serviceEndpoints</span>: [<span style="color:#f1fa8c">&#39;Microsoft.Sql&#39;</span>, <span style="color:#f1fa8c">&#39;Microsoft.KeyVault&#39;</span>]
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Shared Variable for Key Vault Secrets User Role Definition ID&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefId</span> = <span style="color:#f1fa8c">&#39;4633458b-17de-408a-b874-0445c86b69e6&#39;</span>
</span></span></code></pre></div><p>These can then be used in your deployment templates as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#ff79c6">/***************************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Main</span> <span style="color:#8be9fd;font-style:italic">Deploy</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Author</span>: <span style="color:#8be9fd;font-style:italic">Andrew</span> <span style="color:#8be9fd;font-style:italic">Wilson</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">****************************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Shared Imports **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ********************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">import</span> { <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefId</span> } <span style="color:#8be9fd;font-style:italic">from</span> <span style="color:#f1fa8c">&#39;./Common/variables.bicep&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">roleassignment</span> <span style="color:#f1fa8c">&#39;Microsoft.Authorization/roleAssignments@2022-04-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#50fa7b">guid</span>(<span style="color:#8be9fd;font-style:italic">logicAppName</span>, <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefId</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">roleDefinitionId</span>: <span style="color:#50fa7b">resourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Authorization/roleDefinitions&#39;</span>, <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefId</span>)
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="best-practices-for-using-shared-variables">Best Practices for Using Shared Variables</h2>
<ol>
<li><strong>Keep it Simple</strong>: Only share variables that are common and stable.</li>
<li><strong>Tight Control</strong>: Treat shared variables as critical infrastructure code.</li>
<li><strong>Documentation</strong>: Clearly document and describe what each variable represents or is used for.</li>
<li><strong>Selective Imports</strong>: Don&rsquo;t import everything with <code>*</code>; be selective about what needs to be imported into deployment templates.</li>
</ol>
<h2 id="when-not-to-use-shared-variables">When NOT to Use Shared Variables</h2>
<ul>
<li>For values that change frequently or are deployment-specific</li>
<li>For simple, one-off configurations</li>
<li>For sensitive values (use parameters or Key Vault references instead)</li>
</ul>
<h2 id="summary">Summary</h2>
<p>Shared variables help eliminate duplication and improve consistency across your Bicep templates. This simple approach gives you a single point of truth for common configurations, making updates easier and reducing the risk of inconsistencies across environments. Hope this helps, and happy Bicep-ing!</p>
]]></content:encoded></item><item><title>Bicep Tips and Tricks | #3 | Naming Convention and Functions</title><link>https://andrewilson.co.uk/post/2025/07/bicep-tips-and-tricks-naming-convention-functions/</link><pubDate>Wed, 30 Jul 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/07/bicep-tips-and-tricks-naming-convention-functions/</guid><description>Overview One of my bugbears is seeing either a complete lack of naming conventions or manual naming mechanisms that introduce human error through mistakes and misunderstandings. Naming conventions are incredibly important, but equally critical is how they&amp;rsquo;re implemented and maintained. Let&amp;rsquo;s explore a better approach.
What is a Naming Convention and Why Do We Need One?
A naming convention is a set of rules for naming Resource Groups and Resources (among other Azure components).</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>One of my bugbears is seeing either a complete lack of naming conventions or manual naming mechanisms that introduce human error through mistakes and misunderstandings. Naming conventions are incredibly important, but equally critical is how they&rsquo;re implemented and maintained. Let&rsquo;s explore a better approach.</p>
<p><strong>What is a Naming Convention and Why Do We Need One?</strong></p>
<p>A naming convention is a set of rules for naming Resource Groups and Resources (among other Azure components). These rules define the structure and composition of names, ensuring clarity, consistency, and compliance with Azure naming requirements such as length limits, valid characters, and scope uniqueness.</p>
<p>Common components that make up resource names include:</p>
<ul>
<li>Location</li>
<li>Environment</li>
<li>Project Prefix</li>
<li>Resource Abbreviation</li>
</ul>
<p>Naming conventions aren&rsquo;t one-size-fits-all and require tailoring to your specific needs. Any changes should be considered across all systems to maintain a single source of truth. Well-defined naming conventions significantly improve the readability and maintainability of your Azure estate, aligning with best practices outlined in the <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming">Cloud Adoption Framework</a>.</p>
<p><strong>Example convention:</strong> <code>{Resource Type}-{Project}-{Environment}-{Location}</code><br>
<strong>Result:</strong> LogicAppName: <code>logic-btt-dev-ukwest</code></p>
<p><strong>Anti-patterns to Avoid</strong></p>
<blockquote>
<p><strong>Hard-coded resource names:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;MyLogicApp&#39;</span>
</span></span></code></pre></div><p><em>Issues:</em> Poor maintainability, readability, consistency, and convention adherence</p>
</blockquote>
<blockquote>
<p><strong>Hard-coded naming variables:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">lgName</span> = <span style="color:#f1fa8c">&#39;MyLogicApp&#39;</span>
</span></span></code></pre></div><p><em>Issues:</em> Poor maintainability across templates, inconsistency, and no convention</p>
</blockquote>
<blockquote>
<p><strong>Hard-coded naming convention:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">lgName</span> = <span style="color:#f1fa8c">&#39;logic-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">project</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">environment</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">location</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span>
</span></span></code></pre></div><p><em>Issues:</em> Poor maintainability across templates, template inconsistency, and scattered convention logic</p>
</blockquote>
<h2 id="recommended-approach">Recommended Approach</h2>
<p>My recommended approach leverages <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/user-defined-functions">User-defined functions</a> to provide consistent and maintainable naming conventions. Combined with <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-import">Bicep Imports</a>, this ensures consistency across all templates.</p>
<p>For enhanced maintainability, I also recommend implementing <a href="/post/2025/07/bicep-tips-and-tricks-core-parameters-and-constructors/">centralized core parameters with types and constructors</a>. This approach significantly reduces the number of function parameters needed for each naming operation.</p>
<p><strong>Implementation Structure</strong></p>
<p>Create a <code>Common</code> folder within your IaC directory:</p>
<pre tabindex="0"><code>IaC
  ↳ Common
    ↳ types.bicep
    ↳ nameConventionFunctions.bicep
  ↳ main.bicep
</code></pre><ul>
<li><strong>types.bicep</strong> - Contains core parameter user-defined types and constructor functions</li>
<li><strong>nameConventionFunctions.bicep</strong> - Contains naming convention functions</li>
</ul>
<p><strong>Core Types Setup</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#ff79c6">/**********************************</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Shared</span> <span style="color:#8be9fd;font-style:italic">Types</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">Author</span>: <span style="color:#8be9fd;font-style:italic">Andrew</span> <span style="color:#8be9fd;font-style:italic">Wilson</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">***********************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** User Defined Types and Constructors **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *****************************************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// TYPE: Core Parameters</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *********************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Core Parameters Definition for Bicep Templates&#39;</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">sealed</span>()
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">coreParams</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Location to deploy resources to&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Location Short Name for resource naming&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">locationShortName</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Environment to deploy to&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">environment</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Project Prefix for resource naming&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">projectPrefix</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Core Parameters Constructor&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">newCoreParams</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">locationShortName</span> <span style="color:#8be9fd;font-style:italic">string</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">projectPrefix</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>) <span style="color:#8be9fd;font-style:italic">coreParams</span> =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">locationShortName</span>: <span style="color:#8be9fd;font-style:italic">locationShortName</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">environment</span>: <span style="color:#8be9fd;font-style:italic">environment</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">projectPrefix</span>: <span style="color:#8be9fd;font-style:italic">projectPrefix</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>Naming Convention Functions</strong></p>
<p>I&rsquo;ve structured my naming conventions using these key parameters:</p>
<ul>
<li><strong>Resource Abbreviation</strong> - Identifies the resource type (<a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations">Azure abbreviations reference</a>)</li>
<li><strong>Project Prefix</strong> - Identifies the project</li>
<li><strong>Environment</strong> - Identifies the deployment environment</li>
<li><strong>Location</strong> - Identifies the deployment location (optional for global resources)</li>
<li><strong>Context</strong> - Describes the resource&rsquo;s purpose (optional)</li>
</ul>
<p>The following functions cover some common naming scenarios:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#ff79c6">/*******************************************</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Name</span> <span style="color:#8be9fd;font-style:italic">Constructor</span> <span style="color:#8be9fd;font-style:italic">Functions</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">Author</span>: <span style="color:#8be9fd;font-style:italic">Andrew</span> <span style="color:#8be9fd;font-style:italic">Wilson</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">*******************************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Shared Imports **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ********************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">import</span> { <span style="color:#8be9fd;font-style:italic">coreParams</span> } <span style="color:#8be9fd;font-style:italic">from</span> <span style="color:#f1fa8c">&#39;./types.bicep&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Resource Name Constructors</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">//***************************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Basic</span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Generates a resource name based on the provided parameters (used for common usage resources)&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">basicResource</span>(<span style="color:#8be9fd;font-style:italic">resourceAbbreviation</span> <span style="color:#8be9fd;font-style:italic">string</span>, <span style="color:#8be9fd;font-style:italic">coreParameters</span> <span style="color:#8be9fd;font-style:italic">coreParams</span>) <span style="color:#8be9fd;font-style:italic">string</span> =&gt;
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">resourceAbbreviation</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">projectPrefix</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">environment</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">location</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Generates a resource name based on the provided parameters but ignoring the location (used for common usage resources that are not location specific)&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">unlocalisedBasicResource</span>(<span style="color:#8be9fd;font-style:italic">resourceAbbreviation</span> <span style="color:#8be9fd;font-style:italic">string</span>, <span style="color:#8be9fd;font-style:italic">coreParameters</span> <span style="color:#8be9fd;font-style:italic">coreParams</span>) <span style="color:#8be9fd;font-style:italic">string</span> =&gt;
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">resourceAbbreviation</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">projectPrefix</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">environment</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Context Specific</span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Generates a Context Specific Resource Name based on the provided parameters&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">csResource</span>(<span style="color:#8be9fd;font-style:italic">resourceAbbreviation</span> <span style="color:#8be9fd;font-style:italic">string</span>, <span style="color:#8be9fd;font-style:italic">coreParameters</span> <span style="color:#8be9fd;font-style:italic">coreParams</span>, <span style="color:#8be9fd;font-style:italic">contextName</span> <span style="color:#8be9fd;font-style:italic">string</span>) <span style="color:#8be9fd;font-style:italic">string</span> =&gt;
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">resourceAbbreviation</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">projectPrefix</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">contextName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">environment</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">location</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Resource Group</span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Generates a Resource Group name based on the provided parameters&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">resourceGroup</span>(<span style="color:#8be9fd;font-style:italic">contextName</span> <span style="color:#8be9fd;font-style:italic">string</span>, <span style="color:#8be9fd;font-style:italic">coreParameters</span> <span style="color:#8be9fd;font-style:italic">coreParams</span>) <span style="color:#8be9fd;font-style:italic">string</span> =&gt;
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;rg-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">projectPrefix</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">contextName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">environment</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">location</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Generates a Resource Group name based on the provided parameters but without an env specified&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">resourceGroupNonEnvSpecific</span>(<span style="color:#8be9fd;font-style:italic">contextName</span> <span style="color:#8be9fd;font-style:italic">string</span>, <span style="color:#8be9fd;font-style:italic">coreParameters</span> <span style="color:#8be9fd;font-style:italic">coreParams</span>) <span style="color:#8be9fd;font-style:italic">string</span> =&gt;
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;rg-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">projectPrefix</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">contextName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">location</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-shared&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Storage Account</span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Generates a Storage Account Resource Name based on the provided parameters&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">storageAccountResource</span>(<span style="color:#8be9fd;font-style:italic">coreParameters</span> <span style="color:#8be9fd;font-style:italic">coreParams</span>, <span style="color:#8be9fd;font-style:italic">contextName</span> <span style="color:#8be9fd;font-style:italic">string</span>?) <span style="color:#8be9fd;font-style:italic">string</span> =&gt;
</span></span><span style="display:flex;"><span>  <span style="color:#50fa7b">empty</span>(<span style="color:#8be9fd;font-style:italic">contextName</span>)
</span></span><span style="display:flex;"><span>    ? <span style="color:#f1fa8c">&#39;st</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">projectPrefix</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">environment</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">locationShortName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span>
</span></span><span style="display:flex;"><span>    : <span style="color:#f1fa8c">&#39;st</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">projectPrefix</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">contextName</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">environment</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">locationShortName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span>
</span></span></code></pre></div><p><strong>Usage Example</strong></p>
<p>Here&rsquo;s how to use these functions in your main deployment template:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#ff79c6">/***************************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Main</span> <span style="color:#8be9fd;font-style:italic">Deploy</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Author</span>: <span style="color:#8be9fd;font-style:italic">Andrew</span> <span style="color:#8be9fd;font-style:italic">Wilson</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">****************************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Shared Imports **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ********************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">import</span> { <span style="color:#8be9fd;font-style:italic">newCoreParams</span> } <span style="color:#8be9fd;font-style:italic">from</span> <span style="color:#f1fa8c">&#39;./Common/types.bicep&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">import</span> <span style="color:#ff79c6">*</span> <span style="color:#8be9fd;font-style:italic">as</span> <span style="color:#8be9fd;font-style:italic">newName</span> <span style="color:#8be9fd;font-style:italic">from</span> <span style="color:#f1fa8c">&#39;./Common/nameConventionFunctions.bicep&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Location to deploy resources to&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Location Short Name for resource naming&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">locationShortName</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Environment to deploy to&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;dev&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Project Prefix for resource naming&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">projectPrefix</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;myproject&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">coreParameters</span> = <span style="color:#50fa7b">newCoreParams</span>(<span style="color:#8be9fd;font-style:italic">location</span>, <span style="color:#8be9fd;font-style:italic">locationShortName</span>, <span style="color:#8be9fd;font-style:italic">environment</span>, <span style="color:#8be9fd;font-style:italic">projectPrefix</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Standard Logic App Resource Names</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">logicAppName</span> = <span style="color:#8be9fd;font-style:italic">newName</span>.<span style="color:#50fa7b">basicResource</span>(<span style="color:#f1fa8c">&#39;logic&#39;</span>, <span style="color:#8be9fd;font-style:italic">coreParameters</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">appServicePlanName</span> = <span style="color:#8be9fd;font-style:italic">newName</span>.<span style="color:#50fa7b">csResource</span>(<span style="color:#f1fa8c">&#39;asp&#39;</span>, <span style="color:#8be9fd;font-style:italic">coreParameters</span>, <span style="color:#f1fa8c">&#39;lg&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">storageAccountName</span> = <span style="color:#8be9fd;font-style:italic">newName</span>.<span style="color:#50fa7b">storageAccountResource</span>(<span style="color:#8be9fd;font-style:italic">coreParameters</span>, <span style="color:#f1fa8c">&#39;lg&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span></code></pre></div><p><strong>Generated Names:</strong></p>
<ul>
<li><strong>Logic App:</strong> <code>logic-myproject-dev-ukwest</code></li>
<li><strong>App Service Plan:</strong> <code>asp-myproject-lg-dev-ukwest</code></li>
<li><strong>Storage Account:</strong> <code>stmyprojectlgdevukw</code></li>
</ul>
<h2 id="summary">Summary</h2>
<p>Implementing a robust naming convention is crucial for Azure resource management, but the mechanism matters just as much as the convention itself. By using Bicep user-defined functions combined with centralized core parameters, you can:</p>
<ul>
<li><strong>Eliminate human error</strong> from manual naming processes</li>
<li><strong>Ensure consistency</strong> across all templates and environments</li>
<li><strong>Improve maintainability</strong> with centralized naming logic</li>
<li><strong>Enhance readability</strong> of your Infrastructure as Code</li>
<li><strong>Align with best practices</strong> from the Cloud Adoption Framework</li>
</ul>
<p>This approach transforms naming from a scattered, error-prone process into a centralized, reliable system that scales with your infrastructure needs. The initial setup investment pays dividends in reduced maintenance overhead and improved deployment reliability. Happy Bicep-ing!</p>
]]></content:encoded></item><item><title>Bicep Tips and Tricks | #2 | Centralize Core Parameters with Types, Constructors, and Imports</title><link>https://andrewilson.co.uk/post/2025/07/bicep-tips-and-tricks-core-parameters-and-constructors/</link><pubDate>Wed, 23 Jul 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/07/bicep-tips-and-tricks-core-parameters-and-constructors/</guid><description>Overview When building IaC templates we strive to enable them to be environment agnostic, configurable even. One of the mechanisms that we do this is through lots of &amp;ldquo;Core Parameters&amp;rdquo; that disseminate the fundamental details of our deployment and resources. The number of core parameters is often variable but the verdict is always true, three or more usually does the trick. These Core Parameters are passed into the main deployment template and any other sub deployment template (Module) too.</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>When building IaC templates we strive to enable them to be environment agnostic, configurable even. One of the mechanisms that we do this is through lots of &ldquo;<em>Core Parameters</em>&rdquo; that disseminate the fundamental details of our deployment and resources. The number of core parameters is often variable but the verdict is always true, three or more usually does the trick. These Core Parameters are passed into the main deployment template and any other sub deployment template (Module) too. These may include but are not limited to:</p>
<ul>
<li>Environment</li>
<li>Location/Region</li>
<li>Project/Application Prefix</li>
</ul>
<p>But why am I going on about this, why is it a problem. Well it&rsquo;s not a problem as much as it&rsquo;s a readability and maintainability issue. Think about it, every template now requires three or more core parameters defined, with every module call having to define them as parameters to be passed in. Following this, if there is a change, the change will need to occur everywhere, and let&rsquo;s hope that the descriptions/metadata has been kept up to date too!</p>
<p>My recommendation to resolve this issue involves three Bicep concepts:</p>
<ol>
<li><a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/user-defined-data-types">User-defined data types</a></li>
<li><a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/user-defined-functions">User-defined functions</a></li>
<li><a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-import">Imports</a></li>
</ol>
<h2 id="recommendation-broken-down">Recommendation broken down</h2>
<p><strong>1. Define our source of truth Core Parameters | User-defined data type</strong></p>
<p>User-defined data types provide us a way to define our own data object containing all the core parameters that we use throughout such as the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#6272a4">// Bicep</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Core Parameters Definition for Bicep Templates&#39;</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">sealed</span>()
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">coreParams</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Location to deploy resources to&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Environment to deploy to&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">environment</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Project Prefix for resource naming&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">projectPrefix</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>The decorator <code>@sealed()</code> means that you are only permitting the use of properties specifically included in the type definition. This helps with making sure there is only one single source of truth for the coreParams definition.</p>
</blockquote>
<p>The type definition means that we can pass through a single parameter to our deployment templates, and manage change in a single place.</p>
<p><strong>2. Create a Constructor to setup our Core Parameters | User-defined function</strong></p>
<p>To aid in the initialization of the coreParams type, I typically create a user-defined function (<em>Constructor</em>) which sets up the initial state by assigning values to the respective properties. This ensures the Core Params object starts in a valid, predictable state. The function would look like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#6272a4">// Bicep</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Core Parameters Constructor&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">newCoreParams</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">projectPrefix</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>) <span style="color:#8be9fd;font-style:italic">coreParams</span> =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">environment</span>: <span style="color:#8be9fd;font-style:italic">environment</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">projectPrefix</span>: <span style="color:#8be9fd;font-style:italic">projectPrefix</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>3. Promote Shared Use of the Type and Constructor | Imports</strong></p>
<p>The user defined type and function alone would not make this an effective recommendation due to the duplication of both the type and function definition on all templates. However, due to the recent introduction of exports and imports, we can continue on the path of a single and reusable single source of truth.</p>
<p>To make this work, what I would suggest is to create a new folder in your IaC location called <code>Common</code>, in this folder create a new <code>bicep</code> file called <code>types.bicep</code>. This is the shared bicep file that we are going to use for our new type and constructor.</p>
<p>An example of this setup is as follows:</p>
<ul>
<li><code>IaC → Common → types.bicep</code></li>
</ul>
<blockquote>
<p>Shared Types Template</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#6272a4">// Bicep</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">/**********************************</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Shared</span> <span style="color:#8be9fd;font-style:italic">Types</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">Author</span>: <span style="color:#8be9fd;font-style:italic">Andrew</span> <span style="color:#8be9fd;font-style:italic">Wilson</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">***********************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** User Defined Types and Constructors **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *****************************************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// TYPE: Core Parameters</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *********************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Core Parameters Definition for Bicep Templates&#39;</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">sealed</span>()
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">coreParams</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Location to deploy resources to&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Environment to deploy to&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">environment</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Project Prefix for resource naming&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">projectPrefix</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">export</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Core Parameters Constructor&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">newCoreParams</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">projectPrefix</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>) <span style="color:#8be9fd;font-style:italic">coreParams</span> =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">environment</span>: <span style="color:#8be9fd;font-style:italic">environment</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">projectPrefix</span>: <span style="color:#8be9fd;font-style:italic">projectPrefix</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The <code>@export()</code> decorator is used to allow the type and function to be imported into other Bicep files.</p>
<p>Now we can use the import syntax in our main and sub deployment (module) templates, allowing use of the single defined type and function. In the main template we import the constructor and assign the initialized object to our coreParameters variable. For our sub deployment (module) templates we import the coreParams type so we can use it as a parameter definition. Both combined allow simplified module reference definitions and assignments. This can be seen in the following example templates:</p>
<blockquote>
<p>Main Deployment Template</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#6272a4">// Bicep</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">/***************************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Main</span> <span style="color:#8be9fd;font-style:italic">Deploy</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Author</span>: <span style="color:#8be9fd;font-style:italic">Andrew</span> <span style="color:#8be9fd;font-style:italic">Wilson</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">****************************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Shared Imports **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ********************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">import</span> { <span style="color:#8be9fd;font-style:italic">newCoreParams</span> } <span style="color:#8be9fd;font-style:italic">from</span> <span style="color:#f1fa8c">&#39;./Common/types.bicep&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Location to deploy resources to&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#50fa7b">resourceGroup</span>().<span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Environment to deploy to&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;dev&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Project Prefix for resource naming&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">projectPrefix</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;myProject&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">coreParameters</span> = <span style="color:#50fa7b">newCoreParams</span>(<span style="color:#8be9fd;font-style:italic">location</span>, <span style="color:#8be9fd;font-style:italic">environment</span>, <span style="color:#8be9fd;font-style:italic">projectPrefix</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Demonstration of the core parameters being passed to a submodule&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">module</span> <span style="color:#8be9fd;font-style:italic">subDeploy</span> <span style="color:#f1fa8c">&#39;./subDeploy.bicep&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;subDeploy&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">params</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">coreParameters</span>: <span style="color:#8be9fd;font-style:italic">coreParameters</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>Sub Deployment (Module) Template</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#6272a4">// Bicep</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">/***************************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Resources</span> <span style="color:#8be9fd;font-style:italic">Deploy</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Author</span>: <span style="color:#8be9fd;font-style:italic">Andrew</span> <span style="color:#8be9fd;font-style:italic">Wilson</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">****************************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Shared Imports **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ********************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">import</span> { <span style="color:#8be9fd;font-style:italic">coreParams</span> } <span style="color:#8be9fd;font-style:italic">from</span> <span style="color:#f1fa8c">&#39;./Common/types.bicep&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Core Parameters for the deployment&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">coreParameters</span> <span style="color:#8be9fd;font-style:italic">coreParams</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The name of the resource to deploy&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">resourceName</span> = <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">projectPrefix</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">coreParameters</span>.<span style="color:#8be9fd;font-style:italic">environment</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-resource&#39;</span>
</span></span></code></pre></div><h2 id="summary">Summary</h2>
<p>Managing core parameters in Bicep templates can quickly become unwieldy as your infrastructure grows. By leveraging user-defined types, constructors, and imports, you can centralize your parameter definitions, improve readability, and simplify maintenance. This approach ensures consistency across your deployments and makes future changes easier to manage. Happy Bicep-ing!</p>
]]></content:encoded></item><item><title>Bicep Tips and Tricks | #1 | Template Versioning and Applying to Azure Resource Tags</title><link>https://andrewilson.co.uk/post/2025/07/bicep-template-versioning-resource-tags-tips-tricks-azure-devops/</link><pubDate>Wed, 16 Jul 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/07/bicep-template-versioning-resource-tags-tips-tricks-azure-devops/</guid><description>The first of many For those who know me well, starting a bicep tips and tricks series would not be a surprise to them. The moment the Bicep language was introduced, I knew I would be completely obsessed. I love writing bicep templates and even more the clever refinement to make them reusable, configurable, manageable, readable&amp;hellip; I really enjoy sharing my experiences with Bicep, so here it begins on the wider front.</description><content:encoded><![CDATA[<h2 id="the-first-of-many">The first of many</h2>
<p>For those who know me well, starting a bicep tips and tricks series would not be a surprise to them. The moment the Bicep language was introduced, I knew I would be completely obsessed. I love writing bicep templates and even more the clever refinement to make them reusable, configurable, manageable, readable&hellip; I really enjoy sharing my experiences with Bicep, so here it begins on the wider front. From one Bicep nerd to another, I hope you find these tips useful, happy templating!</p>
<h2 id="why-is-versioning-important">Why is versioning important</h2>
<p>Versioning is a fundamental practice in software development that helps us manage change effectively. It provides a structured way to track and communicate updates, ensuring clarity and consistency throughout the development lifecycle.</p>
<ul>
<li>
<p>Why apply a version to your templates?</p>
<p>Applying versioning to templates ensures you can track changes, maintain consistency, and know exactly which version of your infrastructure code was used for any deployment. It supports best practices like binary promotion, simplifies troubleshooting, and helps teams coordinate updates and rollbacks with confidence.</p>
</li>
<li>
<p>Why apply the version as a tag to Azure Resources?</p>
<p>Tagging Azure resources with the template version gives you direct visibility into what was deployed, right from the Azure portal or API. This makes audits, governance, and troubleshooting much easier, as you can instantly see which template version created or updated a resource. It also helps correlate infrastructure changes with application releases and supports compliance requirements for traceability.</p>
</li>
</ul>
<p>ARM templates have a Json property right at the top of the file underneath <code>$schema</code> called <code>contentVersion</code>. By default and if never changed, this will always be <code>1.0.0.0</code>. As part of our versioning practice we can make sure that the templates that we deploy make use of the same version that is applied to other built artifacts. This allows us to track deployments and their respective templates through versions.</p>
<h2 id="how-to-version-templates-using-azure-devops">How to version templates using Azure DevOps</h2>
<p>As part of a YAML CI/CD pipeline I typically have a build stage, with a inner Test and Version ARM Templates Job. The focus of the job is to do two things:</p>
<ol>
<li>Using the az cli - Build the Bicep templates
<ul>
<li>I also include the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/linter">Bicep linting file</a>, this means that as the templates are being transpiled into ARM I am also getting any build errors and warnings. I output these as logissues to the Azure DevOps Pipeline for visibility.</li>
</ul>
</li>
<li>Version the built ARM JSON artifacts to support Binary promotion and align with versioning practices.</li>
</ol>
<p>I use the following tasks to complete the tasks:</p>
<ol>
<li><strong>AzureCLI@2</strong> - used to build the Bicep templates (transpile into ARM) and log any warnings or errors.</li>
<li><strong><a href="https://marketplace.visualstudio.com/items?itemName=richardfennellBM.BM-VSTS-Versioning-Task">VersionJSONFile@3</a></strong> - used to version stamp the ARM templates.</li>
<li><strong>CopyFiles@2</strong> - Used to copy the versioned ARM artifacts to the staging directory.</li>
<li><strong>PublishPipelineArtifact@1</strong> - used to publish the ARM artifacts to the pipeline so they can be downloaded in future deployment stages.</li>
</ol>
<p>Here is a sample YAML template:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-YAML" data-lang="YAML"><span style="display:flex;"><span><span style="color:#6272a4"># Yaml</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#ff79c6">task</span>: AzureCLI@2
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Build Bicep Files&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">azureSubscription</span>: <span style="color:#f1fa8c">&#39;xxyyzz&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">scriptType</span>: <span style="color:#f1fa8c">&#39;ps&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">scriptLocation</span>: <span style="color:#f1fa8c">&#39;inlineScript&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">useGlobalConfig</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">powershellerrorActionPreference</span>: <span style="color:#f1fa8c">&#39;continue&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">inlineScript</span>: |<span style="color:#f1fa8c">
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">      # create folder if it doesn&#39;t exist
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">      if (!(Test-Path -Path $(Build.SourcesDirectory)\{YourPath}\IAC\ARMOutput)) {
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">        New-Item -ItemType Directory -Path $(Build.SourcesDirectory)\{YourPath}\IAC\ARMOutput
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">      }
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">      write-host &#34;Build the Bicep file&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">      $output = az bicep build --file $(Build.SourcesDirectory)\{YourPath}\IAC\template.azuredeploy.bicep --outdir $(Build.SourcesDirectory)\{YourPath}\IAC\ARMOutput 2&gt;&amp;1</span>      
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>      write-host &#34;Process the output&#34;
</span></span><span style="display:flex;"><span>      $output | foreach-object {
</span></span><span style="display:flex;"><span>         if ($_ -match &#39;Error&#39;) {
</span></span><span style="display:flex;"><span>            Write-Host &#34;##vso[task.logissue type=error]$_&#34;
</span></span><span style="display:flex;"><span>         } 
</span></span><span style="display:flex;"><span>         if ($_ -match &#39;Warning&#39;) {
</span></span><span style="display:flex;"><span>             Write-Host &#34;##vso[task.logissue type=warning]$_&#34;
</span></span><span style="display:flex;"><span>         }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#ff79c6">task</span>: VersionJSONFile@3
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Version stamp ARM templates&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">Path</span>: <span style="color:#f1fa8c">&#39;$(Build.SourcesDirectory)\{YourPath}\IAC\ARMOutput&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">recursion</span>: <span style="color:#ff79c6">True</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">VersionNumber</span>: $(Build.BuildNumber) <span style="color:#6272a4"># an example versioning option but can also be options like gitversion</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">useBuildNumberDirectly</span>: <span style="color:#ff79c6">False</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">VersionRegex</span>: \d+\.\d+\.\d+\.\d+
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">versionForJSONFileFormat</span>: <span style="color:#f1fa8c">&#39;{1}.{2}.{3}.{4}&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">FilenamePattern</span>: <span style="color:#f1fa8c">&#39;\w+.json&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">Field</span>: <span style="color:#f1fa8c">&#39;contentVersion&#39;</span> <span style="color:#6272a4"># Versioning is applied to the templates contentVersion field</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">OutputVersion</span>: OutputedVersion
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#ff79c6">task</span>: CopyFiles@2
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Copy ARMOutput to Artifact Staging Directory&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">SourceFolder</span>: <span style="color:#f1fa8c">&#39;$(Build.SourcesDirectory)\{YourPath}\IAC\ARMOutput&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">Contents</span>: <span style="color:#f1fa8c">&#39;**/*.json&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">TargetFolder</span>: <span style="color:#f1fa8c">&#39;$(Build.ArtifactStagingDirectory)/templates&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">flattenFolders</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#ff79c6">task</span>: PublishPipelineArtifact@1
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Publish ARMOutput as template build artifact&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">targetPath</span>: <span style="color:#f1fa8c">&#39;$(Build.ArtifactStagingDirectory)/templates&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">publishLocation</span>: <span style="color:#f1fa8c">&#39;pipeline&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">artifactName</span>: <span style="color:#f1fa8c">&#39;ARMtemplates&#39;</span>
</span></span></code></pre></div><p>This setup ensures your ARM templates are built, versioned, and published as pipeline artifacts in a repeatable, traceable way. By stamping each template with a version number and making it available as an artifact, you enable binary promotion through environments and make it easy to track exactly which template version was used for each deployment. This improves auditability, supports rollback scenarios, and aligns with best practices for infrastructure as code in CI/CD pipelines.</p>
<h2 id="how-to-apply-the-template-version-as-a-tag-to-azure-resources">How to apply the template version as a tag to Azure Resources</h2>
<p>Versioning our ARM templates is one part of the transparency and traceability. But it would also be nice to be able to see which template version deployed the Azure Resources from the Azure Resources themselves. We can do this by applying a tag onto our resources and tying it back to our ARM <code>contentVersion</code> property field. We can do this by using the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-deployment#deployment"><code>deployment()</code></a> function as such <code>deployment().properties.template.contentVersion</code>.</p>
<p>On a resource it would look like the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#6272a4">// Bicep</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">AppService</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/sites@2024-11-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">appServiceName</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">tags</span>: {
</span></span><span style="display:flex;"><span>	...
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">version</span>: <span style="color:#50fa7b">deployment</span>().<span style="color:#8be9fd;font-style:italic">properties</span>.<span style="color:#8be9fd;font-style:italic">template</span>.<span style="color:#8be9fd;font-style:italic">contentVersion</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Applying template versioning and tagging resources with the deployed template’s version brings transparency and traceability to your Azure infrastructure. You’ll always know which version of your code and templates are running in each environment, making troubleshooting and governance much easier. These small practices help teams stay organised, reduce deployment risks, and build confidence in their automation. Happy Bicep-ing!</p>
]]></content:encoded></item><item><title>Bicep | Existing Resource Dependencies</title><link>https://andrewilson.co.uk/post/2025/03/bicep-existing-resource-dependencies/</link><pubDate>Wed, 26 Mar 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/03/bicep-existing-resource-dependencies/</guid><description>Background The Bicep existing keyword is a powerful capability that allows us to reference a resource that wasn&amp;rsquo;t deployed as part of the current Bicep file.
One of the typical use cases that I often see is where a resource is deployed as part of a module called by the parent template, the resource that was deployed as part of the module is then required later in the parent template and therefore an existing resource definition is used.</description><content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>The Bicep <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/existing-resource">existing</a> keyword is a powerful capability that allows us to reference a resource that wasn&rsquo;t deployed as part of the current Bicep file.</p>
<p>One of the typical use cases that I often see is where a resource is deployed as part of a module called by the parent template, the resource that was deployed as part of the module is then required later in the parent template and therefore an existing resource definition is used. As part of the template configuration there would be an explicit dependency between the existing resource definition and the module reference.</p>
<p>An example diagram of this is shown below:</p>
<p>
  <img src="/images/posts/2025/03/BicepExistingDependencyDiagramBackground.png" alt="Background">

</p>
<p>For Bicep v0.34.1 and lower, there are two scenarios that can be observed when deploying the templates:</p>
<ul>
<li>Scenario 1: Successful deployment. The existing resource has been found and the explicit dependency between Existing Resource and Module Ref 1 has been honoured.</li>
<li>Scenario 2: Deployment Failure. <code>The Resource XYZ under resource group 'XXYYZZ' was not found.</code>. The existing resource has not been found and the explicit dependency between Existing Resource and Module Ref 1 has not been honoured.</li>
</ul>
<p>For most that fall into scenario 2, without much explanation of what may be going on behind the scenes, their typical solution to this particular problem may be to force the dependency chaining by extracting further of the parent template into a further module.</p>
<p>An example of this can be seen below:</p>
<p>
  <img src="/images/posts/2025/03/BicepExistingDependencyDiagramMidSolution.png" alt="MidSolution">

</p>
<p>Yes this is a valid solution, but why is there a problem in the first place?</p>
<h2 id="explanation">Explanation</h2>
<p>It all comes down to whether your template is compiled with <strong>symbolic name code generation</strong>.</p>
<p><a href="https://github.com/Azure/bicep/blob/main/docs/experimental-features.md#symbolicnamecodegen">Symbolic Name Code Generation</a> allows the ARM template layer to use a new schema to represent resources as an object dictionary rather than an array of objects. This feature improves the semantic equivalence of the Bicep and ARM templates, resulting in more reliable code generation.</p>
<p>In a symbolic name template, an existing resource can depend on other resources or modules, and this will <strong>cause the backend to delay reading the resource until those dependencies are completed</strong>. This allows you to deploy a resource in a module, then refer to it with an existing declaration in the module parent. In <strong>non-symbolic name templates</strong>, existing resources are compiled to reference(resourceId()) expressions and are all <strong>handled in the first wave of deployment jobs</strong>.</p>
<p>In a non-symbolic name compiled template the existing reference looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#50fa7b">reference</span>(<span style="color:#50fa7b">resourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.xxx/yyy&#39;</span>, <span style="color:#50fa7b">parameters</span>(<span style="color:#f1fa8c">&#39;resourceName&#39;</span>))), <span style="color:#f1fa8c">&#39;2023-05-01&#39;</span>, <span style="color:#f1fa8c">&#39;full&#39;</span>)
</span></span></code></pre></div><p>In a symbolic name compiled template the existing reference and dependencies look like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#50fa7b">reference</span>(<span style="color:#f1fa8c">&#39;existingResource&#39;</span>, <span style="color:#f1fa8c">&#39;2023-05-01&#39;</span>, <span style="color:#f1fa8c">&#39;full&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&#34;<span style="color:#8be9fd;font-style:italic">dependsOn</span>&#34;: [
</span></span><span style="display:flex;"><span>  &#34;<span style="color:#8be9fd;font-style:italic">existingResource</span>&#34;,
</span></span><span style="display:flex;"><span>  &#34;<span style="color:#8be9fd;font-style:italic">ModuleRef1</span>&#34;
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>This explains why in some template deployment cases the deployment succeeds and in others it fails (the template is not setup for symbolic name code generation). But how do you enable symbolic name code generation?</p>
<h2 id="solution">Solution</h2>
<p>Symbolic name code generation is part of the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/syntax#languageversion-20">languageVersion 2.0</a> functionality and can be enabled <strong>automatically</strong> by using any of the following Bicep Features:</p>
<ul>
<li>user-defined types</li>
<li>user-defined functions</li>
<li>compile-time imports</li>
<li>experimental features</li>
</ul>
<p>For most of you up until this point like me, you will have unintentionally and thankfully enabled symbolic name code generation by using features as described above and therefore not suffered from the problem case as described.</p>
<p>Symbolic name code generation can also be <strong>explicitly</strong> enabled by adding the following to your <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-config">bicepconfig.json</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>	<span style="color:#f1fa8c">&#34;experimentalFeaturesEnabled&#34;</span>: {
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">&#34;symbolicNameCodegen&#34;</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>	 }
</span></span></code></pre></div><p>Or the <strong>preferred option</strong> use <strong>Bicep <a href="https://github.com/Azure/bicep/releases/tag/v0.34.44">v0.34.44</a></strong> or higher where languageVersion2.0 is used automatically when an existing resource has an <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/resource-dependencies#explicit-dependency">explicit DependsOn dependency</a> stipulated.</p>
<p>If you are unsure as to whether your templates are compiled using symbolic name code generation, have a look at the first few lines of your compiled Bicep template. The ARM template should specify languageVersion 2.0 if enabled and missing this line if not.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;languageVersion&#34;</span>: <span style="color:#f1fa8c">&#34;2.0&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span></code></pre></div><p>Hope this helps and have fun.</p>
]]></content:encoded></item><item><title>Key Vault Reference | Logic and Function Apps using User-Assigned Managed Identity</title><link>https://andrewilson.co.uk/post/2025/01/logic-and-function-app-keyvault-access-with-user-assigned-identity/</link><pubDate>Mon, 06 Jan 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/01/logic-and-function-app-keyvault-access-with-user-assigned-identity/</guid><description>Overview Prior to the Christmas break I was involved in writing some integrations that used a mixture of Logic Apps Standard and Function Apps. It was agreed as part of the architecture that user-assigned identities would be the best fit. As part of the implementation, I observed that the differences in configuration setup between system-assigned and user-assigned wasn&amp;rsquo;t widely understood. This article aims to show a brief run through of both.</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>Prior to the Christmas break I was involved in writing some integrations that used a mixture of Logic Apps Standard and Function Apps. It was agreed as part of the architecture that user-assigned identities would be the best fit. As part of the implementation, I observed that the differences in configuration setup between system-assigned and user-assigned wasn&rsquo;t widely understood. This article aims to show a brief run through of both.</p>
<h2 id="setup-and-difference-with-system-assigned">Setup and Difference with System-Assigned</h2>
<p><strong>System-Assigned</strong></p>
<p>The general process when using a <em>System-Assigned identity</em> is as follows:</p>
<ol>
<li>
<p>Create a Key Vault Instance.</p>
</li>
<li>
<p>Create secrets required by the application.</p>
</li>
<li>
<p>Create the app resource (Logic App / Function App)</p>
<ul>
<li>
<p>As part of the configuration, specify the identity as System Assigned.</p>
</li>
<li>
<p><a href="https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references?tabs=azure-cli#source-app-settings-from-key-vault">Reference the key vault secret(s)</a> in your App Settings.</p>
</li>
</ul>
</li>
<li>
<p>Authorise the applications identity read access to key vault or <a href="/post/2023/11/rbac-key-vault-specific-secret/">specifically the key vaults secret</a>.</p>
</li>
</ol>
<p>The main points with System-Assigned setup is:</p>
<ul>
<li>The identity is tied to the created app resource and its life-cycle</li>
<li>The resource cannot reference key vault secrets at the point of creation
<ul>
<li>Authorisation to read the key vault secrets has not occurred at this point</li>
</ul>
</li>
<li>The identity cannot be associated with other resources</li>
</ul>
<p><strong>User-Assigned</strong></p>
<p>The general process when using a <em>User-Assigned identity</em> is as follows:</p>
<ol>
<li>
<p>Create a Key Vault instance</p>
</li>
<li>
<p>Create secrets required by the application(s)</p>
</li>
<li>
<p>Create the user-assigned identity</p>
</li>
<li>
<p>Authorise the user-assigned identity read access to key vault or <a href="/post/2023/11/rbac-key-vault-specific-secret/">specifically the key vaults secret</a>.</p>
</li>
<li>
<p>Create the app resource (Logic App / Function App)</p>
<p>As part of the resource configuration</p>
<ul>
<li>
<p>Specify the identity as user-assigned and reference the created identity in step 3</p>
</li>
<li>
<p>Specify the identity to be used for key vault reference operations by setting the <code>keyVaultReferenceIdentity</code> property to the resource ID of the user-assigned identity</p>
</li>
<li>
<p><a href="https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references?tabs=azure-cli#source-app-settings-from-key-vault">Reference the key vault secret(s)</a> in your App Settings.</p>
</li>
</ul>
</li>
</ol>
<p>The main points with User-Assigned setup is:</p>
<ul>
<li>The identity is managed outside the context of a resource and its life-cycle</li>
<li>A resource that uses the identity can read secrets from keyvault at the point of creation
<ul>
<li>Given that the authorisation has occurred prior to the resource creation.</li>
</ul>
</li>
<li>The identity can be associated with on or more resources</li>
</ul>
<p>⚠️ <strong>Note</strong> One of the most common gotchas [user-assigned] is missing or forgetting to specify the identity to be used for key vault reference operations (<code>keyVaultReferenceIdentity</code> property - Step 5).</p>
<p>Hope this helps and have fun.</p>
]]></content:encoded></item><item><title>Easy Auth | Standard Logic App with Azure API Management</title><link>https://andrewilson.co.uk/post/2024/02/standard-logic-app-easy-auth-apim/</link><pubDate>Tue, 26 Nov 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/02/standard-logic-app-easy-auth-apim/</guid><description>Overview The recent work that I have been doing with Standard Logic Apps and linking them as backends to Azure API Management has relied on the use of the Logic App Workflow SAS key for security. This is a valid authentication approach, but there are risks that you need to be aware of as well as best practices that you need to be abiding by. Such as:
Some Potential Risks:</description><content:encoded><![CDATA[<p><a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend">
  <img src="https://img.shields.io/badge/Repo-Easy%20Auth%20With%20Standard%20Logic%20App%20And%20APIM-blue?logo=github&amp;style=for-the-badge" alt="GitHub Repository">

</a></p>
<h2 id="overview">Overview</h2>
<p>The recent work that I have been doing with Standard Logic Apps and linking them as backends to Azure API Management has relied on the use of the Logic App Workflow SAS key for security. This is a valid authentication approach, but there are risks that you need to be aware of as well as <a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview#best-practices-when-using-sas">best practices</a> that you need to be abiding by. Such as:</p>
<ul>
<li>
<p>Some Potential Risks:</p>
<ul>
<li>If a <strong>SAS is leaked</strong>, it can be used by anyone who obtains it to call your Logic App Workflow.</li>
<li>If a <strong>SAS expires</strong> and the API Management API has not been updated to make use of the updated SAS, then the integration functionality will be hindered.</li>
</ul>
</li>
<li>
<p>Some SAS Best Practices to mitigate risks:</p>
<ul>
<li><strong>Always use HTTPS</strong> - If a SAS is passed over HTTP and intercepted, an attacker can perform a man-in-the-middle attack and read the SAS.</li>
<li><strong>Have a revocation plan</strong> - Make sure that you are prepared to respond if a SAS is compromised.</li>
<li><strong>Have a rotation plan</strong> - Look to replace the SAS with new ones at regular intervals and include shorter time intervals for the expiration period.</li>
</ul>
</li>
</ul>
<p>There are other features that can be used to further restrict access to your Logic App, for instance adding <a href="https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-securing-a-logic-app?tabs=azure-portal#standard-workflows-1">IP Address Restrictions</a> to limit traffic to only your API Management Instance.</p>
<p>But what if you require further governance whereby the Logic App requires a valid Entra ID bearer token in order to be invoked. To implement this we are going to make use of Easy Auth.</p>
<h2 id="easy-auth">Easy Auth</h2>
<p><a href="https://learn.microsoft.com/en-us/azure/app-service/overview-authentication-authorization">Easy Auth</a> is a built-in authentication and authorisation capability provided by Azure App Services and Azure Functions. Easy Auth makes use of federated identity whereby a third-party identity provider manages the user identities and authentication flow for you. Fortunately for us, Standard Logic Apps have the same foundations as Azure Functions and therefore Easy Auth is also extended to Logic Apps Standard.</p>
<p>
  <img src="/images/posts/2024/02/EasyAuth.png" alt="Eay Auth">

</p>
<p>Easy Auth is a platform feature running on the same virtual machine as your application. Once enabled, any incoming HTTP requests will pass through this feature prior to being handled by your application. Easy Auth runs separately from your application code and can be configured using ARM settings or using a configuration file.</p>
<h2 id="using-easy-auth-and-linking-to-api-management">Using Easy Auth and Linking to API Management</h2>
<p>As mentioned above, I would like to use Easy Auth to protect my HTTP Triggered Azure Logic App (Standard) workflows, but more importantly, I would like Azure API Management to be the only identity that can be used to make requests to my workflows as shown in the diagram below:</p>
<p>
  <img src="/images/posts/2024/02/EasyAuthAPIM.png" alt="Eay Auth">

</p>
<blockquote>
<p><strong>Note:</strong></p>
<p>As we will be invoking the Logic App HTTP triggered workflows with Easy Auth, we no longer need to specify the following parameters in the request:</p>
<ul>
<li>sp: The permissions; generally &lsquo;read&rsquo; or &lsquo;write&rsquo;</li>
<li>sv: The version number of the query parameters</li>
<li>sig: Shared-Access-Signature</li>
</ul>
<p>Furthermore, I have opted for Infrastructure as Code (IaC) as my method of implementation, specifically Bicep.</p>
</blockquote>
<p>For setup, we will need to conduct the following four steps:</p>
<ol>
<li>
<p>Configure the Logic App to <a href="https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad?tabs=workforce-configuration">Use Microsoft Entra sign-in</a>.</p>
<p>Working through the Microsoft instructions in the link above, you will require as minimum the following when setting up the Logic App Application Registration:</p>
<ul>
<li>
<p>Select the supported account type. (<em>I&rsquo;m using Current tenant - single tenant</em>)</p>
</li>
<li>
<p>Setup a Redirect URI for your Logic App:</p>
<p>Select Web for platform and set the URI to <code>&lt;app-url&gt;/.auth/login/aad/callback</code>. For example, <a href="https://contoso.azurewebsites.net/.auth/login/aad/callback">https://contoso.azurewebsites.net/.auth/login/aad/callback</a>.</p>
</li>
<li>
<p>Make not of the Application (client) ID.</p>
</li>
<li>
<p>Create a Client Secret and store this securely (<em>I&rsquo;m using Azure  DevOps Secure Library Variables as part of a deployment</em>).</p>
<p><em>This is a secret value that the application uses to prove its identity when requesting a token. This value is saved in your app&rsquo;s configuration as a slot-sticky application setting named <code>MICROSOFT_PROVIDER_AUTHENTICATION_SECRET</code>. If the client secret isn&rsquo;t set, sign-in operations from the service use the OAuth 2.0 implicit grant flow, which isn&rsquo;t recommended.</em></p>
</li>
<li>
<p>Expose an API &gt; Add &gt; Save. This value uniquely identifies the application when it&rsquo;s used as a resource, allowing tokens to be requested that grant access. It&rsquo;s used as a prefix for scopes you create.</p>
<p>I am using a single-tenant app and therefore using the default value. Appears as such <code>api://&lt;application-client-id&gt;</code></p>
</li>
<li>
<p>Add the <code>user_impersonation</code> scope to your App Registration allowing <code>Admins and users</code> to consent.</p>
</li>
</ul>
</li>
<li>
<p>Make sure that API Management has been setup with Managed Identity. I am using System Assigned.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Deployment of the APIM instance&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">apimInstanceDeploy</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service@2022-08-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">identity</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">type</span>: <span style="color:#f1fa8c">&#39;SystemAssigned&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
<li>
<p>Store the App Registration Secret in KeyVault and Reference in your Logic App Settings to allow the Logic App to prove its identity.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">secure</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The client secret for the Easy Auth App Registration&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">applicationEasyAuthClientSecret</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Role Definition Id for the Key Vault Secrets User role&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefId</span> = <span style="color:#f1fa8c">&#39;4633458b-17de-408a-b874-0445c86b69e6&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Deploy the Application Easy Auth App Registration Secret to Keyvault&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">vaultLogicAppRegSecret</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults/secrets@2023-07-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationLogicAppName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-EasyAuth-Secret&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">applicationKeyVaultDeploy</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">contentType</span>: <span style="color:#f1fa8c">&#39;string&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">value</span>: <span style="color:#8be9fd;font-style:italic">applicationEasyAuthClientSecret</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Deploy the Application Standard Logic App&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">applicationLogicAppStandardDeploy</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/sites@2024-04-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">applicationLogicAppName</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">identity</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">type</span>: <span style="color:#f1fa8c">&#39;SystemAssigned&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">config</span> <span style="color:#f1fa8c">&#39;config@2024-04-01&#39;</span> = {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;appsettings&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>      ...
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">MICROSOFT_PROVIDER_AUTHENTICATION_SECRET</span>: <span style="color:#f1fa8c">&#39;@Microsoft.KeyVault(VaultName=</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationKeyVaultDeploy</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">;SecretName=</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">vaultLogicAppRegSecret</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">)&#39;</span>
</span></span><span style="display:flex;"><span>      ...
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Create the RBAC for the Logic App to Read the Secret from Key Vault&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">applicationLogicAppRBACWithKV</span> <span style="color:#f1fa8c">&#39;Microsoft.Authorization/roleAssignments@2022-04-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#50fa7b">guid</span>(<span style="color:#8be9fd;font-style:italic">applicationKeyVaultDeploy</span>.<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#8be9fd;font-style:italic">applicationLogicAppStandardDeploy</span>.<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefId</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">scope</span>: <span style="color:#8be9fd;font-style:italic">vaultLogicAppRegSecret</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">principalId</span>: <span style="color:#8be9fd;font-style:italic">applicationLogicAppStandardDeploy</span>.<span style="color:#8be9fd;font-style:italic">identity</span>.<span style="color:#8be9fd;font-style:italic">principalId</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">roleDefinitionId</span>: <span style="color:#50fa7b">subscriptionResourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Authorization/roleDefinitions&#39;</span>, <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefId</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">principalType</span>: <span style="color:#f1fa8c">&#39;ServicePrincipal&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
<li>
<p>Enable Easy Auth on the Standard Logic App using <a href="https://learn.microsoft.com/en-us/azure/templates/microsoft.web/sites/config-authsettingsv2?pivots=deployment-language-bicep#siteauthsettingsv2properties">ARM/Bicep Template AuthSettingsV2</a> or <a href="https://github.com/Azure/azure-rest-api-specs/blob/main/specification/web/resource-manager/Microsoft.Web/stable/2021-02-01/WebApps.json#L1197">ARM REST API</a>. I am using a Bicep Template.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Setup the Easy Auth config settings for the Standard Logic App&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">applicationAuthSettings</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/sites/config@2023-01-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;authsettingsV2&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">applicationLogicAppStandardDeploy</span> <span style="color:#6272a4">// Existing Standard Logic App for Easy Auth to be enabled</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">globalValidation</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">requireAuthentication</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">unauthenticatedClientAction</span>: <span style="color:#f1fa8c">&#39;AllowAnonymous&#39;</span> <span style="color:#6272a4">// Do not change: See note below.</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">httpSettings</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">requireHttps</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">routes</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">apiPrefix</span>: <span style="color:#f1fa8c">&#39;/.auth&#39;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">forwardProxy</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">convention</span>: <span style="color:#f1fa8c">&#39;NoProxy&#39;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">identityProviders</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">azureActiveDirectory</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">enabled</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">registration</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">openIdIssuer</span>: <span style="color:#50fa7b">uri</span>(<span style="color:#f1fa8c">&#39;https://sts.windows.net/&#39;</span>, <span style="color:#50fa7b">tenant</span>().<span style="color:#8be9fd;font-style:italic">tenantId</span>)
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">clientId</span>: <span style="color:#8be9fd;font-style:italic">logicAppEasyAuthClientId</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">clientSecretSettingName</span>: <span style="color:#f1fa8c">&#39;MICROSOFT_PROVIDER_AUTHENTICATION_SECRET&#39;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">validation</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">allowedAudiences</span>: <span style="color:#50fa7b">environment</span>().<span style="color:#8be9fd;font-style:italic">authentication</span>.<span style="color:#8be9fd;font-style:italic">audiences</span> <span style="color:#6272a4">// Azure Management Plane [management.core.windows.net and management.azure.com]</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">defaultAuthorizationPolicy</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd;font-style:italic">allowedPrincipals</span>: {
</span></span><span style="display:flex;"><span>              <span style="color:#8be9fd;font-style:italic">identities</span>: [
</span></span><span style="display:flex;"><span>                <span style="color:#8be9fd;font-style:italic">apimInstance</span>.<span style="color:#8be9fd;font-style:italic">identity</span>.<span style="color:#8be9fd;font-style:italic">principalId</span> <span style="color:#6272a4">// APIM System Assigned Principal Id</span>
</span></span><span style="display:flex;"><span>              ]
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>          }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">platform</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">enabled</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">runtimeVersion</span>: <span style="color:#f1fa8c">&#39;~1&#39;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>Note:</p>
<p><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>EasyAuth is managed by AppService, and for an incoming request, it is a hop that comes before LA Runtime. When EasyAuth is enabled for a Logicapp standard, all incoming requests are validated against the policies in your V2 Auth settings.</p>
<p>If you have “unauthenticatedClientAction”: “Return401” and when the request fails with EasyAuth, those requests are not routed to LA runtime and will fail with 401 from AppService. Therefore, you will also observe broken portal experience with Return401. When you set it to “AllowAnonymous”, all calls (failed and successful) will be routed to the LA runtime. The LA runtime will know if the request failed with EasyAuth or was successful and will process the request accordingly. For example, to get run histories, we authenticate it on SAS specific to that run generated based on the Logic Apps access keys. LA runtime will know that this request failed with EasyAuth but it will be processed successfully as it has valid SAS. The underlying AppService platform will have no knowledge of validating other auth like SAS.</p>
</blockquote>
</li>
<li>
<p>Configure API Management to obtain a valid Bearer token and add it to the request Authorization Header. Implemented through <a href="https://learn.microsoft.com/en-us/azure/api-management/authentication-managed-identity-policy">APIM Policy</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-XML" data-lang="XML"><span style="display:flex;"><span><span style="color:#6272a4">&lt;!-- API ALL OPERATIONS SCOPE --&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">&lt;policies&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;inbound&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">&lt;!-- Uses System Assigned Managed Identity of the APIM Instance --&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&lt;authentication-managed-identity</span> <span style="color:#50fa7b">resource=</span><span style="color:#f1fa8c">&#34;https://management.azure.com/&#34;</span> <span style="color:#50fa7b">output-token-variable-name=</span><span style="color:#f1fa8c">&#34;msi-access-token&#34;</span> <span style="color:#50fa7b">ignore-error=</span><span style="color:#f1fa8c">&#34;false&#34;</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&lt;set-header</span> <span style="color:#50fa7b">name=</span><span style="color:#f1fa8c">&#34;Authorization&#34;</span> <span style="color:#50fa7b">exists-action=</span><span style="color:#f1fa8c">&#34;override&#34;</span><span style="color:#ff79c6">&gt;</span>
</span></span><span style="display:flex;"><span> 	         <span style="color:#ff79c6">&lt;value&gt;</span>@(&#34;Bearer &#34; + (string)context.Variables[&#34;msi-access-token&#34;])<span style="color:#ff79c6">&lt;/value&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&lt;/set-header&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/inbound&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;backend&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/backend&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;outbound&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/outbound&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;on-error&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/on-error&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">&lt;/policies&gt;</span>
</span></span></code></pre></div></li>
<li>
<p>Configure API Management to Append the Logic App Workflow api-version query parameter to the request. Implemented through <a href="https://learn.microsoft.com/en-us/azure/api-management/set-query-parameter-policy">APIM Policy</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-XML" data-lang="XML"><span style="display:flex;"><span><span style="color:#6272a4">&lt;!-- API OPERATION SCOPE --&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">&lt;policies&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;inbound&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;rewrite-uri</span> <span style="color:#50fa7b">template=</span><span style="color:#f1fa8c">&#34;__uri__&#34;</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;set-query-parameter</span> <span style="color:#50fa7b">name=</span><span style="color:#f1fa8c">&#34;api-version&#34;</span> <span style="color:#50fa7b">exists-action=</span><span style="color:#f1fa8c">&#34;append&#34;</span><span style="color:#ff79c6">&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&lt;value&gt;</span>__api-version__<span style="color:#ff79c6">&lt;/value&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;/set-query-parameter&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/inbound&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;backend&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/backend&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;outbound&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/outbound&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;on-error&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/on-error&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">&lt;/policies&gt;</span>
</span></span></code></pre></div></li>
</ol>
<h2 id="summary">Summary</h2>
<p>In short, we have defined our three A&rsquo;s (Access, Authentication, Authorisation) providing further governance on our Standard Logic App and API Management through the use of Easy Auth. To see my worked example, have a look at my <a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend">GitHub repository</a> along with a README that explains how to get started.</p>
<p>Hope this helps and have fun.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Trigger workflows in Standard logic apps with Easy Auth | <a href="https://techcommunity.microsoft.com/t5/azure-integration-services-blog/trigger-workflows-in-standard-logic-apps-with-easy-auth/ba-p/3207378">https://techcommunity.microsoft.com/t5/azure-integration-services-blog/trigger-workflows-in-standard-logic-apps-with-easy-auth/ba-p/3207378</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Easy Auth | Function App with Azure API Management</title><link>https://andrewilson.co.uk/post/2024/11/function-app-easy-auth-apim/</link><pubDate>Thu, 14 Nov 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/11/function-app-easy-auth-apim/</guid><description>Overview The recent work that I have been doing with Function Apps and linking them as backends to Azure API Management has relied on the use of the Function Apps Function SAS key for security. This is a valid authentication approach, but there are risks that you need to be aware of as well as best practices that you need to be abiding by. Such as:
Some Potential Risks:</description><content:encoded><![CDATA[<p><a href="https://github.com/Andrew-D-Wilson/Function-App-APIM-Backend/">
  <img src="https://img.shields.io/badge/Repo-Easy%20Auth%20With%20Function%20Apps%20And%20APIM-blue?logo=github&amp;style=for-the-badge" alt="GitHub Repository">

</a></p>
<h2 id="overview">Overview</h2>
<p>The <a href="/post/2024/10/function-app-apim-backend/">recent work</a> that I have been doing with Function Apps and linking them as backends to Azure API Management has relied on the use of the Function Apps Function SAS key for security. This is a valid authentication approach, but there are risks that you need to be aware of as well as <a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview#best-practices-when-using-sas">best practices</a> that you need to be abiding by. Such as:</p>
<ul>
<li>
<p>Some Potential Risks:</p>
<ul>
<li>If a <strong>SAS is leaked</strong>, it can be used by anyone who obtains it to call your Function.</li>
<li>If a <strong>SAS expires</strong> and the API Management API has not been updated to make use of the updated SAS, then the integration functionality will be hindered.</li>
</ul>
</li>
<li>
<p>Some SAS Best Practices to mitigate risks:</p>
<ul>
<li><strong>Always use HTTPS</strong> - If a SAS is passed over HTTP and intercepted, an attacker can perform a man-in-the-middle attack and read the SAS.</li>
<li><strong>Have a revocation plan</strong> - Make sure that you are prepared to respond if a SAS is compromised.</li>
<li><strong>Have a rotation plan</strong> - Look to replace the SAS with new ones at regular intervals and include shorter time intervals for the expiration period.</li>
</ul>
</li>
</ul>
<p>But what if you require further governance whereby the Function App requires a valid Entra ID bearer token in order to be invoked. To implement this we are going to make use of Easy Auth.</p>
<h2 id="easy-auth">Easy Auth</h2>
<p><a href="https://learn.microsoft.com/en-us/azure/app-service/overview-authentication-authorization">Easy Auth</a> is a built-in authentication and authorisation capability provided by Azure App Services, Azure Functions, and <a href="/post/2024/02/standard-logic-app-easy-auth-apim/">Standard Logic Apps</a>. Easy Auth makes use of federated identity whereby a third-party identity provider manages the user identities and authentication flow for you.</p>
<p>
  <img src="/images/posts/2024/02/EasyAuth.png" alt="Eay Auth">

</p>
<p>Easy Auth is a platform feature running on the same virtual machine as your application. Once enabled, any incoming HTTP requests will pass through this feature prior to being handled by your application. Easy Auth runs separately from your application code and can be configured using ARM settings or using a configuration file.</p>
<h2 id="using-easy-auth-and-linking-to-api-management">Using Easy Auth and Linking to API Management</h2>
<p>As mentioned above, I would like to use Easy Auth to protect my HTTP triggered functions, but more importantly, I would like Azure API Management to be the only identity that can be used to make requests to my functions as shown in the diagram below:</p>
<p>
  <img src="/images/posts/2024/11/EasyAuthAPIM.png" alt="Eay Auth">

</p>
<blockquote>
<p><strong>Note:</strong></p>
<p>As we will be invoking the Function App HTTP triggered Functions with Easy Auth, we no longer need to specify the following parameters or details:</p>
<ul>
<li>Parameter <strong>code</strong>: Shared-Access-Signature</li>
<li><strong>AuthorizationLevel</strong>: When Easy Auth is enabled, the HTTP triggered Functions no longer need to have the <code>AuthorizationLevel</code> set to Function but rather set to Anonymous. This is because Easy Auth will conduct the authorisation prior to reaching your code and no longer requires a SAS key to be provided of which the Function AuthorisationLevel requires.</li>
</ul>
</blockquote>
<p>For setup, we will need to conduct the following steps:</p>
<blockquote>
<p><em>I have opted for Infrastructure as Code (IaC) as my method of implementation, specifically Bicep. I have also opted for Microsoft Entra as my Identity Provider.</em></p>
</blockquote>
<ol>
<li>
<p>Configure the Function App to <a href="https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad?tabs=workforce-configuration">Use Microsoft Entra sign-in</a>.</p>
<p>Working through the Microsoft instructions in the link above, you will require as minimum the following when setting up the Function App Application Registration:</p>
<ul>
<li>
<p>Select the supported account type. (<em>I&rsquo;m using Current tenant - single tenant</em>)</p>
</li>
<li>
<p>Setup a Redirect URI for your Function:</p>
<p>Select Web for platform and set the URI to <code>&lt;app-url&gt;/.auth/login/aad/callback</code>. For example, <a href="https://contoso.azurewebsites.net/.auth/login/aad/callback">https://contoso.azurewebsites.net/.auth/login/aad/callback</a>.</p>
</li>
<li>
<p>Make not of the Application (client) ID.</p>
</li>
<li>
<p>Create a Client Secret and store this securely (<em>I&rsquo;m using Azure  DevOps Secure Library Variables as part of a deployment</em>).</p>
<p><em>This is a secret value that the application uses to prove its identity when requesting a token. This value is saved in your app&rsquo;s configuration as a slot-sticky application setting named <code>MICROSOFT_PROVIDER_AUTHENTICATION_SECRET</code>. If the client secret isn&rsquo;t set, sign-in operations from the service use the OAuth 2.0 implicit grant flow, which isn&rsquo;t recommended.</em></p>
</li>
<li>
<p>Expose an API &gt; Add &gt; Save. This value uniquely identifies the application when it&rsquo;s used as a resource, allowing tokens to be requested that grant access. It&rsquo;s used as a prefix for scopes you create.</p>
<p>I am using a single-tenant app and therefore using the default value. Appears as such <code>api://&lt;application-client-id&gt;</code></p>
</li>
<li>
<p>Add the <code>user_impersonation</code> scope to your App Registration allowing <code>Admins and users</code> to consent.</p>
</li>
</ul>
</li>
<li>
<p>Make sure that API Management has been setup with Managed Identity. I am using System Assigned.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Deployment of the APIM instance&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">apimInstanceDeploy</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service@2024-05-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">identity</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">type</span>: <span style="color:#f1fa8c">&#39;SystemAssigned&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
<li>
<p>Store the App Registration Secret in KeyVault and Reference in your Function App Settings to allow the Function App to prove its identity.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">secure</span>()
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The client secret for the Easy Auth App Registration&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">applicationEasyAuthClientSecret</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Role Definition Id for the Key Vault Secrets User role&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefId</span> = <span style="color:#f1fa8c">&#39;4633458b-17de-408a-b874-0445c86b69e6&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Deploy the Application Easy Auth App Registration Secret to Keyvault&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">vaultFunctionAppRegSecret</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults/secrets@2023-07-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationFunctionAppName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-EasyAuth-Secret&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">applicationKeyVaultDeploy</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">contentType</span>: <span style="color:#f1fa8c">&#39;string&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">value</span>: <span style="color:#8be9fd;font-style:italic">applicationEasyAuthClientSecret</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Deploy the Application Function App&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">applicationFunctionAppDeploy</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/sites@2024-04-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">applicationFunctionAppName</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">identity</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">type</span>: <span style="color:#f1fa8c">&#39;SystemAssigned&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">config</span> <span style="color:#f1fa8c">&#39;config@2024-04-01&#39;</span> = {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;appsettings&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>      ...
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">MICROSOFT_PROVIDER_AUTHENTICATION_SECRET</span>: <span style="color:#f1fa8c">&#39;@Microsoft.KeyVault(VaultName=</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationKeyVaultDeploy</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">;SecretName=</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">vaultFunctionAppRegSecret</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">)&#39;</span>
</span></span><span style="display:flex;"><span>      ...
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Create the RBAC for the Function App to Read the Secret from Key Vault&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">applicationFunctionAppRBACWithKV</span> <span style="color:#f1fa8c">&#39;Microsoft.Authorization/roleAssignments@2022-04-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#50fa7b">guid</span>(<span style="color:#8be9fd;font-style:italic">applicationKeyVaultDeploy</span>.<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#8be9fd;font-style:italic">applicationFunctionAppDeploy</span>.<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefId</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">scope</span>: <span style="color:#8be9fd;font-style:italic">vaultFunctionAppRegSecret</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">principalId</span>: <span style="color:#8be9fd;font-style:italic">applicationFunctionAppDeploy</span>.<span style="color:#8be9fd;font-style:italic">identity</span>.<span style="color:#8be9fd;font-style:italic">principalId</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">roleDefinitionId</span>: <span style="color:#50fa7b">subscriptionResourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Authorization/roleDefinitions&#39;</span>, <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefId</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">principalType</span>: <span style="color:#f1fa8c">&#39;ServicePrincipal&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
<li>
<p>Enable Easy Auth on the Function App using <a href="https://learn.microsoft.com/en-us/azure/templates/microsoft.web/sites/config-authsettingsv2?pivots=deployment-language-bicep">ARM/Bicep Template AuthSettingsV2</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Setup the Easy Auth config settings for the Function App&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">applicationAuthSettings</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/sites/config@2024-04-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;authsettingsV2&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">applicationFunctionAppDeploy</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">globalValidation</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">requireAuthentication</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">unauthenticatedClientAction</span>: <span style="color:#f1fa8c">&#39;Return401&#39;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">httpSettings</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">requireHttps</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">routes</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">apiPrefix</span>: <span style="color:#f1fa8c">&#39;/.auth&#39;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">forwardProxy</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">convention</span>: <span style="color:#f1fa8c">&#39;NoProxy&#39;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">identityProviders</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">azureActiveDirectory</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">enabled</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">registration</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">openIdIssuer</span>: <span style="color:#50fa7b">uri</span>(<span style="color:#f1fa8c">&#39;https://sts.windows.net/&#39;</span>, <span style="color:#50fa7b">tenant</span>().<span style="color:#8be9fd;font-style:italic">tenantId</span>)
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">clientId</span>: <span style="color:#8be9fd;font-style:italic">functionAppEasyAuthClientId</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">clientSecretSettingName</span>: <span style="color:#f1fa8c">&#39;MICROSOFT_PROVIDER_AUTHENTICATION_SECRET&#39;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">validation</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">allowedAudiences</span>: <span style="color:#50fa7b">environment</span>().<span style="color:#8be9fd;font-style:italic">authentication</span>.<span style="color:#8be9fd;font-style:italic">audiences</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">defaultAuthorizationPolicy</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd;font-style:italic">allowedPrincipals</span>: {
</span></span><span style="display:flex;"><span>              <span style="color:#8be9fd;font-style:italic">identities</span>: [
</span></span><span style="display:flex;"><span>                <span style="color:#8be9fd;font-style:italic">apimInstance</span>.<span style="color:#8be9fd;font-style:italic">identity</span>.<span style="color:#8be9fd;font-style:italic">principalId</span>
</span></span><span style="display:flex;"><span>              ]
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>          }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">platform</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">enabled</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">runtimeVersion</span>: <span style="color:#f1fa8c">&#39;~1&#39;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>Note:</p>
<p>EasyAuth is managed by the AppService, and for an incoming request, it is a hop that comes before FA Runtime. When EasyAuth is enabled for a Function App, all incoming requests are validated against the policies in your V2 Auth settings.</p>
</blockquote>
</li>
<li>
<p>Configure API Management to obtain a valid Bearer token and add it to the request Authorization Header. Implemented through <a href="https://learn.microsoft.com/en-us/azure/api-management/authentication-managed-identity-policy">APIM Policy</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-XML" data-lang="XML"><span style="display:flex;"><span><span style="color:#6272a4">&lt;!-- API ALL OPERATIONS SCOPE --&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">&lt;policies&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;inbound&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">&lt;!-- Uses System Assigned Managed Identity of the APIM Instance --&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&lt;authentication-managed-identity</span> <span style="color:#50fa7b">resource=</span><span style="color:#f1fa8c">&#34;https://management.azure.com/&#34;</span> <span style="color:#50fa7b">output-token-variable-name=</span><span style="color:#f1fa8c">&#34;msi-access-token&#34;</span> <span style="color:#50fa7b">ignore-error=</span><span style="color:#f1fa8c">&#34;false&#34;</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&lt;set-header</span> <span style="color:#50fa7b">name=</span><span style="color:#f1fa8c">&#34;Authorization&#34;</span> <span style="color:#50fa7b">exists-action=</span><span style="color:#f1fa8c">&#34;override&#34;</span><span style="color:#ff79c6">&gt;</span>
</span></span><span style="display:flex;"><span> 	         <span style="color:#ff79c6">&lt;value&gt;</span>@(&#34;Bearer &#34; + (string)context.Variables[&#34;msi-access-token&#34;])<span style="color:#ff79c6">&lt;/value&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&lt;/set-header&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/inbound&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;backend&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/backend&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;outbound&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/outbound&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;on-error&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/on-error&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">&lt;/policies&gt;</span>
</span></span></code></pre></div></li>
</ol>
<h2 id="summary">Summary</h2>
<p>In short, we have applied further governance on our Function App and API Management through the use of Easy Auth. To see my worked example, have a look at my <a href="https://github.com/Andrew-D-Wilson/Function-App-APIM-Backend">GitHub repository</a> along with a README that explains how to get started.</p>
<p>Hope this helps and have fun.</p>
]]></content:encoded></item><item><title>Bicep | Prevent a Nasty Refactor with Function Namespaces</title><link>https://andrewilson.co.uk/post/2024/05/bicep-function-namespaces/</link><pubDate>Mon, 27 May 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/05/bicep-function-namespaces/</guid><description>Problem Space There have been few times where I have landed into this particular predicament whereby either by my own doing or through the use of another&amp;rsquo;s code base, a deep nested or thoroughly utilised (parameter/variable/or other defined item) has been created with the same name as a Bicep function. As by Murphy&amp;rsquo;s law, its only once you have reached this point of no return that you realise that your items name conflicts.</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>There have been few times where I have landed into this particular predicament whereby either by my own doing or through the use of another&rsquo;s code base, a deep nested or thoroughly utilised (parameter/variable/or other defined item) has been created with the same name as a Bicep function. As by Murphy&rsquo;s law, its only once you have reached this point of no return that you realise that your items name conflicts.</p>
<p>Now if you are like many in your unawares, your thoughts and actions will conclude to a singular one of <em>&rsquo;that sucks&hellip; followed by a nasty refactor</em>'.</p>
<h2 id="solution---namespaces">Solution - Namespaces</h2>
<p>Namespaces are a declarative scope in which identifiers such as the names of types, functions, and variables can be declared. These namespaces are used to organise code into logical groups and to prevent name collisions such as the one you are currently experiencing.</p>
<p>Thankfully, in <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions#namespaces-for-functions">Bicep there are two namespaces</a> where all functions are split, <code>az</code> and <code>sys</code>.</p>
<ul>
<li><strong>az</strong> | Contains functions that are specific to an Azure deployment such as:
<ul>
<li>deployment</li>
<li>environment</li>
<li>resourceGroup</li>
<li>subscription</li>
</ul>
</li>
<li><strong>sys</strong> | Contains functions that are used to construct values, and decorators for parameters and resource loops. This includes but is not limited to:
<ul>
<li>[<strong>Array</strong>] concat</li>
<li>[<strong>File</strong>] loadJsonContent</li>
<li>[<strong>Lambda</strong>] filter</li>
<li>[<strong>Logical</strong>] bool</li>
<li>[<strong>Numeric</strong>] int</li>
<li>[<strong>String</strong>] contains</li>
</ul>
</li>
</ul>
<p>To make use of these namespaces, simply add the namespace identifier in front of the function. The example below shows a real world example of this issue where a parameter name &lsquo;<em>environment</em>&rsquo; has been extensively utilised in this template and many others. This particular template is being used to deploy an AuthProvider in API Management, and the author wishes to utilise the environment function for the <code>authentication.loginEndpoint</code>. Without the use of the az namespace, the environment parameter would need to be refactored both in this template and wider templates for consistency.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">/******************************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Function</span> <span style="color:#8be9fd;font-style:italic">Namespace</span> <span style="color:#8be9fd;font-style:italic">Example</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Author</span>: <span style="color:#8be9fd;font-style:italic">Andrew</span> <span style="color:#8be9fd;font-style:italic">Wilson</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">******************************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The environment to deploy the resources to.&#39;</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">allowed</span>([
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;dev&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;test&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;prod&#39;</span>
</span></span><span style="display:flex;"><span>])
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">environment</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;dev&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">ApiManagementName</span> = <span style="color:#50fa7b">toLower</span>(<span style="color:#f1fa8c">&#39;apim-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">projectName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">environment</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">location</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Create a new Auth Provider in APIM Credential Manager</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">AuthorizationProvider</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/authorizationProviders@2023-05-01-preview&#39;</span> = {
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">oauth2</span>: {
</span></span><span style="display:flex;"><span>      ...
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">grantTypes</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">clientCredentials</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">loginUri</span>: <span style="color:#8be9fd;font-style:italic">az</span>.<span style="color:#50fa7b">environment</span>().<span style="color:#8be9fd;font-style:italic">authentication</span>.<span style="color:#8be9fd;font-style:italic">loginEndpoint</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>As always, have a play, and hope this helps.</p>
]]></content:encoded></item><item><title>Microsoft.Web/Connections | Access Policies</title><link>https://andrewilson.co.uk/post/2024/04/api-connection-access-policies/</link><pubDate>Wed, 10 Apr 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/04/api-connection-access-policies/</guid><description>Problem Space I have recently been adding email alerting to some Logic App Standard workflows as part of the error handling flow. In doing so I made use of an existing Office 365 Outlook Connector in the Azure Subscription; the connector is not built in for Standard Logic Apps but is rather part of the Managed Api Connections.
Managed Api Connectors require more than just the connection details to be detailed in the Logic Apps connections.</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>I have recently been adding email alerting to some Logic App Standard workflows as part of the error handling flow. In doing so I made use of an existing Office 365 Outlook Connector in the Azure Subscription; the connector is not built in for Standard Logic Apps but is rather part of the Managed Api Connections.</p>
<p>Managed Api Connectors require more than just the connection details to be detailed in the Logic Apps connections.json configuration as shown below:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;managedApiConnections&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;office365&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;api&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;id&#34;</span>: <span style="color:#f1fa8c">&#34;@appsetting(&#39;office365_apiId&#39;)&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;authentication&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;ManagedServiceIdentity&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;connection&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;id&#34;</span>: <span style="color:#f1fa8c">&#34;@appsetting(&#39;office365_connectionId&#39;)&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;connectionRuntimeUrl&#34;</span>: <span style="color:#f1fa8c">&#34;@appsetting(&#39;office365_connectionRuntimeUrl&#39;)&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The Managed API Connector also requires that the Logic App has been granted access to the Connector through the use of Access Policies. This can be configured through the Azure Portal or through infrastructure deployments, in my case I have opted for infrastructure deployments with the use of Bicep templates.</p>
<p>It was at this point that I realised that the Access Policy resource which is a child resource to <code>Microsoft.Web/connections</code> has not been <a href="https://learn.microsoft.com/en-us/azure/templates/microsoft.web/connections?tabs=json&amp;pivots=deployment-language-bicep">documented</a>, obtainable through an Azure Portal template export, or through the Resource Explorer.</p>
<h2 id="solution">Solution</h2>
<p>From digging around the <a href="https://learn.microsoft.com/en-us/azure/logic-apps/azure-arc-enabled-logic-apps-create-deploy-workflows?tabs=azure-cli#arm-template">Access Policy resource</a> has the following ARM Template Schema:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>   <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Microsoft.Web/connections/accessPolicies&#34;</span>,
</span></span><span style="display:flex;"><span>   <span style="color:#ff79c6">&#34;apiVersion&#34;</span>: <span style="color:#f1fa8c">&#34;2016-06-01&#34;</span>,
</span></span><span style="display:flex;"><span>   <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;[concat(&#39;&lt;connection-name&gt;&#39;),&#39;/&#39;,&#39;&lt;object-ID&gt;&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>   <span style="color:#ff79c6">&#34;location&#34;</span>: <span style="color:#f1fa8c">&#34;&lt;location&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>   <span style="color:#ff79c6">&#34;dependsOn&#34;</span>: [
</span></span><span style="display:flex;"><span>      <span style="color:#f1fa8c">&#34;[resourceId(&#39;Microsoft.Web/connections&#39;, parameters(&#39;connection_name&#39;))]&#34;</span>
</span></span><span style="display:flex;"><span>   ],
</span></span><span style="display:flex;"><span>   <span style="color:#ff79c6">&#34;properties&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;principal&#34;</span>: {
</span></span><span style="display:flex;"><span>         <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;ActiveDirectory&#34;</span>,
</span></span><span style="display:flex;"><span>         <span style="color:#ff79c6">&#34;identity&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;objectId&#34;</span>: <span style="color:#f1fa8c">&#34;&lt;object-ID&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;tenantId&#34;</span>: <span style="color:#f1fa8c">&#34;&lt;tenant-ID&gt;&#34;</span>
</span></span><span style="display:flex;"><span>         }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>   }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p><strong>Resource Properties</strong></p>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&lt;connection-name&gt;</code></td>
<td>The name for your managed API connection, for example office365</td>
</tr>
<tr>
<td><code>&lt;object-ID&gt;</code></td>
<td>The object ID for your Microsoft Entra identity, for example the system assigned managed identity of the logic app</td>
</tr>
<tr>
<td><code>&lt;tenant-ID&gt;</code></td>
<td>The tenant ID for your Microsoft Entra identity</td>
</tr>
</tbody>
</table>
</blockquote>
<p>In Bicep this takes on the following form:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Create the access policy for the Logic App to access the managed Api Connection &#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">managedAPIConnectionAccessPolicy</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/connections/accessPolicies@2016-06-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">logicApp</span>.<span style="color:#8be9fd;font-style:italic">name</span> <span style="color:#6272a4">// Using the Logic App Name for readability</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">managedAPIConnector</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">Location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">principal</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">type</span>: <span style="color:#f1fa8c">&#39;ActiveDirectory&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">identity</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">tenantId</span>: <span style="color:#50fa7b">subscription</span>().<span style="color:#8be9fd;font-style:italic">tenantId</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">objectId</span>: <span style="color:#8be9fd;font-style:italic">logicApp</span>.<span style="color:#8be9fd;font-style:italic">identity</span>.<span style="color:#8be9fd;font-style:italic">principalId</span> <span style="color:#6272a4">// Using the System Assigned Managed Identity of the Logic App</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p><strong>Note</strong></p>
<p>The Bicep tooling will give you the following warning but will not hinder in both the transpilation into ARM or in the deployment of the resource:</p>
<p><code>Resource type &quot;Microsoft.Web/connections/accessPolicies@2016-06-01&quot; does not have types available.</code></p>
</blockquote>
]]></content:encoded></item><item><title>Bicep | User Defined Types</title><link>https://andrewilson.co.uk/post/2024/02/bicep-user-defined-types/</link><pubDate>Wed, 07 Feb 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/02/bicep-user-defined-types/</guid><description>Problem Space Over the years of developing Infrastructure as Code (IaC) with either ARM templates or Bicep (since it was released in 2020), I have made it my best practice where possible to use well-defined base type parameters (Strings | Integers | Booleans) so that the templates are usable and maintainable by collaborators apart from myself. This usually equated to where possible avoiding the use of Object and Array parameters, although in many cases the use of these types was inevitable given the complexity of the infrastructure and resources being deployed.</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>Over the years of developing Infrastructure as Code (IaC) with either ARM templates or Bicep (since it was released in 2020), I have made it my best practice where possible to use well-defined base type parameters (<code>Strings | Integers | Booleans</code>) so that the templates are usable and maintainable by collaborators apart from myself. This usually equated to where possible avoiding the use of <code>Object and Array</code> parameters, although in many cases the use of these types was inevitable given the complexity of the infrastructure and resources being deployed.</p>
<p>In the situations where I needed to make use of <code>Object or Array</code> parameters, I would heavily rely on defining attributes, comments, and defaults to allow the template to retain some kind of reusability and maintainability, such as:</p>
<blockquote>
<p>Example: Array of Objects</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Array of API operations&#39;</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">minLength</span>(<span style="color:#8be9fd;font-style:italic">1</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">apimAPIOperations</span> <span style="color:#8be9fd;font-style:italic">array</span> = [
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;&#39;</span> <span style="color:#6272a4">// Name of API Operation</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">displayName</span>: <span style="color:#f1fa8c">&#39;&#39;</span> <span style="color:#6272a4">// Friendly Name of API Operation</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">method</span>: <span style="color:#f1fa8c">&#39;&#39;</span> <span style="color:#6272a4">// HTTP Method -  GET | POST | PUT | DELETE | PATCH</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">lgWorkflowName</span>: <span style="color:#f1fa8c">&#39;&#39;</span> <span style="color:#6272a4">// Name of the Backing Logic App Workflow</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">lgWorkflowTrigger</span>: <span style="color:#f1fa8c">&#39;&#39;</span> <span style="color:#6272a4">// Name of the Backing Logic App Workflow HTTP Trigger</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>Although this approach provides nice documentation around what is expected of the parameter,</p>
<ul>
<li>There is no enforcement on the object structure allowing users to pass in a variation of the object causing issues further on.</li>
<li>The parameter has a default value for documentation purposes, but if a user is unaware, they might use the default, again resulting in issues further on.</li>
<li>There is no intellisense when using the parameter. You will completely rely on the fact that you have referenced the properties and structuring within correctly.</li>
</ul>
<p>I promise, its not all doom and gloom, the light at the end of the tunnel has arrived&hellip;</p>
<h2 id="my-new-best-practice">My New Best Practice</h2>
<p>As of December 2023 <a href="https://github.com/Azure/bicep/releases/tag/v0.24.24">Bicep Version 0.24.24</a> <strong>User Defined Types</strong> are no longer experimental! <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/install">Bicep CLI version 0.12.X</a> or higher is required to use this feature.</p>
<blockquote>
<p>⚠️ <strong>NOTE:</strong></p>
<p>When transpiled into ARM, the user-defined types uses the new <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/syntax#languageversion-20">Language Version 2.0</a>.</p>
<ul>
<li>The current release of the Azure Resource Manager Tools extension for Visual Studio Code does not recognize the enhancements made in languageVersion 2.0.</li>
<li>The Azure Portal Custom deployment does not currently recognize the enhancements made in languageVersion 2.0.</li>
</ul>
</blockquote>
<p>User Defined Types allow us to define our own custom types with ambient types <code>Strings | Integers | Booleans</code>, primitive literals for validation (allowed options) <code> = 'bicep' | 'arm' | 'azure'</code> and markings for optional properties <code>?</code>. There is more configurations and abilities with this feature so for more information see <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/user-defined-data-types">User-defined data types in Bicep</a>.</p>
<p>User Defined Types removes my problem space. I can now create a defined type that represents my complex object or array. This new Type can then be used on a parameter with full intellisense support and validation so incorrect usage is flagged early with a Fail Fast approach.</p>
<p>The problem case example shown earlier can now be represented as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#6272a4">// ** User Defined Types **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ************************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Configuration properties for setting up a LG App stnd APIM API Operation&#39;</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">metadata</span>({
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;Name of the API Operation&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">displayName</span>: <span style="color:#f1fa8c">&#39;User friendly name of the API Operation&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">method</span>: <span style="color:#f1fa8c">&#39;The API Operations HTTP method&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">lgWorkflowName</span>: <span style="color:#f1fa8c">&#39;Name of the Standard Logic App Workflow to use for the Operation Backend&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">lgWorkflowTrigger</span>: <span style="color:#f1fa8c">&#39;Name of the Workflow HTTP Trigger&#39;</span>
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">sealed</span>()
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">apimAPIOperation</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">displayName</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">method</span>: <span style="color:#f1fa8c">&#39;GET&#39;</span> | <span style="color:#f1fa8c">&#39;PUT&#39;</span> | <span style="color:#f1fa8c">&#39;POST&#39;</span> | <span style="color:#f1fa8c">&#39;PATCH&#39;</span> | <span style="color:#f1fa8c">&#39;DELETE&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">lgWorkflowName</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">lgWorkflowTrigger</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;One or more APIM API Operations to configure&#39;</span>)
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">minLength</span>(<span style="color:#8be9fd;font-style:italic">1</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">apimAPIOperationArray</span> = <span style="color:#8be9fd;font-style:italic">apimAPIOperation</span>[]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Array of API operations&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">apimAPIOperations</span> <span style="color:#8be9fd;font-style:italic">apimAPIOperationArray</span>
</span></span></code></pre></div><p>The example above shows the use of:</p>
<ul>
<li>
<p><code>@description</code> decorator to provide a high-level summary of the type</p>
</li>
<li>
<p><code>@metadata</code> decorator being used to document the object properties.</p>
</li>
<li>
<p><code>@sealed</code> decorator being used to mark the object parameter as only permitting properties specifically included in the type definition.</p>
</li>
<li>
<p>Primitive Literals for Validation, the value specified can only be one of the following: <code>method: 'GET' | 'PUT' | 'POST' | 'PATCH' | 'DELETE'</code>.</p>
</li>
<li>
<p>Union of types by creating an array type of a user-defined object type:</p>
<p><code>type apimAPIOperationArray = apimAPIOperation[]</code></p>
</li>
</ul>
<p>Have a go and see if this also becomes your new best practice.
For more configurations and abilities with this feature see <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/user-defined-data-types">User-defined data types in Bicep</a>.</p>
]]></content:encoded></item><item><title>Azure RBAC Key Vault | Role Assignment for Specific Secret</title><link>https://andrewilson.co.uk/post/2023/11/rbac-key-vault-specific-secret/</link><pubDate>Wed, 22 Nov 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/11/rbac-key-vault-specific-secret/</guid><description>Background Azure role-based access control (Azure RBAC) provides fine grained control over access to Azure resources. Azure RBAC is founded on top of the Azure Resource Manager which allows us to provide access authorisation at differing scope levels ranging from the Management Group through to individual resources.
With RBAC enabled key vaults we can manage access to the resource and data stored in the vault. We can also manage access for individual keys, secrets, and certificates.</description><content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>Azure role-based access control (Azure RBAC) provides fine grained control over access to Azure resources. Azure RBAC is founded on top of the Azure Resource Manager which allows us to provide access authorisation at differing scope levels ranging from the Management Group through to individual resources.</p>
<p>With RBAC enabled key vaults we can manage access to the resource and data stored in the vault. We can also manage access for individual keys, secrets, and certificates.</p>
<h2 id="scenario">Scenario</h2>
<p>
  <img src="/images/posts/2023/11/KVScenario.png" alt="Key Vault RBAC Scenario">

</p>
<p>In this scenario we have an application that has stored all its secrets in its own application specific key vault. Part of this application makes use of an Azure Function that we would like to place into Azure API Management (APIM).</p>
<blockquote>
<p>An Application can be a single or group of related services.</p>
</blockquote>
<p>When deploying the Azure Function we are placing the Function Authorisation Key into the Application Specific Key Vault. As part of the APIM API Backend we would like to add the Function Authorisation Key as a header and obtain this key from the application specific key vault.</p>
<p>If we provide APIM access to the entire application key vault we would be compromising our security boundary.</p>
<blockquote>
<p><strong><a href="https://learn.microsoft.com/en-us/azure/key-vault/general/best-practices">Best Practice</a></strong></p>
<p>Security Boundary - We do not want to provide complete access to the application Key Vault. If there is a breach of security we do not want to compromise the application and widen the blast radius.</p>
</blockquote>
<p>To mitigate this, we can specify granular access to the specific secret in the key vault that APIM requires. In this case, if there were to be a security breach, we have limited the reach and access to which damage could spread.</p>
<h2 id="solution">Solution</h2>
<p>To setup the RBAC permission between APIM and the application specific Key Vault, we will need to have the following configured:</p>
<ol>
<li>For a deployment principle to add role assignments, you must have Microsoft.Authorization/roleAssignments/write and Microsoft.Authorization/roleAssignments/delete permissions setup, such as one of the following:
<ul>
<li><a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#key-vault-data-access-administrator-preview">Key Vault Data Access Administrator (preview)</a></li>
<li><a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#user-access-administrator">User Access Administrator</a></li>
<li><a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#owner">Owner</a></li>
</ul>
</li>
<li>Make sure your Application Key Vault is RBAC Enabled.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">symbolicname</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults@2022-07-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;string&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#f1fa8c">&#39;string&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">enableRbacAuthorization</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><ol start="3">
<li>Azure API Management is setup to use <a href="https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-use-managed-service-identity">System Assigned Managed Identity</a>.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">symbolicname</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service@2023-03-01-preview&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;string&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#f1fa8c">&#39;string&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">sku</span>: {
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">identity</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">type</span>: <span style="color:#f1fa8c">&#39;SystemAssigned&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><ol start="4">
<li>The Azure Function Auth Key added into Key Vault as a secret.</li>
</ol>
<blockquote>
<p>Note: The function default host key is created and only accessible after the function code is provisioned. The following IaC would need to be conducted as a secondary deployment.</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Retrieve the Application Key Vault instance to store secrets&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">keyVault</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults@2023-07-01&#39;</span> <span style="color:#8be9fd;font-style:italic">existing</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">keyVaultName</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Retrieve the Function App for linking as a backend&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">functionApp</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/sites@2022-09-01&#39;</span> <span style="color:#8be9fd;font-style:italic">existing</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">functionName</span>
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Vault the Function App Key as a secret&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">vaultFunctionAppKey</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults/secrets@2023-07-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;functionAppKey&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">keyVault</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">contentType</span>: <span style="color:#f1fa8c">&#39;string&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">value</span>: <span style="color:#50fa7b">listkeys</span>(<span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">functionApp</span>.<span style="color:#8be9fd;font-style:italic">id</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/host/default/&#39;</span>, <span style="color:#f1fa8c">&#39;2021-02-01&#39;</span>).<span style="color:#8be9fd;font-style:italic">functionkeys</span>.<span style="color:#8be9fd;font-style:italic">default</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><ol start="5">
<li>Grant APIM Identity Role permissions to access the Key Vault Secret
<ul>
<li>Granting <a href="https://learn.microsoft.com/en-us/azure/key-vault/general/rbac-guide?tabs=azure-cli#azure-built-in-roles-for-key-vault-data-plane-operations">Key Vault Reader</a> Role.</li>
<li>Property Scope is pointing at the Key Vault Secret so access is confined to only that secret.</li>
</ul>
</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;APIM Instance&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">apimInstance</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service@2022-08-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">apimInstanceName</span>
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Grant APIM Key Vault Reader for the function API key secret&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">grantAPIMPermissionsToSecret</span> <span style="color:#f1fa8c">&#39;Microsoft.Authorization/roleAssignments@2022-04-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#50fa7b">guid</span>(<span style="color:#8be9fd;font-style:italic">keyVault</span>.<span style="color:#8be9fd;font-style:italic">id</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">scope</span>: <span style="color:#8be9fd;font-style:italic">vaultFunctionAppKey</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">roleDefinitionId</span>: <span style="color:#50fa7b">subscriptionResourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Authorization/roleDefinitions&#39;</span>, <span style="color:#f1fa8c">&#39;4633458b-17de-408a-b874-0445c86b69e6&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">principalId</span>: <span style="color:#8be9fd;font-style:italic">apimInstance</span>.<span style="color:#8be9fd;font-style:italic">identity</span>.<span style="color:#8be9fd;font-style:italic">principalId</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">principalType</span>: <span style="color:#f1fa8c">&#39;ServicePrincipal&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><ol start="6">
<li>Setup a APIM Backend with a Named Value to make use of the Key Vault Secret</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Create the backend for the Function API&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">functionBackend</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/backends@2022-08-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">apiName</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">apimInstance</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">protocol</span>: <span style="color:#f1fa8c">&#39;http&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">url</span>: <span style="color:#f1fa8c">&#39;https://</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">functionApp</span>.<span style="color:#8be9fd;font-style:italic">properties</span>.<span style="color:#8be9fd;font-style:italic">defaultHostName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/api&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">resourceId</span>: <span style="color:#f1fa8c">&#39;https://management.azure.com</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">functionApp</span>.<span style="color:#8be9fd;font-style:italic">id</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">tls</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">validateCertificateChain</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">validateCertificateName</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">credentials</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">header</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f1fa8c">&#39;x-functions-key&#39;</span>: [
</span></span><span style="display:flex;"><span>          <span style="color:#f1fa8c">&#39;{{</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">apiName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-key}}&#39;</span>
</span></span><span style="display:flex;"><span>        ]
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">dependsOn</span>: [
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">functionBackendNamedValues</span>
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Create the named value for the function API backend&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">functionBackendNamedValues</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/namedValues@2022-08-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">apiName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-key&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">apimInstance</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">displayName</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">apiName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-key&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">tags</span>: [
</span></span><span style="display:flex;"><span>      <span style="color:#f1fa8c">&#39;key&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f1fa8c">&#39;function&#39;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">secret</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">keyVault</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">identityClientId</span>: <span style="color:#ff79c6">null</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">secretIdentifier</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">keyVault</span>.<span style="color:#8be9fd;font-style:italic">properties</span>.<span style="color:#8be9fd;font-style:italic">vaultUri</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">secrets/</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">vaultFunctionAppKey</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">dependsOn</span>: [
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">grantAPIMPermissionsToSecret</span>
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="summary">Summary</h2>
<p>And there we have it, APIM now has granular access to the secrets it requires without compromising our security boundaries that have been put in place to protect the backing application.</p>
<p>Have a play and have fun.</p>
]]></content:encoded></item><item><title>Bicep | Deployment Scope Hopping</title><link>https://andrewilson.co.uk/post/2023/04/bicep-deployment-scope-hopping/</link><pubDate>Tue, 25 Apr 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/04/bicep-deployment-scope-hopping/</guid><description>Background An Azure Tenant is hierarchically structured with the following make up:
Tenant One or more Management Groups One or more Subscriptions One or more Resource groups One or more Resources Deployment Scopes {Tenant, Management Group, Subscription, Resource Group} allow us to deploy respective types of resources at each level.
A Scope is dictated by two attributes, the selected scope level, and the identifier of the item at that scope level.</description><content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>An Azure Tenant is hierarchically structured with the following make up:</p>
<ul>
<li>Tenant</li>
<li>One or more Management Groups</li>
<li>One or more Subscriptions</li>
<li>One or more Resource groups</li>
<li>One or more Resources</li>
</ul>
<p>
  <img src="/images/posts/2023/04/az-scopes.png" alt="AzureScopes">

</p>
<p>Deployment Scopes <code>{Tenant, Management Group, Subscription, Resource Group}</code> allow us to deploy respective types of resources at each level.</p>
<p>A <code>Scope</code> is dictated by two attributes, the selected scope level, and the identifier of the item at that scope level. For example, if I am to deploy at the Subscription Scope, I need to both Identify that I am doing a Subscription Deployment, and I need to provide the identifier of the Subscription. Resource Groups are the lowest in the list of Scope Levels.</p>
<p>
  <img src="/images/posts/2023/04/scopeselection.png" alt="SingleScopeDeployment">

</p>
<h2 id="single-scope-deployment">Single Scope Deployment</h2>
<p>In a single scoped deployment, we essentially design our Bicep templates to target a single scope level such as Resource group. In this type of deployment we only deploy respective resources to a specific Resource Group and no other. For Example:</p>
<p>
  <img src="/images/posts/2023/04/resourcegroupscope.png" alt="SingleScopeDeployment">

</p>
<p>In Bicep this would appear as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Parameters</span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Variables</span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Resources</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">keyVault</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults@2023-02-01&#39;</span> = {
</span></span><span style="display:flex;"><span>	...
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">storageaccount</span> <span style="color:#f1fa8c">&#39;Microsoft.Storage/storageAccounts@2021-02-01&#39;</span> = {
</span></span><span style="display:flex;"><span>	...
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Outputs</span>
</span></span><span style="display:flex;"><span>...
</span></span></code></pre></div><h2 id="parent-child-deployment-scope-hopping">Parent Child Deployment Scope Hopping</h2>
<p>Parent Child Deployment Scope Hopping is where we choose a parent scope that will effectively be our starting point in conducting deployments. In this type of deployment, we can conduct deployments in the parent scope and in each child. For Example:</p>
<p>
  <img src="/images/posts/2023/04/parentchildscope.png" alt="ParentChildScopeDeployment">

</p>
<p>In Bicep this would appear as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;subscription&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Parameters</span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Name of the Second existing Resource Group&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">resourceGroup2Name</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Variables</span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Resources</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Deploy Resource Group 1</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">RG1Deploy</span> <span style="color:#f1fa8c">&#39;Microsoft.Resources/resourceGroups@2022-09-01&#39;</span> = {
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Deploy Resources into Resource Group 1</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">module</span> <span style="color:#8be9fd;font-style:italic">rg1</span> <span style="color:#f1fa8c">&#39;rg1.bicep&#39;</span> = {
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">scope</span>: <span style="color:#8be9fd;font-style:italic">RG1Deploy</span>
</span></span><span style="display:flex;"><span>	...
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Deploy Resources into Existing Resource Group 2</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">module</span> <span style="color:#8be9fd;font-style:italic">rg2</span> <span style="color:#f1fa8c">&#39;rg1.bicep&#39;</span> = {
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">scope</span>: <span style="color:#50fa7b">ResourceGroup</span>(<span style="color:#8be9fd;font-style:italic">resourceGroup2Name</span>) <span style="color:#6272a4">// Use the ResourceGroup function to obtain the reference to the second RG</span>
</span></span><span style="display:flex;"><span>	...
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Outputs</span>
</span></span><span style="display:flex;"><span>...
</span></span></code></pre></div><p>If we choose the Management Group Scope for our deployment, we can conduct deployments in the selected Management Group, each linked Subscription, and furthermore each Resource group under each Subscription.</p>
<h2 id="sibling-scope-hop">Sibling Scope Hop</h2>
<p>Sibling Deployment Scope Hopping is where we choose a scope that will effectively be our starting point in conducting deployments, but from this scope move to deploy into another scope but on the same scope level.</p>
<p>An example would be where we wish to deploy resources into different resource groups. The Subscriptions can be different, all that is required is the deployment principal be provided the relevant rights to the Resource Groups such as <code>Contributor</code>. The benefit of this approach is rights do not need to be supplied to a higher scope of which would be applied to child items.</p>
<p>
  <img src="/images/posts/2023/04/siblingscopehop.png" alt="SiblingScopeHop">

</p>
<p>In the diagram above, we are looking to use the first Resource Group as the starting scope. Each hop will then point to a different Resource Group but in the same scope level. For the second and third hop, we will also need to provide a Subscription Id as we will not be able to reference by Resource Group name only.</p>
<p>The corresponding Bicep for the diagram above would appear as such:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Parameters</span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Name of the Second existing Resource Group&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">resourceGroup2Name</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Subscription Id for the 3rd Resource Group&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">subscription2Id</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Name of the 3rd existing Resource Group&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">resourceGroup3Name</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Subscription Id for the 4th Resource Group&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">subscription3Id</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Name of the 4th existing Resource Group&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">resourceGroup4Name</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Variables</span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Resources</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Deploy Resources into Resource Group 1</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// No Scope applied as we are deploying to the starting scope</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">module</span> <span style="color:#8be9fd;font-style:italic">rg1</span> <span style="color:#f1fa8c">&#39;rg.bicep&#39;</span> = {
</span></span><span style="display:flex;"><span>	...
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Deploy Resources into Resource Group 2</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Only need to specify ResourceGroup function with a name as still under the same subscription</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">module</span> <span style="color:#8be9fd;font-style:italic">rg2</span> <span style="color:#f1fa8c">&#39;rg.bicep&#39;</span> = {
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">scope</span>: <span style="color:#50fa7b">ResourceGroup</span>(<span style="color:#8be9fd;font-style:italic">resourceGroup2Name</span>)
</span></span><span style="display:flex;"><span>	...
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Deploy Resources into Resource Group 3</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Need to specify ResourceGroup function with a name and subscription</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">module</span> <span style="color:#8be9fd;font-style:italic">rg3</span> <span style="color:#f1fa8c">&#39;rg.bicep&#39;</span> = {
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">scope</span>: <span style="color:#50fa7b">ResourceGroup</span>(<span style="color:#8be9fd;font-style:italic">subscription2Id</span>, <span style="color:#8be9fd;font-style:italic">resourceGroup3Name</span>)
</span></span><span style="display:flex;"><span>	...
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Deploy Resources into Resource Group 4</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Need to specify ResourceGroup function with a name and subscription</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">module</span> <span style="color:#8be9fd;font-style:italic">rg4</span> <span style="color:#f1fa8c">&#39;rg.bicep&#39;</span> = {
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">scope</span>: <span style="color:#50fa7b">ResourceGroup</span>(<span style="color:#8be9fd;font-style:italic">subscription3Id</span>, <span style="color:#8be9fd;font-style:italic">resourceGroup4Name</span>)
</span></span><span style="display:flex;"><span>	...
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Outputs</span>
</span></span></code></pre></div><h2 id="mix-and-match">Mix and Match</h2>
<p>Usage of these methods can vary and can also be combined. For instance you could combine both a Parent Child Scope hop with Sibling hops. How you structure your deployments will depend on the architecture and resource dependencies at play.</p>
<p>You may find yourself looking to deploy Key Vault in one Resource Group and a Web App in another. Your Web App might require access to the Key Vault instance and therefore an Access Policy will need deploying. Rather than navigating a potential parent child deployment, why not do a sibling hop?</p>
]]></content:encoded></item><item><title>Bicep | Conditional Iterative Deployment</title><link>https://andrewilson.co.uk/post/2023/04/bicep-conditional-iterative-deployment/</link><pubDate>Tue, 11 Apr 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/04/bicep-conditional-iterative-deployment/</guid><description>Background I have recently been looking at creating multiple of the same resource using Bicep. There is however a condition where I would wish for the set of resources not to be deployed. The following stages show my work through of this particular problem (using a storage account resource as an example):
Conditional Deployment Conditional Deployment is used where you may or may not wish to deploy a given resource depending on the outcome of a given condition (if statement).</description><content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>I have recently been looking at creating multiple of the same resource using Bicep. There is however a condition where I would wish for the set of resources not to be deployed. The following stages show my work through of this particular problem (<em>using a storage account resource as an example</em>):</p>
<h3 id="conditional-deployment">Conditional Deployment</h3>
<p><a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/conditional-resource-deployment">Conditional Deployment</a> is used where you may or may not wish to deploy a given resource depending on the outcome of a given condition (if statement).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#ff79c6">/**********************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Conditional</span> <span style="color:#8be9fd;font-style:italic">Storage</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">***********************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">name</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">deployStorage</span> <span style="color:#8be9fd;font-style:italic">bool</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">storageAccountDeployment</span> <span style="color:#f1fa8c">&#39;Microsoft.Storage/storageAccounts@2021-02-01&#39;</span> = <span style="color:#8be9fd;font-style:italic">if</span> (<span style="color:#8be9fd;font-style:italic">deployStorage</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">name</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">kind</span>: <span style="color:#f1fa8c">&#39;StorageV2&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">sku</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;Premium_LRS&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span></code></pre></div><p>In the instance shown above, if the <code>deployStorage</code> parameter is <code>True</code> then the storageAccountDeployment will be deployed, if <code>False</code> then the storageAccountDeployment will not be deployed. As an ARM template the instance above will appear as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;parameters&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;name&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;string&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;location&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;string&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;deployStorage&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;bool&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;resources&#34;</span>: [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;condition&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;deployStorage&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Microsoft.Storage/storageAccounts&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;apiVersion&#34;</span>: <span style="color:#f1fa8c">&#34;2021-02-01&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;name&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;location&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;location&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;kind&#34;</span>: <span style="color:#f1fa8c">&#34;StorageV2&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;sku&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;Premium_LRS&#34;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="looping-resources">Looping Resources</h3>
<p><a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/loops">Iterative loops</a> in Bicep utilise the <code>For</code> syntax as shown below:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">/**********************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Iterative</span> <span style="color:#8be9fd;font-style:italic">Storage</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">***********************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">storageAccounts</span> <span style="color:#8be9fd;font-style:italic">array</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">storageAccountDeployment</span> <span style="color:#f1fa8c">&#39;Microsoft.Storage/storageAccounts@2021-02-01&#39;</span> = [<span style="color:#8be9fd;font-style:italic">for</span> <span style="color:#8be9fd;font-style:italic">storage</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">storageAccounts</span>: {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">storage</span>.<span style="color:#8be9fd;font-style:italic">name</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">kind</span>: <span style="color:#f1fa8c">&#39;StorageV2&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">sku</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;Premium_LRS&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span></code></pre></div><p>The syntax above will deploy a number of storage accounts based on the number of storage items in the parameter array. In ARM this appears as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;parameters&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;name&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;string&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;location&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;string&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;storageAccounts&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;array&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;resources&#34;</span>: [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;copy&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;storageaccountDeployment&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;count&#34;</span>: <span style="color:#f1fa8c">&#34;[length(parameters(&#39;storageAccounts&#39;))]&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Microsoft.Storage/storageAccounts&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;apiVersion&#34;</span>: <span style="color:#f1fa8c">&#34;2021-02-01&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;name&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;location&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;location&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;kind&#34;</span>: <span style="color:#f1fa8c">&#34;StorageV2&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;sku&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;Premium_LRS&#34;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="conditional-looping-resources">Conditional Looping Resources</h3>
<p><a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/loops">Iterative Conditional Deployments</a> are used in the instance where you would like to create multiple instances of a resource, but on each iteration you would like to check if the resource should be deployed based on a condition. Such as:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">/********************************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Iterative</span> <span style="color:#8be9fd;font-style:italic">Conditional</span> <span style="color:#8be9fd;font-style:italic">Storage</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">*********************************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">storageAccounts</span> <span style="color:#8be9fd;font-style:italic">array</span> = [
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;st1&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">kind</span>: <span style="color:#f1fa8c">&#39;StorageV2&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">storageAccountDeployment</span> <span style="color:#f1fa8c">&#39;Microsoft.Storage/storageAccounts@2021-02-01&#39;</span> = [<span style="color:#8be9fd;font-style:italic">for</span> <span style="color:#8be9fd;font-style:italic">storage</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">storageAccounts</span>: <span style="color:#50fa7b">if</span>(<span style="color:#8be9fd;font-style:italic">storage</span>.<span style="color:#8be9fd;font-style:italic">kind</span> <span style="color:#ff79c6">!=</span> <span style="color:#f1fa8c">&#39;BlobStorage&#39;</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">storage</span>.<span style="color:#8be9fd;font-style:italic">name</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">kind</span>: <span style="color:#8be9fd;font-style:italic">storage</span>.<span style="color:#8be9fd;font-style:italic">kind</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">sku</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;Premium_LRS&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span></code></pre></div><p>The example above will loop through each storage item in the <code>storageAccounts</code> array. At each point of iteration, the conditional is evaluated. In this example only deployment of an item will occur if it is not a <code>'BlobStorage'</code> account type. The ARM for this example appears as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;parameters&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;location&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;string&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;storageAccounts&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;array&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;defaultValue&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;st1&#34;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&#34;kind&#34;</span>: <span style="color:#f1fa8c">&#34;StorageV2&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      ]
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;resources&#34;</span>: [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;copy&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;storageAccountDeployment&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;count&#34;</span>: <span style="color:#f1fa8c">&#34;[length(parameters(&#39;storageAccounts&#39;))]&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;condition&#34;</span>: <span style="color:#f1fa8c">&#34;[not(equals(parameters(&#39;storageAccounts&#39;)[copyIndex()].kind, &#39;BlobStorage&#39;))]&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Microsoft.Storage/storageAccounts&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;apiVersion&#34;</span>: <span style="color:#f1fa8c">&#34;2021-02-01&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;storageAccounts&#39;)[copyIndex()].name]&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;location&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;location&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;kind&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;storageAccounts&#39;)[copyIndex()].kind]&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;sku&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;Premium_LRS&#34;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="problem-space">Problem Space</h3>
<p>The previous case <code>conditionally deploys each resource in the looped array</code>. Although this appears to be what we are looking for, it&rsquo;s not. We would like to <code>conditionally loop through the array</code> which is a step higher than what is provided to us.</p>
<p>At this point, we are not limited by the Bicep language rather that ARM conditionals and loops do not support this behaviour.</p>
<h2 id="solution--conditional-module-with-looping-resource">Solution | Conditional Module with Looping Resource</h2>
<p>To solve this problem, we need to rely on Bicep Modules or in ARM this is nested resource deployments. Essentially we need to split the problem into two. The calling template has the conditional module whilst the nested template (<em>module</em>) handles the looping of storage account creation. For example:</p>
<blockquote>
<p>Calling Template:</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">/********************************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Conditional</span> <span style="color:#8be9fd;font-style:italic">Storage</span> <span style="color:#8be9fd;font-style:italic">Module</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">*********************************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">StorageDeploy</span> <span style="color:#8be9fd;font-style:italic">bool</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">storageAccounts</span> <span style="color:#8be9fd;font-style:italic">array</span> = [
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;st1&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">kind</span>: <span style="color:#f1fa8c">&#39;StorageV2&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">module</span> <span style="color:#8be9fd;font-style:italic">storageDeploy</span> <span style="color:#f1fa8c">&#39;storageDeploy.bicep&#39;</span> = <span style="color:#8be9fd;font-style:italic">if</span> (<span style="color:#8be9fd;font-style:italic">StorageDeploy</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;storageDeploy&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">params</span>:{
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">storageAccounts</span>: <span style="color:#8be9fd;font-style:italic">storageAccounts</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span></code></pre></div><blockquote>
<p>Nested Template (Module)</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">/*********************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Iterative</span> <span style="color:#8be9fd;font-style:italic">Storage</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">**********************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#50fa7b">resourceGroup</span>().<span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">storageAccounts</span> <span style="color:#8be9fd;font-style:italic">array</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">storageAccountDeployment</span> <span style="color:#f1fa8c">&#39;Microsoft.Storage/storageAccounts@2021-02-01&#39;</span> = [<span style="color:#8be9fd;font-style:italic">for</span> <span style="color:#8be9fd;font-style:italic">storage</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">storageAccounts</span>: {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">storage</span>.<span style="color:#8be9fd;font-style:italic">name</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">kind</span>: <span style="color:#8be9fd;font-style:italic">storage</span>.<span style="color:#8be9fd;font-style:italic">kind</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">sku</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;Premium_LRS&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span></code></pre></div><blockquote>
<p>ARM representation</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;parameters&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;location&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;string&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;StorageDeploy&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;bool&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;storageAccounts&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;array&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;defaultValue&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;st1&#34;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&#34;kind&#34;</span>: <span style="color:#f1fa8c">&#34;StorageV2&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      ]
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;resources&#34;</span>: [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;condition&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;StorageDeploy&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Microsoft.Resources/deployments&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;apiVersion&#34;</span>: <span style="color:#f1fa8c">&#34;2022-09-01&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;storageDeploy&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;properties&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;expressionEvaluationOptions&#34;</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&#34;scope&#34;</span>: <span style="color:#f1fa8c">&#34;inner&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;mode&#34;</span>: <span style="color:#f1fa8c">&#34;Incremental&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;parameters&#34;</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&#34;location&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;value&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;location&#39;)]&#34;</span>
</span></span><span style="display:flex;"><span>          },
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&#34;storageAccounts&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;value&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;storageAccounts&#39;)]&#34;</span>
</span></span><span style="display:flex;"><span>          }
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;template&#34;</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#&#34;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&#34;parameters&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;location&#34;</span>: {
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;string&#34;</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">&#34;defaultValue&#34;</span>: <span style="color:#f1fa8c">&#34;[resourceGroup().location]&#34;</span>
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;storageAccounts&#34;</span>: {
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;array&#34;</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>          },
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">&#34;resources&#34;</span>: [
</span></span><span style="display:flex;"><span>            {
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">&#34;copy&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;storageAccountDeployment&#34;</span>,
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;count&#34;</span>: <span style="color:#f1fa8c">&#34;[length(parameters(&#39;storageAccounts&#39;))]&#34;</span>
</span></span><span style="display:flex;"><span>              },
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Microsoft.Storage/storageAccounts&#34;</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">&#34;apiVersion&#34;</span>: <span style="color:#f1fa8c">&#34;2021-02-01&#34;</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;storageAccounts&#39;)[copyIndex()].name]&#34;</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">&#34;location&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;location&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">&#34;kind&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;storageAccounts&#39;)[copyIndex()].kind]&#34;</span>,
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">&#34;sku&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;Premium_LRS&#34;</span>
</span></span><span style="display:flex;"><span>              }
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>          ]
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>We have now constructed a template (or two) which will only conduct looping of the resource deployment if we have said so through our <code>StorageDeploy</code> module conditional parameter.</p>
]]></content:encoded></item><item><title>Bicep Template | Shared Variable File Pattern</title><link>https://andrewilson.co.uk/post/2023/03/bicep-shared-variable-file-pattern/</link><pubDate>Wed, 08 Mar 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/03/bicep-shared-variable-file-pattern/</guid><description>Background I have recently been playing around with some of the Bicep functions when I came across a pattern by Microsoft called the Shared Variable File Pattern.
This nifty pattern describes a method in which you can extract what would either be commonly used or complex configuration values away from your Bicep Template. Using the pattern will allow you to retain easy to read and manageable Bicep templates where you have modelled large variable configurations and or configuration values that are used repeatedly across your templates (prevents multiple copies of the value that would need to be maintained).</description><content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>I have recently been playing around with some of the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions">Bicep functions</a> when I came across a pattern by Microsoft called the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/patterns-shared-variable-file"><strong>Shared Variable File Pattern</strong></a>.</p>
<p>This nifty pattern describes a method in which you can extract what would either be commonly used or complex configuration values away from your Bicep Template. Using the pattern will allow you to retain easy to read and manageable Bicep templates where you have modelled large variable configurations and or configuration values that are used repeatedly across your templates (prevents multiple copies of the value that would need to be maintained).</p>
<h2 id="how-it-works">How it works</h2>
<h3 id="scenario--large-configuration-sets">Scenario | large configuration sets</h3>
<p>Let&rsquo;s assume we are creating an Azure API Management (APIM) resource that is internal (i.e. it can only be accessed within a virtual network, unless it is extended through other resources). Moving forward with our design, we have placed APIM in its own Subnet with a Network Security Group containing many inbound and outbound security rules. There is a minimum set of <a href="https://learn.microsoft.com/en-us/azure/api-management/api-management-using-with-internal-vnet?tabs=stv2#configure-nsg-rules">security rules</a> that are required to ensure proper operation and access to our APIM instance.</p>
<p>Previously the method of modelling these in Bicep would be to add the configuration to a variable or parameter input. The configuration would look as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>	[
</span></span><span style="display:flex;"><span>      <span style="color:#6272a4">// Inbound</span>
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;AllowManagementEndpoint&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">description</span>: <span style="color:#f1fa8c">&#39;Management endpoint for Azure portal and PowerShell&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">sourceAddressPrefix</span>: <span style="color:#f1fa8c">&#39;ApiManagement&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">sourcePortRange</span>: <span style="color:#f1fa8c">&#39;*&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">destinationAddressPrefix</span>: <span style="color:#f1fa8c">&#39;VirtualNetwork&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">destinationPortRange</span>: <span style="color:#f1fa8c">&#39;3443&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">protocol</span>: <span style="color:#f1fa8c">&#39;Tcp&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">access</span>: <span style="color:#f1fa8c">&#39;Allow&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">priority</span>: <span style="color:#8be9fd;font-style:italic">100</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">direction</span>: <span style="color:#f1fa8c">&#39;Inbound&#39;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;AllowAzureInfrastructureLoadBalancer&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">description</span>: <span style="color:#f1fa8c">&#39;Azure Infrastructure Load Balancer&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">sourceAddressPrefix</span>: <span style="color:#f1fa8c">&#39;AzureLoadBalancer&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">sourcePortRange</span>: <span style="color:#f1fa8c">&#39;*&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">destinationAddressPrefix</span>: <span style="color:#f1fa8c">&#39;VirtualNetwork&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">destinationPortRange</span>: <span style="color:#f1fa8c">&#39;6390&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">protocol</span>: <span style="color:#f1fa8c">&#39;Tcp&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">access</span>: <span style="color:#f1fa8c">&#39;Allow&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">priority</span>: <span style="color:#8be9fd;font-style:italic">110</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">direction</span>: <span style="color:#f1fa8c">&#39;Inbound&#39;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#6272a4">// Outbound</span>
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;AllowDependencyOnAzureStorage&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">description</span>: <span style="color:#f1fa8c">&#39;Dependency on Azure Storage&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">sourceAddressPrefix</span>: <span style="color:#f1fa8c">&#39;VirtualNetwork&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">sourcePortRange</span>: <span style="color:#f1fa8c">&#39;*&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">destinationAddressPrefix</span>: <span style="color:#f1fa8c">&#39;Storage&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">destinationPortRange</span>: <span style="color:#f1fa8c">&#39;443&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">protocol</span>: <span style="color:#f1fa8c">&#39;Tcp&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">access</span>: <span style="color:#f1fa8c">&#39;Allow&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">priority</span>: <span style="color:#8be9fd;font-style:italic">120</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">direction</span>: <span style="color:#f1fa8c">&#39;Outbound&#39;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;AllowAccessToAzureSQLEndpoints&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">description</span>: <span style="color:#f1fa8c">&#39;Access to Azure SQL endpoints&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">sourceAddressPrefix</span>: <span style="color:#f1fa8c">&#39;VirtualNetwork&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">sourcePortRange</span>: <span style="color:#f1fa8c">&#39;*&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">destinationAddressPrefix</span>: <span style="color:#f1fa8c">&#39;Sql&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">destinationPortRange</span>: <span style="color:#f1fa8c">&#39;1433&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">protocol</span>: <span style="color:#f1fa8c">&#39;Tcp&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">access</span>: <span style="color:#f1fa8c">&#39;Allow&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">priority</span>: <span style="color:#8be9fd;font-style:italic">130</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">direction</span>: <span style="color:#f1fa8c">&#39;Outbound&#39;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;AllowAccessToAzureKeyVault&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">description</span>: <span style="color:#f1fa8c">&#39;Access to Azure Key Vault&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">sourceAddressPrefix</span>: <span style="color:#f1fa8c">&#39;VirtualNetwork&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">sourcePortRange</span>: <span style="color:#f1fa8c">&#39;*&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">destinationAddressPrefix</span>: <span style="color:#f1fa8c">&#39;AzureKeyVault&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">destinationPortRange</span>: <span style="color:#f1fa8c">&#39;443&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">protocol</span>: <span style="color:#f1fa8c">&#39;Tcp&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">access</span>: <span style="color:#f1fa8c">&#39;Allow&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">priority</span>: <span style="color:#8be9fd;font-style:italic">140</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">direction</span>: <span style="color:#f1fa8c">&#39;Outbound&#39;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    ]
</span></span></code></pre></div><p>As can be seen in the example above, the values are static, the size of the configuration is sizeable and detracts away from the functioning parts of the Bicep template.</p>
<h3 id="solution">Solution</h3>
<p>Using the Shared Variable File Pattern, let&rsquo;s model the NSG rules in a Json file outside of our Bicep template.</p>
<p>Now that the configuration is outside of the Bicep template, the template already appears to be easier to maintain. However, we still need access to our configuration. To do this we are going to load the configuration into a variable in our Bicep template using the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-files#loadjsoncontent">loadJsonContent Function</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">nsgRules</span> = <span style="color:#50fa7b">loadJsonContent</span>(<span style="color:#f1fa8c">&#39;./nsgConfiguration.json&#39;</span>)
</span></span></code></pre></div><p>If like me, you are using VS Code to create your Bicep templates, you will immediately notice that when you attempt to use your nsgRules variable there is <code>intellisense when traversing the Json Object</code>.</p>
<p>You can now easily assign your nsgRules to your Network Security Group, or conduct further work on the configuration before use, such as concatenating your minimum nsg rules with some custom ones that are dynamically created in the Bicep Template.</p>
<h3 id="multiple-configuration-components-in-a-single-file">Multiple configuration components in a single file</h3>
<p>If you are looking to keep a single configuration json file for your Bicep templates, but you do not want to keep loading the entire configuration into each template that requires it <code>(there are template size limitations)</code>. There is a solution to this. The <code>loadJsonContent</code> function allows you to specify a jsonPath as the second parameter. This allows you to only load in parts of the configuration file that are applicable to the given template and its resource deployments. For example:</p>
<blockquote>
<p>Configuration file <em>apimConfiguration.json</em></p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Json" data-lang="Json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>	<span style="color:#ff79c6">&#34;ApimConfig&#34;</span>:{
</span></span><span style="display:flex;"><span>		...
</span></span><span style="display:flex;"><span>	},
</span></span><span style="display:flex;"><span>	<span style="color:#ff79c6">&#34;securityRules&#34;</span>: [
</span></span><span style="display:flex;"><span>		...
</span></span><span style="display:flex;"><span>	]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>Bicep template loading only securityRules</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">nsgRules</span> = <span style="color:#50fa7b">loadJsonContent</span>(<span style="color:#f1fa8c">&#39;./apimConfiguration.json&#39;</span>, <span style="color:#f1fa8c">&#39;securityRules&#39;</span>)
</span></span></code></pre></div><blockquote>
<p><strong>Note</strong></p>
<p>I would recommend that you create multiple Json Shared Variable Files, each defined for logical aspects of your deployment. As mentioned at the start, the reason for doing any of this is to provide a manageable deployment solution. One file that contains all aspects to your deployment is not the dream.</p>
</blockquote>
<h2 id="things-to-consider">Things to consider</h2>
<p>As mentioned in the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/patterns-shared-variable-file#considerations">Microsoft Documentation</a> there are the following considerations:</p>
<ul>
<li>
<p>When you use this approach, the loaded JSON content will be included inside the ARM template generated by Bicep. The JSON <code>ARM templates generated by Bicep have a file limit of 4MB</code></p>
<ul>
<li>Either avoid using large shared variable files or only load in aspect that are required per template. If you are still hitting limits, you might need to consider splitting your deployment into multiple parts.</li>
</ul>
</li>
<li>
<p>Ensure your shared variable arrays don&rsquo;t conflict with the array values specified in each Bicep file. For example, when using the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/patterns-configuration-set">configuration set pattern</a> to define network security groups, ensure you don&rsquo;t have multiple rules that define the same priority and direction.</p>
</li>
<li>
<p>If you have various aspects of configuration for your deployment in a single Json file, look to separate these into different Json files each respective to the resource deployments that they will be used for. Such as:</p>
<ul>
<li>commonConfig.json | configuration used commonly throughout your deployment templates.</li>
<li>apimConfig.json | configuration specificly used when deploying APIM resources.</li>
</ul>
</li>
</ul>
]]></content:encoded></item><item><title>Automating Deployment of Azure Consumption Logic Apps | Bicep and ARM</title><link>https://andrewilson.co.uk/post/2023/02/automate-deployment-of-azure-consumption-logic-apps/</link><pubDate>Fri, 24 Feb 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/02/automate-deployment-of-azure-consumption-logic-apps/</guid><description>Azure Logic Apps Azure Logic Apps is an Azure Integration Service (AIS) that provides you the ability to create and run automated workflows with little to no code. Consumption Logic Apps are developed using the visual designer within the Azure Portal.
If you are new to developing Azure Logic Apps, there is great Microsoft Learning material to get you started:
What are Azure Logic Apps | https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-overview Introduction to Azure Logic Apps | https://learn.</description><content:encoded><![CDATA[<h2 id="azure-logic-apps">Azure Logic Apps</h2>
<p>Azure Logic Apps is an Azure Integration Service (AIS) that provides you the ability to create and run automated workflows with little to no code. Consumption Logic Apps are developed using the visual designer within the Azure Portal.</p>
<p>If you are new to developing Azure Logic Apps, there is great Microsoft Learning material to get you started:</p>
<ul>
<li>What are Azure Logic Apps | <a href="https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-overview">https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-overview</a></li>
<li>Introduction to Azure Logic Apps | <a href="https://learn.microsoft.com/en-us/training/modules/intro-to-logic-apps/">https://learn.microsoft.com/en-us/training/modules/intro-to-logic-apps/</a></li>
<li>Quickstart: Create an integration workflow | <a href="https://learn.microsoft.com/en-us/azure/logic-apps/quickstart-create-first-logic-app-workflow">https://learn.microsoft.com/en-us/azure/logic-apps/quickstart-create-first-logic-app-workflow</a></li>
</ul>
<h2 id="problem-space">Problem Space</h2>
<p>Once you have created your workflow in the Logic App designer, your next question may be:</p>
<blockquote>
<p>How do I consistently deploy the Logic App with my developed workflow?</p>
</blockquote>
<p>We are going to be looking at conducting our deployment with ARM and Bicep templates.</p>
<p>ARM templates are a declarative way of defining Azure service configurations in code. When deploying services to Azure using ARM templates that are under source control and therefore in step with product development, are by far the best approach. The Azure services can be defined in a single template and that template can be used to deploy multiple copies of the system, either for dev/test purposes or to provision live instances for multiple customers in a reliable and predictable manner.</p>
<p>Bicep<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> is a transparent abstraction over ARM template JSON and doesn&rsquo;t lose any of the JSON template capabilities. Bicep templates will be compiled into ARM templates.</p>
<h3 id="arm-template-deployments">ARM Template Deployments</h3>
<p>Following the Microsoft guide<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> for both building an Azure Logic App using ARM templates and including the Logic Apps definition, you will likely end up with the following development process:</p>
<ol>
<li>Develop and Deploy a Basic Logic App ARM template:</li>
</ol>
<p><code>Remember to add the template to your Source Control</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Json" data-lang="Json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;parameters&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;logicAppName&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;String&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;location&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;String&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;variables&#34;</span>: {},
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;resources&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Microsoft.Logic/workflows&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;apiVersion&#34;</span>: <span style="color:#f1fa8c">&#34;2019-05-01&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;logicAppName&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;location&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;location&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;properties&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;definition&#34;</span>: {},
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;parameters&#34;</span>: {}
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><ol start="2">
<li>Develop a workflow in the deployed Logic App Portal Designer:

  <img src="/images/posts/2023/02/LogicAppPortalDesigner.png" alt="Logic App Portal Designer">

</li>
<li>Obtain the workflow definition from <code>Code view</code>:

  <img src="/images/posts/2023/02/LogicAppCodeView.png" alt="Logic App Portal Designer">

</li>
<li>Copy the workflow definition from the Portal into your Logic App ARM Template:</li>
</ol>
<p><code>Make sure to parametrise fields within your workflow using the ARM Parameter syntax</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Json" data-lang="Json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;parameters&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;logicAppName&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;String&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;location&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;String&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;interval&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;int&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;variables&#34;</span>: {},
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;resources&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Microsoft.Logic/workflows&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;apiVersion&#34;</span>: <span style="color:#f1fa8c">&#34;2019-05-01&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;logicAppName&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;location&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;location&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;properties&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;definition&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#&#34;</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;actions&#34;</span>: {
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;Condition_-_Morning_or_Afternoon&#34;</span>: {
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;actions&#34;</span>: {
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;Terminate_-_Afternoon&#34;</span>: {
</span></span><span style="display:flex;"><span>                                    <span style="color:#ff79c6">&#34;inputs&#34;</span>: {
</span></span><span style="display:flex;"><span>                                        <span style="color:#ff79c6">&#34;runStatus&#34;</span>: <span style="color:#f1fa8c">&#34;Succeeded&#34;</span>
</span></span><span style="display:flex;"><span>                                    },
</span></span><span style="display:flex;"><span>                                    <span style="color:#ff79c6">&#34;runAfter&#34;</span>: {},
</span></span><span style="display:flex;"><span>                                    <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Terminate&#34;</span>
</span></span><span style="display:flex;"><span>                                }
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;else&#34;</span>: {
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;actions&#34;</span>: {
</span></span><span style="display:flex;"><span>                                    <span style="color:#ff79c6">&#34;Terminate_-_Morning&#34;</span>: {
</span></span><span style="display:flex;"><span>                                        <span style="color:#ff79c6">&#34;inputs&#34;</span>: {
</span></span><span style="display:flex;"><span>                                            <span style="color:#ff79c6">&#34;runStatus&#34;</span>: <span style="color:#f1fa8c">&#34;Succeeded&#34;</span>
</span></span><span style="display:flex;"><span>                                        },
</span></span><span style="display:flex;"><span>                                        <span style="color:#ff79c6">&#34;runAfter&#34;</span>: {},
</span></span><span style="display:flex;"><span>                                        <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Terminate&#34;</span>
</span></span><span style="display:flex;"><span>                                    }
</span></span><span style="display:flex;"><span>                                }
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;expression&#34;</span>: {
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;and&#34;</span>: [
</span></span><span style="display:flex;"><span>                                    {
</span></span><span style="display:flex;"><span>                                        <span style="color:#ff79c6">&#34;greaterOrEquals&#34;</span>: [
</span></span><span style="display:flex;"><span>                                            <span style="color:#f1fa8c">&#34;@utcNow(&#39;H:mm:ss&#39;)&#34;</span>,
</span></span><span style="display:flex;"><span>                                            <span style="color:#f1fa8c">&#34;12:00:00&#34;</span>
</span></span><span style="display:flex;"><span>                                        ]
</span></span><span style="display:flex;"><span>                                    }
</span></span><span style="display:flex;"><span>                                ]
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;runAfter&#34;</span>: {},
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;If&#34;</span>
</span></span><span style="display:flex;"><span>                        }
</span></span><span style="display:flex;"><span>                    },
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;outputs&#34;</span>: {},
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;parameters&#34;</span>: {},
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;triggers&#34;</span>: {
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;Recurrence_-_Start&#34;</span>: {
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;evaluatedRecurrence&#34;</span>: {
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;frequency&#34;</span>: <span style="color:#f1fa8c">&#34;Day&#34;</span>,
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;interval&#34;</span>: <span style="color:#bd93f9">1</span>,
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;schedule&#34;</span>: {
</span></span><span style="display:flex;"><span>                                    <span style="color:#ff79c6">&#34;hours&#34;</span>: [
</span></span><span style="display:flex;"><span>                                        <span style="color:#f1fa8c">&#34;11&#34;</span>
</span></span><span style="display:flex;"><span>                                    ]
</span></span><span style="display:flex;"><span>                                }
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;recurrence&#34;</span>: {
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;frequency&#34;</span>: <span style="color:#f1fa8c">&#34;Hour&#34;</span>,
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;interval&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;interval&#39;)]&#34;</span>
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Recurrence&#34;</span>
</span></span><span style="display:flex;"><span>                        }
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                },
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;parameters&#34;</span>: {}
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><ol start="5">
<li>Any changes to the workflow in the Azure Portal Designer are to be reflected in your ARM Template that is in Source Control. You can use source control change tooling to see the diff between current and latest, this can help you retain parameterized values that you have previously included.</li>
</ol>
<h3 id="bicep-template-deployment">Bicep Template Deployment</h3>
<p>Generating Logic App ARM Templates from Bicep is possible but has a different approach. Bicep is not written in Json as ARM is, and therefore you cannot simply copy the Json workflow definition into the Logic App deployment resource.</p>
<p>Following the example given by Microsoft<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, their method is to write out the definition using the Bicep syntax and replace property values with Bicep properties as you would with ARM. This approach however does not adapt well to change, nor is it an easy task to bring the workflow definition across from the portal. Having to convert the workflow to Bicep syntax every time is simply a pain.</p>
<p>That said, hope is not lost.</p>
<p>Bicep has some built in functions that will allow us to retain a similar development process, such that:</p>
<p>We will still run through <code>steps 1, 2, and 3</code> as we have done for ARM, but this time with Bicep.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#ff79c6">/************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Logic</span> <span style="color:#8be9fd;font-style:italic">App</span> <span style="color:#8be9fd;font-style:italic">Template</span> <span style="color:#8be9fd;font-style:italic">Bicep</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">**************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Logic App Name&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">logicAppName</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The location that the resource will be deployed to&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">logicAppDeployment</span> <span style="color:#f1fa8c">&#39;Microsoft.Logic/workflows@2022-10-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">logicAppName</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">definition</span>: {}
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">parameters</span>: {}
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">state</span>: <span style="color:#f1fa8c">&#39;Enabled&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">output</span> <span style="color:#8be9fd;font-style:italic">LogicAppName</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#8be9fd;font-style:italic">logicAppName</span>
</span></span></code></pre></div><ol start="4">
<li>
<p>Rather than copying the workflow definition into the Bicep template, copy the definition into a <code>new Json file</code> in your source control repository.</p>
</li>
<li>
<p>As you do not have access to your Bicep Parameters, any values that need parametrising will need to be replaced with tokens that will be replaced by the Bicep Template. For instance, **interval**.</p>
</li>
<li>
<p>Next we will load our workflow definition into the template, conduct token replacement with Parameter values, and then assign the definition and parameters to the Logic App Resource.</p>
<p>The Bicep Functions that we will be using to conduct these activities are:</p>
<ul>
<li><strong>loadTextContent</strong> | Loads the content of the specified file as a string. | <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-files#loadtextcontent">https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-files#loadtextcontent</a></li>
<li><strong>replace</strong> | Returns a new string with all instances of one string replaced by another string. | <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-string#replace">https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-string#replace</a></li>
<li><strong>json</strong> | Converts a valid JSON string into a JSON data type. | <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-object#json">https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-object#json</a></li>
</ul>
<p>See the Example Below:</p>
</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#ff79c6">/************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Logic</span> <span style="color:#8be9fd;font-style:italic">App</span> <span style="color:#8be9fd;font-style:italic">Template</span> <span style="color:#8be9fd;font-style:italic">Bicep</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">**************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Logic App Name&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">logicAppName</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The location that the resource will be deployed to&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Trigger Interval&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">interval</span> <span style="color:#8be9fd;font-style:italic">int</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">logicAppDefinition</span> = <span style="color:#50fa7b">loadTextContent</span>(<span style="color:#f1fa8c">&#39;./Definition.json&#39;</span>) <span style="color:#6272a4">// Load our definition into a string variable</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">logicAppReplacementParameter</span> = <span style="color:#50fa7b">replace</span>(<span style="color:#8be9fd;font-style:italic">logicAppDefinition</span>, <span style="color:#f1fa8c">&#39;**interval**&#39;</span>, <span style="color:#8be9fd;font-style:italic">interval</span>) <span style="color:#6272a4">// Replace tokens with our parameters</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">logicAppDefinitionJson</span> = <span style="color:#50fa7b">json</span>(<span style="color:#8be9fd;font-style:italic">logicAppReplacementParameter</span>) <span style="color:#6272a4">// Retrieve the Json object from the Json String so we can access specific data when assigning</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">logicAppDeployment</span> <span style="color:#f1fa8c">&#39;Microsoft.Logic/workflows@2022-10-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">logicAppName</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">definition</span>: <span style="color:#8be9fd;font-style:italic">logicAppDefinitionJson</span>.<span style="color:#8be9fd;font-style:italic">definition</span> <span style="color:#6272a4">// Set the definition</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">parameters</span>: <span style="color:#8be9fd;font-style:italic">logicAppDefinitionJson</span>.<span style="color:#8be9fd;font-style:italic">parameters</span> <span style="color:#6272a4">// Set any Properties that may be set</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">output</span> <span style="color:#8be9fd;font-style:italic">LogicAppName</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#8be9fd;font-style:italic">logicAppName</span>
</span></span></code></pre></div><ol start="7">
<li>As with the ARM templates, any changes to the workflow in the Azure Portal Designer are to be reflected in your Json Workflow file that is in Source Control. You can use source control change tooling to see the diff between current and latest, this can help you retain tokenised values that you have previously included.</li>
</ol>
<h2 id="take-note-when">Take Note When</h2>
<ul>
<li>Special point of care should be taken if your logic app is using xpath expressions, proper concatenation/interpolation of the quotes can be a nightmare.</li>
<li>Bicep loadTextContent has size limits: The maximum allowed size of the file is 96 Kb.
<ul>
<li>If you are meeting these size limits for your workflows, this may be an indication that you need to split your workflow up.</li>
</ul>
</li>
</ul>
<h2 id="summary">Summary</h2>
<p>Whether you choose to build your resource deployment templates with ARM or Bicep, both options as shown will provide you a development process where you can keep your low code consumption Logic App not only in source control, but have a reliable deployment template that will allow you to create your Logic App when and where ever you wish.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>What is Bicep | <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep">https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Automate deployment for Azure Logic Apps by using <strong>ARM Templates</strong> | <a href="https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-azure-resource-manager-templates-overview">https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-azure-resource-manager-templates-overview</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Automate deployment for Azure Logic Apps by using <strong>Bicep Templates</strong> | <a href="https://learn.microsoft.com/en-us/azure/logic-apps/quickstart-create-deploy-bicep?tabs=CLI">https://learn.microsoft.com/en-us/azure/logic-apps/quickstart-create-deploy-bicep?tabs=CLI</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Bicep | File Reference in a Git Repository Containing Spaces</title><link>https://andrewilson.co.uk/post/2023/01/bicep-git-repository-spaces/</link><pubDate>Fri, 06 Jan 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/01/bicep-git-repository-spaces/</guid><description>Problem Space I recently started working on a Git Repository (not of my own creation) that had a repository name containing spaces.
As a helping hand when a repository is created with spaces in the name, the spaces are replaced with %20 when cloned &amp;hellip; Unless otherwise specified, however, this would then need to be specified whenever another user/pipeline clones the repo. See the git command below:
git clone https://.../.../my%20repo myrepo My best practice answer would be, don&amp;rsquo;t create repositories with names containing spaces, but this one already did.</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>I recently started working on a Git Repository (<em>not of my own creation</em>) that had a repository name containing spaces.</p>
<p>As a helping hand when a repository is created with spaces in the name, the spaces are replaced with %20 when cloned &hellip; Unless otherwise specified, however, this would then need to be specified whenever another user/pipeline clones the repo. See the git command below:</p>
<pre tabindex="0"><code>git clone https://.../.../my%20repo myrepo
</code></pre><p>My best practice answer would be, <strong>don&rsquo;t create repositories with names containing spaces</strong>, but this one already did.</p>
<p>So what was the problem?</p>
<p>Lets say I have a repository called &lsquo;demo repo&rsquo;, that would in tern be cloned into a folder called &lsquo;demo%20repo&rsquo;. In this repository I have some Bicep templates, of which most importantly I have my main Azure Deploy template that orchestrates the others through modules.</p>
<p>Here is what that template looks like:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#ff79c6">/**********************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: <span style="color:#8be9fd;font-style:italic">Demo</span> <span style="color:#8be9fd;font-style:italic">Azure</span> <span style="color:#8be9fd;font-style:italic">Deploy</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">***********************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">module</span> <span style="color:#8be9fd;font-style:italic">moduleDeployment</span> <span style="color:#f1fa8c">&#39;storage-account.bicep&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;moduleDeployment&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">params</span>: {
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span></code></pre></div><p>When you open this file in VS Code with the <a href="https://learn.microsoft.com/en-gb/azure/azure-resource-manager/bicep/install#vs-code-and-bicep-extension">Bicep Extension installed</a> <code>v0.13.1</code>, you&rsquo;ll shortly notice that an error has appeared on the module path:</p>
<p>
  <img src="/images/posts/2023/01/git-repo-spaces-bicep-error.PNG" alt="error">

</p>
<pre><code>An error occurred reading file. Could not find a part of the path 'c:\src\demo repo\Bicep-Templates\storage-account.bicep'.
</code></pre>
<p>As you&rsquo;ll find, when the Bicep extension checks your file for validity, it translates the %20 into actual spaces as seen in the error message <em>&rsquo;<strong>\demo repo\</strong>&rsquo;</em>.</p>
<p><strong>This will also prevent your templates from being built into ARM Templates.</strong> (<code>This is not the case for Bicep CLI and Azure CLI</code>)</p>
<p>It is also not possible to update the path on the module to contain the %20 as this is considered special characters and will throw more errors if attempted.</p>
<h2 id="summary">Summary</h2>
<p>In my situation, the repository was relatively new in creation and little to no refactoring would be required if the name was to be changed. So we removed the spaces out of the repository name and all is well.</p>
<p>The following GitHub issue has been raised: <a href="https://github.com/Azure/bicep/issues/9466">Error in File Reference in a Git Repository Containing Spaces</a></p>
]]></content:encoded></item><item><title>Azure API Management | Subscription Contract Names</title><link>https://andrewilson.co.uk/post/2022/12/apim-subscription-contract-name/</link><pubDate>Tue, 20 Dec 2022 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2022/12/apim-subscription-contract-name/</guid><description>Problem Space Subscriptions are a nice and easy method of securing your APIs in APIM, however as I bumped into a small detail around their use the other day, I thought it wise to note it down.
When a Subscription Key is required on an API, as an invoker I will need to provide either a Header or a Parameter to my request which will contain the Subscription Key.
By default:</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>Subscriptions are a nice and easy method of securing your APIs in APIM, however as I bumped into a small detail around their use the other day, I thought it wise to note it down.</p>
<p>When a Subscription Key is required on an API, as an invoker I will need to provide either a Header or a Parameter to my request which will contain the Subscription Key.</p>
<p>By default:</p>
<ul>
<li>The <strong>Header</strong> is as follows:
<ul>
<li><strong>Ocp-Apim-Subscription-Key</strong>: SubscriptionKey</li>
</ul>
</li>
<li>The Query Parameter is as follows:
<ul>
<li>?<strong>subscription-key</strong>=SubscriptionKey</li>
</ul>
</li>
</ul>
<p>This being said, Azure does also allow you to override these Header and Query Parameter names.</p>
<h3 id="azure-portal">Azure Portal</h3>
<p>To view or change these Subscription Contract Names in the <a href="https://portal.azure.com/">Azure Portal</a>, <em>navigate through to your APIM instance - APIs - Select the API you wish to view/change - Select Settings</em>.</p>
<p>As can be seen by the image below, the header and query parameter name is shown and editable under the Subscription Header in the Portal.</p>
<p>
  <img src="/images/posts/2022/12/SubscriptionContractNamesPortal.png" alt="SubscriptionContractNamesPortal">

</p>
<h3 id="iac--bicep">IaC | Bicep</h3>
<p>When defining your API Management Service APIs in Bicep, there is a property called <strong>subscriptionKeyParameterNames</strong> which defines the <a href="https://learn.microsoft.com/en-us/azure/templates/microsoft.apimanagement/service/apis?pivots=deployment-language-bicep#subscriptionkeyparameternamescontract">contract names for your Subscription Header and Query Parameter</a></p>
<p>The Bicep Template will look similar to the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bicep" data-lang="bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">symbolicname</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis@2021-08-01&#39;</span> = {
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;string&#39;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">resourceSymbolicName</span>
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>		...
</span></span><span style="display:flex;"><span>		<span style="color:#8be9fd;font-style:italic">subscriptionRequired</span> = <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>		<span style="color:#8be9fd;font-style:italic">subscriptionKeyParameterNames</span>: {
</span></span><span style="display:flex;"><span>			<span style="color:#8be9fd;font-style:italic">header</span>: <span style="color:#f1fa8c">&#39;mySubscriptionHeaderName&#39;</span>
</span></span><span style="display:flex;"><span>			<span style="color:#8be9fd;font-style:italic">query</span>: <span style="color:#f1fa8c">&#39;myQueryParameterSubscriptionName&#39;</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		...
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content:encoded></item><item><title>Bicep File Template | VS Code Snippet</title><link>https://andrewilson.co.uk/post/2022/11/bicep-template-snippet/</link><pubDate>Wed, 16 Nov 2022 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2022/11/bicep-template-snippet/</guid><description>Problem Space: After developing native ARM templates for a year or two within a set structure, I have found myself applying the same structure to my Bicep templates using comments. This structure however is not setup by default, and in actuality, the sequencing of your bicep components doesn&amp;rsquo;t really matter as long as your dependencies are there.
Of course we typically follow the standard pattern as shown below, but the larger the template the harder it is to see the breaks between.</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space:</h2>
<p>After developing native ARM templates for a year or two within a set structure, I have found myself applying the same structure to my Bicep templates using comments. This structure however is not setup by default, and in actuality, the sequencing of your bicep components doesn&rsquo;t really matter as long as your dependencies are there.</p>
<p>Of course we typically follow the standard pattern as shown below, but the larger the template the harder it is to see the breaks between.</p>
<ol>
<li>Header Details</li>
<li>Parameters</li>
<li>Variables</li>
<li>Resources</li>
<li>Outputs</li>
</ol>
<h2 id="my-structuring">My Structuring</h2>
<p>As I have mentioned above, I have made section breaks to provide the desired structure. This can be seen in the example below:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#ff79c6">/**********************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Bicep</span> <span style="color:#8be9fd;font-style:italic">Template</span>: {<span style="color:#8be9fd;font-style:italic">DeploymentName</span>}
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Company</span>: {<span style="color:#8be9fd;font-style:italic">Company</span>}
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">***********************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span></code></pre></div><p>This being said, either writing this out each time, or finding a previous example to copy is not the best experience nor is it productive.</p>
<h2 id="vs-code-snippets">VS Code Snippets</h2>
<p>After some looking around I decided to make use of VS Code Snippets <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> . Reasoning being, that I can setup a specific snippet for a language (<em>Bicep</em>) and either invoke it through intellisense or through a keyboard shortcut.</p>
<p>The Snippet that I setup is as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>	<span style="color:#ff79c6">&#34;new bicep file&#34;</span>: {
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">&#34;isFileTemplate&#34;</span>: <span style="color:#ff79c6">true</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">&#34;prefix&#34;</span>: <span style="color:#f1fa8c">&#34;bicepTemplate&#34;</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">&#34;body&#34;</span>: [
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;/**********************************&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;Bicep Template: {DeploymentName}&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;Company: {Company}&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;***********************************/&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;targetScope = &#39;resourceGroup&#39;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;// ** Parameters **&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;// ****************&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;// ** Variables **&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;// ***************&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;// ** Resources **&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;// ***************&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;// ** Outputs **&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;// *************&#34;</span>,
</span></span><span style="display:flex;"><span>			<span style="color:#f1fa8c">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>		]
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="intellisense">Intellisense</h3>
<p>
  <img src="/images/posts/2022/11/Intelli.PNG" alt="Intellisense">

</p>
<h3 id="result">Result</h3>
<p>
  <img src="/images/posts/2022/11/Result.PNG" alt="Intellisense">

</p>
<h2 id="over-to-you">Over to You</h2>
<p>To either setup your own snippets, or see if one already exists have a look at the following:</p>
<ul>
<li><strong>Create your own</strong> |  <a href="https://code.visualstudio.com/docs/editor/userdefinedsnippets#_create-your-own-snippets">https://code.visualstudio.com/docs/editor/userdefinedsnippets#_create-your-own-snippets</a></li>
<li><strong>Install snippets from the Marketplace</strong> | <a href="https://code.visualstudio.com/docs/editor/userdefinedsnippets#_install-snippets-from-the-marketplace">https://code.visualstudio.com/docs/editor/userdefinedsnippets#_install-snippets-from-the-marketplace</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Snippets in Visual Studio Code | <a href="https://code.visualstudio.com/docs/editor/userdefinedsnippets">https://code.visualstudio.com/docs/editor/userdefinedsnippets</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Azure API Management | API Mocking</title><link>https://andrewilson.co.uk/post/2022/09/apim-api-mocking/</link><pubDate>Sun, 18 Sep 2022 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2022/09/apim-api-mocking/</guid><description>Problem Space: I have been recently looking into setting up some APIs within API Management. I do not currently have any backing services to hook the API&amp;rsquo;s to and I would like to decouple development of the front end systems from the backend. Thankfully Azure API Management has provided the ability to create mocks for your API&amp;rsquo;s. In this post I will be walking through API mocking and how to achieve this within Bicep Templates for deployment.</description><content:encoded><![CDATA[<h3 id="problem-space">Problem Space:</h3>
<p>I have been recently looking into setting up some APIs within API Management. I do not currently have any backing services to hook the API&rsquo;s to and I would like to decouple development of the front end systems from the backend. Thankfully Azure API Management has provided the ability to create mocks for your API&rsquo;s. In this post I will be walking through API mocking and how to achieve this within Bicep Templates for deployment.</p>
<h3 id="azure-api-management-mocking">Azure API Management mocking</h3>
<p><a href="https://docs.microsoft.com/en-us/azure/api-management/mock-api-responses?tabs=azure-portal">Mocking APIs</a> within APIM has been made super simple and configurable. The mocks that you setup will be split into two parts:</p>
<ol>
<li>Mock sample data and return type.</li>
<li>Mock enablement.</li>
</ol>
<p>The split has been made so that you can have multiple mock samples configured and then enable the ones that you wish to run. We will look into each component separately.</p>
<h3 id="setting-up-mock-sample-data">Setting up mock sample data</h3>
<p>APIM will allow you to setup multiple mock responses but only one per response status code as the mocks samples are identified by their response status codes.</p>
<p>Currently, if you setup multiple samples, only the first sample will only ever be returned.</p>
<p>Mock Samples can be setup for each API within the <code>Operation frontend</code>. The Mock responses and samples can be found under <code>Responses</code>.</p>
<h3 id="enabling-api-mocks">Enabling API mocks</h3>
<p>Mocks are enabled within the <a href="https://docs.microsoft.com/en-us/azure/api-management/api-management-advanced-policies#mock-response">APIM policy</a> blocks:</p>
<ul>
<li>Policy sections: inbound, outbound, on-error</li>
<li>Policy scopes: all scopes
<ul>
<li>Global - All APIs</li>
<li>Product</li>
<li>API - All Operations</li>
<li>API - Individual Operation</li>
</ul>
</li>
</ul>
<h4 id="policy-block-sample">Policy Block Sample</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#ff79c6">&lt;policies&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;inbound&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;mock-response</span> <span style="color:#50fa7b">status-code=</span><span style="color:#f1fa8c">&#34;200&#34;</span> <span style="color:#50fa7b">content-type=</span><span style="color:#f1fa8c">&#34;application/json&#34;</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/inbound&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;backend&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/backend&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;outbound&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/outbound&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;on-error&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;base</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/on-error&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">&lt;/policies&gt;</span>
</span></span></code></pre></div><h3 id="bicep-mock-sample-and-enablement">Bicep Mock Sample and Enablement</h3>
<p>The type of mocking that I wish to be setting up is enabled on the API operation scope and applied to the inbound policy section as shown above.
The Bicep template for this would appear as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Configure the API Operation with the mock response sample</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">mockOperation</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis/operations@2021-08-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;apiOperationName&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">apimService</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">method</span>: <span style="color:#f1fa8c">&#39;GET&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">urlTemplate</span>: <span style="color:#f1fa8c">&#39;/mockOperation&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">responses</span>: [
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">statusCode</span>: <span style="color:#8be9fd;font-style:italic">200</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">description</span>: <span style="color:#f1fa8c">&#39;Mock Request&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">representations</span>: [
</span></span><span style="display:flex;"><span>          {
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd;font-style:italic">contentType</span>: <span style="color:#f1fa8c">&#39;application/json&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd;font-style:italic">examples</span>: {
</span></span><span style="display:flex;"><span>              <span style="color:#8be9fd;font-style:italic">default</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#8be9fd;font-style:italic">value</span>: <span style="color:#50fa7b">loadJsonContent</span>(<span style="color:#f1fa8c">&#39;jsonMockSample.json&#39;</span>)
</span></span><span style="display:flex;"><span>              }
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>          }
</span></span><span style="display:flex;"><span>        ]
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Configure the API Operation Policy with application/json 200 response</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">apiOperationPolicyDeploy</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis/operations/policies@2021-08-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;policy&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">apiOperation</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">format</span>: <span style="color:#f1fa8c">&#39;xml&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">value</span>: <span style="color:#50fa7b">loadTextContent</span>(<span style="color:#f1fa8c">&#39;policy.xml&#39;</span>)
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Happy Mocking</p>
]]></content:encoded></item><item><title>Azure Role Assignment</title><link>https://andrewilson.co.uk/post/2022/09/azure-assign-role/</link><pubDate>Fri, 02 Sep 2022 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2022/09/azure-assign-role/</guid><description>Problem Space: I recently came into some issues with assigning Azure roles through a Bicep template and pipeline deployment. I was looking to assign &amp;lsquo;Storage Blob Data Reader&amp;rsquo; to a service principal, and refine their access to only the container of the storage account. The three main issues that I ran into were:
What are Role Assignment Conditions and how can I use them in my template? I am trying to assign a built in role, what is the roleDefinitionId that I should be using?</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space:</h2>
<p>I recently came into some issues with assigning Azure roles through a Bicep template and pipeline deployment. I was looking to assign &lsquo;<em>Storage Blob Data Reader</em>&rsquo; to a service principal, and refine their access to only the container of the storage account. The three main issues that I ran into were:</p>
<ol>
<li>What are <a href="#role-assignment-conditions">Role Assignment Conditions</a> and how can I use them in my template?</li>
<li>I am trying to assign a built in role, what is the <a href="#identifying-the-role-definition-id">roleDefinitionId</a> that I should be using?</li>
<li>I am trying to assign the role to a <a href="#referencing-a-service-principal-id">service principal user</a>, what id should I be referencing in the template?</li>
</ol>
<p>For reference, the Bicep template for role assignment<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> that I am using is shown below:</p>
<pre tabindex="0"><code>resource symbolicname &#39;Microsoft.Authorization/roleAssignments@2022-04-01&#39; = {
  name: &#39;string&#39;
  scope: resourceSymbolicName or tenant()
  properties: {
    condition: &#39;string&#39;
    conditionVersion: &#39;string&#39;
    delegatedManagedIdentityResourceId: &#39;string&#39;
    description: &#39;string&#39;
    principalId: &#39;string&#39;
    principalType: &#39;string&#39;
    roleDefinitionId: &#39;string&#39;
  }
}
</code></pre><p>Just in case you are new to this and require a bit more information on role assignments, Azure role assignments are used to provide a security principal access to Azure resources. Assignments are built up of three main components<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> :</p>
<ol>
<li><strong>Security Principal</strong> - this is the user, group, service principal, or managed identity that the role is going to be assigned to.</li>
<li><strong>Role Definition</strong> - this is the built in or custom defined collection of permissions that is to be assigned to the security principal.</li>
<li><strong>Scope</strong> - this is the set of resources that the permissions apply to. You can assign roles at four different scope levels:
<ul>
<li>Management Group
<ul>
<li>Subscription
<ul>
<li>Resource group
<ul>
<li>Resource</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<p>With that in mind, lets look into the problems I was having.</p>
<h3 id="role-assignment-conditions">Role Assignment Conditions</h3>
<p>As per <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/conditions-format">Microsoft Documentation</a>, &lsquo;<em>a condition is an additional check that you can optionally add to your role assignment to provide more fine-grained access control. For example, you can add a condition that requires an object to have a specific tag to read the object.</em>&rsquo; Or in my case, as mentioned above, I would like to refine my service principals access to a specific container on the storage account.</p>
<p>The Bicep template takes your defined condition as a string, so lets look at the syntax and format of the condition that makes up this string.</p>
<h4 id="syntax-of-a-simple-condition">Syntax of a simple condition</h4>
<p>Conditions are made up of a mixture of actions and expressions.</p>
<ul>
<li>An action is an operation that a user can perform on a resource type.</li>
<li>An expression is a statement that evaluates to true or false, which determines whether the action is allowed to be performed.</li>
</ul>
<h5 id="action-condition">Action Condition</h5>
<p>Syntax for an action condition:</p>
<pre tabindex="0"><code>(
  (
    ActionMatches{&#39;&lt;action&gt;&#39;}
  )
)
</code></pre><p>The value that you replace &lsquo;<em><code>&lt;action&gt;</code></em>&rsquo; with is the action namespace such as &lsquo;<em>Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read</em>&rsquo;. For a storage account, you can find a list of these defined actions and examples <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-abac-attributes">here</a>.</p>
<h5 id="expression-condition">Expression Condition</h5>
<p>Syntax for an expression condition:</p>
<pre tabindex="0"><code>(
  (
    &lt;attribute&gt; &lt;operator&gt; &lt;value&gt;
  )
)
</code></pre><p>The values that you replace &lsquo;<em><code>&lt;attribute&gt;</code></em>&rsquo; with can be one of the following:</p>
<ul>
<li>Resource - <em>Indicates that the attribute is on the resource, such as a container name.</em>
<ul>
<li>Example <em><code>@Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name]</code></em></li>
</ul>
</li>
<li>Request - <em>Indicates that the attribute is part of the action request, such as setting the blob index tag.</em>
<ul>
<li>Example <em><code>@Request[Microsoft.Storage/storageAccounts/blobServices/containers/blobs:snapshot]</code></em></li>
</ul>
</li>
<li>Principal - <em>Indicates that the attribute is an Azure AD custom security attribute on the principal, such as a user, enterprise application (service principal), or managed identity.</em>
<ul>
<li>Example <em><code>@Principal[Microsoft.Directory/CustomSecurityAttributes/Id:Engineering_Project]</code></em></li>
</ul>
</li>
</ul>
<h5 id="operators">Operators</h5>
<p>The values that you replace &lsquo;<em><code>&lt;operator&gt;</code></em>&rsquo; indicate how the attribute is to be evaluated. These are split between:</p>
<ul>
<li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/conditions-format#function-operators">Function Operators</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/conditions-format#logical-operators">Logical Operators</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/conditions-format#boolean-comparison-operators">Boolean Comparison Operators</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/conditions-format#string-comparison-operators">String Comparison Operators</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/conditions-format#numeric-comparison-operators">Numeric Comparison Operators</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/conditions-format#datetime-comparison-operators">DateTime Comparison Operators</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/conditions-format#cross-product-comparison-operators">Cross Product Comparison Operators</a></li>
</ul>
<p>Lastly &lsquo;<em><code>&lt;value&gt;</code></em>&rsquo; will be replaced with what you are expecting the attribute to equate to.</p>
<h5 id="identifying-my-condition">Identifying My Condition</h5>
<p>These action and attribute conditions can be combined to create simple and far more complex conditions to suit your needs, see <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/conditions-format">documentation</a> for more.</p>
<p>In my case, I would like a simple condition that will only allow my service principal access to read blobs within a specific storage account container. Therefore, using the syntax above, my condition will look like the following:</p>
<pre tabindex="0"><code>( &lt;-- Condition
  @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] &lt;-- Attribute
  StringEqualsIgnoreCase &lt;-- Operation
  &#39;nameOfContainer&#39; &lt;-- Value
)
</code></pre><p>To place this within a Bicep template, make sure to escape your single quotes around the value, such as:</p>
<pre tabindex="0"><code>&#39;(@Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringEqualsIgnoreCase \&#39;nameOfContainer\&#39;)&#39;
</code></pre><h3 id="identifying-the-role-definition-id">Identifying the Role Definition Id</h3>
<p>The role definition id is what is used to identify the role that is to be applied to your security principal. Whether you create a custom role definition or use a standard built-in role definition, each one will have an id, and finding them is exactly the same.</p>
<p>Methods to find your Definition Id:</p>
<ol>
<li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/role-definitions-list#azure-portal">Azure Portal</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/role-definitions-list#azure-powershell">Azure PowerShell</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/role-definitions-list#azure-cli">Azure CLI</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/role-definitions-list#rest-api">REST API</a></li>
</ol>
<p>Whichever method you choose, retrieve the Id of the role. The role definition id (<em>guid</em>) by itself cannot be utilised to reference the role definition in your template. To reference the Id correctly, you will need to reference the role definition as a resource. Such as:</p>
<pre tabindex="0"><code>roleDefinitionId: &#39;/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/{Guid role definition Id}&#39;
</code></pre><p>In my case, I would like to reference the &lsquo;<em>Storage Blob Data Reader</em>&rsquo; built-in role definition.</p>
<p>Using Azure PowerShell:</p>
<ol>
<li>Using <em><code>Connect-AzAccount</code></em> to Login</li>
<li>Then using this command to get the Role Definition <em><code>Get-AzRoleDefinition 'Storage Blob Data Reader'</code></em></li>
<li>From the output, I have identified the Id which is <code>2a2b9908-6ea1-4ae2-8e65-a410df84e7d1</code></li>
</ol>
<p>Therefore the Bicep template property will appear as follows:</p>
<pre tabindex="0"><code>roleDefinitionId: &#39;/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1&#39;
</code></pre><h3 id="referencing-a-service-principal-id">Referencing a Service Principal Id</h3>
<p>If you are like me, and you followed standard practice for creating a Service Principal user, you followed these steps:</p>
<ol>
<li>Create an App Registration for your Service Principal.</li>
<li>Create a Secret on the App Registration.</li>
<li>Make note of the secret, and the Client Id.</li>
</ol>
<p>In previous API versions of the role assignment template, you were able to reference the <code>Client Id</code> as the Principal Id. However, in the <code>2022-04-01</code> template API version, this does not work.</p>
<p>When you create an App Registration for your service principal, you also get a linked Enterprise Application.</p>
<p>From what appears to be an act of decoupling, role assignments now use the <code>ObjectId</code> of the Enterprise Application of which then references your App Registration. You can obtain the Enterprise Application <code>ObjectId</code> using Azure PowerShell:</p>
<ol>
<li>Using <em><code>Connect-AzAccount</code></em> to Login</li>
<li>Then using this command to get the Enterprise Application details <em><code>Get-AzADServicePrincipal -DisplayName '{Name Of App Reg}' | fl</code></em></li>
<li>From the output, identify the Id - this is the <code>ObjectId</code></li>
</ol>
<p>The method of authentication, if using the App Registration Client Id and Secret will certainly stay the same.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Microsoft Authorization roleAssignments | <a href="https://docs.microsoft.com/en-us/azure/templates/microsoft.authorization/roleassignments?pivots=deployment-language-bicep">https://docs.microsoft.com/en-us/azure/templates/microsoft.authorization/roleassignments?pivots=deployment-language-bicep</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>More on what is Azure role-based access | <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/overview">https://docs.microsoft.com/en-us/azure/role-based-access-control/overview</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item></channel></rss>