<?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>Posts on Andrew Wilson's Blog</title><link>https://andrewilson.co.uk/post/</link><description>Recent content in Posts on Andrew Wilson's Blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 14 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://andrewilson.co.uk/post/index.xml" rel="self" type="application/rss+xml"/><item><title>Azure Key Vault, Terraform, and the Control Plane/Data Plane Difference</title><link>https://andrewilson.co.uk/post/2026/09/azure-key-vault-terraform-control-data-plane/</link><pubDate>Mon, 14 Sep 2026 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2026/09/azure-key-vault-terraform-control-data-plane/</guid><description>&lt;p&gt;The other day I ran into one of those Azure deployment problems that looks reasonable right up until you understand which service is actually making the call.&lt;/p&gt;
&lt;p&gt;I had provisioned an Azure Key Vault with &lt;code&gt;enabled_for_template_deployment&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt;. I was familiar with this setting from working with Bicep and ARM templates, where it allows the vault to be used for secret retrieval during a deployment.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; The deployment identity still needs the required ARM deployment permission.&lt;/p&gt;</description><content:encoded><![CDATA[<p>The other day I ran into one of those Azure deployment problems that looks reasonable right up until you understand which service is actually making the call.</p>
<p>I had provisioned an Azure Key Vault with <code>enabled_for_template_deployment</code> set to <code>true</code>. I was familiar with this setting from working with Bicep and ARM templates, where it allows the vault to be used for secret retrieval during a deployment.</p>
<blockquote>
<p><strong>Note</strong> The deployment identity still needs the required ARM deployment permission.</p>
</blockquote>
<p>The Key Vault deployed successfully. I then used Terraform to create a secret in it and expected the setting to cover that deployment as well. It did not.</p>
<p>That was the point where I had to stop thinking about the setting and look at what was actually making each request.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#ff79c6">variable</span> <span style="color:#f1fa8c">&#34;secret_value&#34;</span> {
</span></span><span style="display:flex;"><span>	type      <span style="color:#ff79c6">=</span> <span style="color:#ff79c6">string</span>
</span></span><span style="display:flex;"><span>	sensitive <span style="color:#ff79c6">=</span> <span style="color:#8be9fd">true</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">resource</span> <span style="color:#f1fa8c">&#34;azurerm_key_vault&#34; &#34;example&#34;</span> {
</span></span><span style="display:flex;"><span>	name                          <span style="color:#ff79c6">=</span> <span style="color:#f1fa8c">&#34;example-key-vault&#34;</span>
</span></span><span style="display:flex;"><span>	location                      <span style="color:#ff79c6">=</span> <span style="color:#ff79c6">azurerm_resource_group</span>.<span style="color:#ff79c6">example</span>.<span style="color:#ff79c6">location</span>
</span></span><span style="display:flex;"><span>	resource_group_name           <span style="color:#ff79c6">=</span> <span style="color:#ff79c6">azurerm_resource_group</span>.<span style="color:#ff79c6">example</span>.<span style="color:#ff79c6">name</span>
</span></span><span style="display:flex;"><span>	tenant_id                     <span style="color:#ff79c6">=</span> <span style="color:#ff79c6">data</span>.<span style="color:#ff79c6">azurerm_client_config</span>.<span style="color:#ff79c6">current</span>.<span style="color:#ff79c6">tenant_id</span>
</span></span><span style="display:flex;"><span>	sku_name                      <span style="color:#ff79c6">=</span> <span style="color:#f1fa8c">&#34;standard&#34;</span>
</span></span><span style="display:flex;"><span>	enable_rbac_authorization     <span style="color:#ff79c6">=</span> <span style="color:#8be9fd">true</span>
</span></span><span style="display:flex;"><span>	enabled_for_template_deployment <span style="color:#ff79c6">=</span> <span style="color:#8be9fd">true</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">resource</span> <span style="color:#f1fa8c">&#34;azurerm_key_vault_secret&#34; &#34;example&#34;</span> {
</span></span><span style="display:flex;"><span>	name         <span style="color:#ff79c6">=</span> <span style="color:#f1fa8c">&#34;example-secret&#34;</span>
</span></span><span style="display:flex;"><span>	value        <span style="color:#ff79c6">=</span> <span style="color:#ff79c6">var</span>.<span style="color:#ff79c6">secret_value</span>
</span></span><span style="display:flex;"><span>	key_vault_id <span style="color:#ff79c6">=</span> <span style="color:#ff79c6">azurerm_key_vault</span>.<span style="color:#ff79c6">example</span>.<span style="color:#ff79c6">id</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="what-i-had-missed">What I had missed</h2>
<p>The Key Vault itself is an Azure Resource Manager resource. Configuring it with <code>azurerm_key_vault</code> is a control plane operation. That part matched what I expected.</p>
<p>Creating a secret is different. <code>azurerm_key_vault_secret</code> uses the Key Vault service API, which is the data plane. The Terraform identity therefore needs an appropriate Azure RBAC role to set secrets, and the machine running Terraform must be able to reach the Key Vault endpoint.</p>
<p>That meant checking two separate things:</p>
<ul>
<li>Did the Terraform identity have permission to write secrets?</li>
<li>Could the Terraform runner reach the vault through its networking configuration?</li>
</ul>
<p>Having successfully deployed the vault through ARM did not answer either question. The vault request and the secret request were taking different paths.</p>
<h2 id="where-azapi-fits">Where AzAPI fits</h2>
<p>Looking at AzAPI helped clarify the difference between the two providers.</p>
<p>AzureRM gives us typed Terraform resources. Many of those resources manage Azure Resource Manager resources through the control plane, but some resources, such as Key Vault secrets, use the service&rsquo;s data-plane API.</p>
<p>AzAPI works directly with Azure Resource Manager REST APIs. It is useful when AzureRM has not yet exposed a control-plane resource, API version, or property that is available in Azure.</p>
<p>That makes AzAPI useful for an AzureRM capability gap, but it is not a general replacement for AzureRM data-plane resources. It is a different route to the Azure Resource Manager API, not a switch that makes every AzureRM resource use ARM.</p>
<p>There is a useful detail here. Azure also exposes secrets as the ARM resource <code>Microsoft.KeyVault/vaults/secrets</code>, which AzAPI can target. That provides an ARM-based way to create a secret and may avoid the Terraform runner needing direct access to the Key Vault data-plane endpoint. It still requires the appropriate ARM permissions, and the ARM resource is intended for deployment scenarios; for normal secret interaction, Azure recommends the data-plane API.</p>
<h2 id="the-thing-worth-remembering">The thing worth remembering</h2>
<p><code>enabled_for_template_deployment</code> enables secret retrieval during an ARM or Bicep template deployment (<em>The deployment identity still needs the required ARM deployment permission</em>). The setting does not grant the deployment identity permission to create/read secrets.</p>
<p>When a deployment behaves unexpectedly, it is worth asking which identity is making the request and which endpoint it is calling. In this case, the vault was deployed through ARM, but the AzureRM secret resource was created through the Key Vault data plane. AzAPI can use a different ARM-based resource path, but that comes with different permissions and lifecycle considerations.</p>
<p>That distinction is easy to miss, particularly when moving between Bicep, ARM, and Terraform, so I am noting it here for the next time I run into it.</p>
<p>Useful references:</p>
<ul>
<li><a href="https://learn.microsoft.com/azure/azure-resource-manager/management/control-plane-and-data-plane">Azure control plane and data plane</a></li>
<li><a href="https://learn.microsoft.com/azure/azure-resource-manager/templates/key-vault-parameter">Use Azure Key Vault in template deployments</a></li>
<li><a href="https://learn.microsoft.com/azure/key-vault/general/rbac-guide">Azure Key Vault RBAC guide</a></li>
<li><a href="https://learn.microsoft.com/rest/api/keyvault/secrets/set-secret">Key Vault Set Secret REST API</a></li>
<li><a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_secret">AzureRM Key Vault secret resource</a></li>
<li><a href="https://learn.microsoft.com/azure/templates/microsoft.keyvault/vaults/secrets">Microsoft.KeyVault/vaults/secrets ARM resource</a></li>
<li><a href="https://learn.microsoft.com/azure/developer/terraform/azapi/overview-azapi-provider">AzAPI provider overview</a></li>
</ul>
]]></content:encoded></item><item><title>Role Assignments | Role Based Access Control Administrator</title><link>https://andrewilson.co.uk/post/2026/09/role-based-access-control-administrator/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2026/09/role-based-access-control-administrator/</guid><description>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;When deploying Azure solutions with Infrastructure as Code, we often create role assignments as part of the deployment. For example, a Bicep deployment may give a Function App&amp;rsquo;s managed identity access to a Key Vault, storage account, or Service Bus namespace.&lt;/p&gt;
&lt;p&gt;It is easy to focus on the role being assigned and forget about the identity performing the deployment. The deployment service principal needs permission to create the role assignment itself. Access to the target resource does not automatically provide this permission.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>When deploying Azure solutions with Infrastructure as Code, we often create role assignments as part of the deployment. For example, a Bicep deployment may give a Function App&rsquo;s managed identity access to a Key Vault, storage account, or Service Bus namespace.</p>
<p>It is easy to focus on the role being assigned and forget about the identity performing the deployment. The deployment service principal needs permission to create the role assignment itself. Access to the target resource does not automatically provide this permission.</p>
<p>When that permission is missing, the deployment commonly fails with an error similar to:</p>
<p><code>The client '&lt;deployment-client-id&gt;' with object id '&lt;object-id&gt;' does not have authorization to perform action 'Microsoft.Authorization/roleAssignments/write' over scope '&lt;scope&gt;'.</code></p>
<p>And then comes the familiar question: what was that role I needed to assign to the deployment identity?</p>
<h2 id="explanation">Explanation</h2>
<p>The role is <a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator">Role Based Access Control Administrator</a>.</p>
<p>This is an Azure built-in role that allows an identity to manage role assignments at the scope where the role is assigned. In practical terms, it gives the deployment service principal permission to create and remove the RBAC assignments declared by the IaC templates.</p>
<p>This role is separate from the role being granted by the deployment:</p>
<ul>
<li>The <strong>deployment service principal</strong> needs permission to write the role assignment.</li>
<li>The <strong>managed identity or application</strong> receives the role assignment.</li>
<li>The <strong>target resource</strong> is where that access is granted.</li>
</ul>
<p>The Role Based Access Control Administrator role has the built-in role ID <code>f58310d9-a9f6-439a-9e8d-f62e7b41a168</code>. It should not be confused with <code>User Access Administrator</code>, which is another role that can manage access and has a different permission boundary.</p>
<h2 id="solution">Solution</h2>
<p>Assign the Role Based Access Control Administrator role to the deployment service principal at the narrowest scope that contains the role assignments. For example, at resource-group scope.</p>
<p>Once this has been assigned, the deployment identity can create the resource role assignments in that resource group, subject to the normal restrictions on which roles it is allowed to assign.</p>
<p>The identity used to assign this permission must already be authorized to create role assignments. This is usually a one-time platform or subscription setup task, rather than something the deployment should attempt to grant to itself.</p>
<blockquote>
<p>⚠️ Best Practice of Least Privilege: avoid assigning this role at subscription scope when the deployment only needs to manage assignments within one resource group or resource.</p>
</blockquote>
<p>So, when an Azure IaC deployment fails while creating a role assignment, check the deployment service principal first. This is to jog my memory as well as yours, the role you are looking for is <strong>Role Based Access Control Administrator</strong>.</p>
<p>Hope this helps, and have fun.</p>
]]></content:encoded></item><item><title>Azure Logic Apps Standard | Keep WEBSITE_CONTENTSHARE Unique</title><link>https://andrewilson.co.uk/post/2026/09/azure-logic-apps-website-contentshare-unique/</link><pubDate>Thu, 03 Sep 2026 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2026/09/azure-logic-apps-website-contentshare-unique/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-app-settings#website_contentshare"&gt;&lt;code&gt;WEBSITE_CONTENTSHARE&lt;/code&gt;&lt;/a&gt; is an app setting used by Azure Functions and Logic Apps Standard (which runs on the Functions runtime) alongside &lt;code&gt;WEBSITE_CONTENTAZUREFILECONNECTIONSTRING&lt;/code&gt; to identify the Azure Files share the app uses for its content.&lt;/p&gt;
&lt;p&gt;The important thing to call out up front: &lt;strong&gt;you don&amp;rsquo;t always need to set this&lt;/strong&gt;. When left unmanaged, Azure generates a share name for you automatically and everything just works.&lt;/p&gt;
&lt;p&gt;There are, however, scenarios where you must set it to a predefined value. The most common is when you &lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/configure-networking-how-to#restrict-your-storage-account-to-a-virtual-network"&gt;use a secured storage account in a virtual network&lt;/a&gt; - in that case, you must set a unique share name for the main app and for the app associated with each deployment slot. For a storage account secured by a virtual network, you must also create the share itself as part of your automated deployment (see &lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-infrastructure-as-code#secured-deployments"&gt;Secured deployments&lt;/a&gt;).&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p><a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-app-settings#website_contentshare"><code>WEBSITE_CONTENTSHARE</code></a> is an app setting used by Azure Functions and Logic Apps Standard (which runs on the Functions runtime) alongside <code>WEBSITE_CONTENTAZUREFILECONNECTIONSTRING</code> to identify the Azure Files share the app uses for its content.</p>
<p>The important thing to call out up front: <strong>you don&rsquo;t always need to set this</strong>. When left unmanaged, Azure generates a share name for you automatically and everything just works.</p>
<p>There are, however, scenarios where you must set it to a predefined value. The most common is when you <a href="https://learn.microsoft.com/en-us/azure/azure-functions/configure-networking-how-to#restrict-your-storage-account-to-a-virtual-network">use a secured storage account in a virtual network</a> - in that case, you must set a unique share name for the main app and for the app associated with each deployment slot. For a storage account secured by a virtual network, you must also create the share itself as part of your automated deployment (see <a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-infrastructure-as-code#secured-deployments">Secured deployments</a>).</p>
<p>The reason it&rsquo;s worth understanding at all is that getting it wrong doesn&rsquo;t fail loudly.</p>
<h2 id="how-the-file-share-backs-your-logic-app">How the File Share Backs Your Logic App</h2>
<p>Rather than storing your workflow definitions and related files directly on the Logic App instance, the runtime uses an Azure Files share (identified by <code>WEBSITE_CONTENTSHARE</code>, within the storage account referenced by <code>WEBSITE_CONTENTAZUREFILECONNECTIONSTRING</code>) and treats it as the source of truth for the app&rsquo;s content such as your workflow <code>.json</code> definitions, <code>host.json</code>, and inline code <code>.csx</code>.</p>
<p>In other words, the file share isn&rsquo;t just a supporting piece of infrastructure - it is the app, from a content perspective. Whatever is in that share is what the app runs.</p>
<h2 id="the-incident">The Incident</h2>
<p>I came across a case where two separate Logic App Standard resources had been deployed referencing the same storage account, and whether through copy-pasted deployment parameters or a naming convention that didn&rsquo;t account for it, had both ended up with the same <code>WEBSITE_CONTENTSHARE</code> value.</p>
<p>The symptoms were bizarre (mainly due to always having had isolation of storage accounts and certainly file shares). Workflows authored in one Logic App started showing up in the other. A deployment or a change made through one app&rsquo;s designer would be reflected in the second app, and vice versa. Two Logic Apps, with different names, different resource IDs, sitting in different places in the resource group, yet behaving as if they were the same application. Nothing about the apps themselves looked wrong, which made it a genuinely uncomfortable thing to debug. The only thing that was the same was the workflow name.</p>
<h2 id="root-cause">Root Cause</h2>
<p>Once traced back, the explanation was straightforward, both Logic Apps were pointed at the exact same Azure Files share. Since the content share is the app&rsquo;s set of workflows and artifacts, both Logic App resources were reading from and writing to the identical set of files.</p>
<p>There weren&rsquo;t two apps with similar configuration that happened to drift into sync, there was only ever one set of workflow content, fronted by two separate compute resources. Both apps were simply reading and writing the same underlying files the whole time.</p>
<h2 id="recommendation">Recommendation</h2>
<p>If you never explicitly manage <code>WEBSITE_CONTENTSHARE</code>, Azure will generate a unique value for you and you won&rsquo;t hit this. Where you do need to set it, such as with a VNet-secured storage account, or when cloning an app or restoring from a backup, make sure the value is unique per app instance, including for the main app and each deployment slot.</p>
<blockquote>
<p>⚠️ <strong>Note</strong></p>
<p>After deploying, it&rsquo;s worth a quick sanity check against the Files service in the storage account to confirm each app has its own distinct share, particularly if your deployment pipeline sets this value explicitly or reuses parameter files across environments.</p>
</blockquote>
<h2 id="conclusion">Conclusion</h2>
<p>It&rsquo;s a strange situation to land in, two apps that are, for all practical purposes, one app hosted twice, but it&rsquo;s also entirely avoidable. <code>WEBSITE_CONTENTSHARE</code> is one of those settings you can safely ignore most of the time, but if you do find yourself setting it explicitly, uniqueness per app is non-negotiable.</p>
]]></content:encoded></item><item><title>Bicep Tips and Tricks | #11 | Working with Role Assignments</title><link>https://andrewilson.co.uk/post/2026/08/bicep-tips-and-tricks-working-with-role-assignments/</link><pubDate>Fri, 07 Aug 2026 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2026/08/bicep-tips-and-tricks-working-with-role-assignments/</guid><description>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;Role assignments are one of those areas in Azure that look simple on the surface, but can become awkward pretty quickly once you start automating them properly.&lt;/p&gt;
&lt;p&gt;Anyone who has worked with RBAC through IaC for long enough will usually run into the same set of problems.&lt;/p&gt;
&lt;p&gt;There are usually three things in play:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The role assignment name needs to be deterministic&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;roleDefinitionId&lt;/code&gt; is represented by a GUID that is not especially readable nor indicative of which role it is referring&lt;/li&gt;
&lt;li&gt;When something already exists, the resulting deployment error has not always been particularly helpful&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thankfully, there have been some recent improvements here that are worth knowing about. Some help with troubleshooting, some help with authoring, and one in particular helps with conformance across tooling.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>Role assignments are one of those areas in Azure that look simple on the surface, but can become awkward pretty quickly once you start automating them properly.</p>
<p>Anyone who has worked with RBAC through IaC for long enough will usually run into the same set of problems.</p>
<p>There are usually three things in play:</p>
<ul>
<li>The role assignment name needs to be deterministic</li>
<li>The <code>roleDefinitionId</code> is represented by a GUID that is not especially readable nor indicative of which role it is referring</li>
<li>When something already exists, the resulting deployment error has not always been particularly helpful</li>
</ul>
<p>Thankfully, there have been some recent improvements here that are worth knowing about. Some help with troubleshooting, some help with authoring, and one in particular helps with conformance across tooling.</p>
<p>In this post I wanted to walk through three of them:</p>
<ol>
<li>Improved ARM error messages when a role assignment already exists</li>
<li>A defined standard for role assignment ID generation</li>
<li>Bicep support for resolving role definition IDs from the friendly role name</li>
</ol>
<h2 id="improved-error-message-for-existing-role-assignments">Improved Error Message for Existing Role Assignments</h2>
<p>Historically, if you attempted to deploy a role assignment that already existed but with a different name ID, ARM would return 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;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>	<span style="color:#ff79c6">&#34;code&#34;</span>: <span style="color:#f1fa8c">&#34;RoleAssignmentExists&#34;</span>,
</span></span><span style="display:flex;"><span>	<span style="color:#ff79c6">&#34;message&#34;</span>: <span style="color:#f1fa8c">&#34;The role assignment already exists.&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Technically correct, but not overly helpful.</p>
<p>You knew something was already there, but not which role assignment, not what its ID was, and not how quickly you could correlate that back to the target scope. In environments with multiple deployment paths or platform teams supporting shared subscriptions, that usually meant extra investigation.</p>
<p>The updated message is much better:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>	<span style="color:#ff79c6">&#34;code&#34;</span>: <span style="color:#f1fa8c">&#34;RoleAssignmentExists&#34;</span>,
</span></span><span style="display:flex;"><span>	<span style="color:#ff79c6">&#34;message&#34;</span>: <span style="color:#f1fa8c">&#34;The role assignment already exists. The ID of the existing role assignment is a3f1c9e2-7b4d-4e8a-bf62-1d0c5a9e34f7.&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This is a small change, but a useful one. You can now go straight to the existing assignment, confirm the scope and principal, and decide whether the issue is:</p>
<ul>
<li>A valid existing assignment that your deployment should align to</li>
<li>A duplicate assignment attempt from another deployment path</li>
<li>A stale or unexpected assignment that needs to be cleaned up</li>
</ul>
<p>From an operational perspective, this should make diagnosis much quicker, especially when you are dealing with shared scopes or multiple deployment paths.</p>
<h2 id="standardised-role-assignment-id-generation">Standardised Role Assignment ID Generation</h2>
<p>For me, this is one of the more important changes because it helps establish consistency across teams and tooling.</p>
<p>There is now a defined standard for how role assignment IDs should be generated across Portal, Bicep, AzCLI, PowerShell, and Terraform:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>guid(scope, principalId, roleDefinitionId)
</span></span></code></pre></div><p>This matters because role assignments should not have arbitrary names. If one tool or team generates the assignment name using one set of inputs and another tool uses something different, you can end up with the same assignment intent represented by different assignment names. That is exactly the sort of inconsistency that makes troubleshooting harder than it needs to be.</p>
<p>Using the standard generation pattern gives you a few benefits:</p>
<ul>
<li>Consistent behaviour across tooling</li>
<li>Better conformance with the broader Azure platform expectation</li>
<li>Easier reasoning when comparing manually created and IaC-created assignments</li>
<li>Fewer surprises when different teams or delivery tools interact with the same scope</li>
</ul>
<p>If you are still generating the role assignment name using arbitrary values, or values that do not include the scope, principal ID, and role definition ID, it is worth correcting that.</p>
<p>For example, this is the pattern I would now avoid:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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">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:#f1fa8c">&#39;contributor&#39;</span>, <span style="color:#8be9fd;font-style:italic">principalId</span>) <span style="color:#6272a4">// &lt;-- Avoid</span>
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">scope</span>: <span style="color:#50fa7b">resourceGroup</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:#8be9fd;font-style:italic">contributorRoleDefinitionId</span>
</span></span><span style="display:flex;"><span>		<span style="color:#8be9fd;font-style:italic">principalId</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><p>And this is the pattern to prefer:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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">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:#50fa7b">resourceGroup</span>().<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#8be9fd;font-style:italic">principalId</span>, <span style="color:#8be9fd;font-style:italic">contributorRoleDefinitionId</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">scope</span>: <span style="color:#50fa7b">resourceGroup</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:#8be9fd;font-style:italic">contributorRoleDefinitionId</span>
</span></span><span style="display:flex;"><span>		<span style="color:#8be9fd;font-style:italic">principalId</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><p>It is deterministic, aligned to the standard, and much easier to reason about when you need to compare behaviour across tools.</p>
<h2 id="friendly-role-definition-lookup-in-bicep">Friendly Role Definition Lookup in Bicep</h2>
<p>The next improvement is the Bicep <code>roleDefinitions()</code> function introduced in <a href="https://github.com/Azure/bicep/releases/tag/v0.42.1">v0.42.1</a>.</p>
<p>This allows you to retrieve the role definition ID from the friendly role name:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bicep" data-lang="bicep"><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">roleDefinitions</span>(<span style="color:#f1fa8c">&#39;Data Factory Contributor&#39;</span>).<span style="color:#8be9fd;font-style:italic">id</span>
</span></span><span style="display:flex;"><span>	...
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>From a readability point of view, this is a nice improvement, and probably the part most people will notice first.</p>
<p>If you compare a friendly name such as <code>Data Factory Contributor</code> to a raw GUID, it is immediately obvious which role is being assigned. That helps with:</p>
<ul>
<li>Code reviews</li>
<li>Onboarding team members into your templates</li>
<li>General maintainability when revisiting code months later</li>
</ul>
<p>Compare the older style:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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">contributorRoleDefinitionGuid</span> = <span style="color:#f1fa8c">&#39;b24988ac-6180-42a0-ab88-20f7382dd24c&#39;</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:#50fa7b">resourceGroup</span>().<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#8be9fd;font-style:italic">principalId</span>, <span style="color:#8be9fd;font-style:italic">contributorRoleDefinitionId</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#8be9fd;font-style:italic">scope</span>: <span style="color:#50fa7b">resourceGroup</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:#8be9fd;font-style:italic">contributorRoleDefinitionId</span>
</span></span><span style="display:flex;"><span>		<span style="color:#8be9fd;font-style:italic">principalId</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><p>With the newer style:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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">contributorRoleDefinition</span> = <span style="color:#50fa7b">roleDefinitions</span>(<span style="color:#f1fa8c">&#39;Contributor&#39;</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:#50fa7b">resourceGroup</span>().<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#8be9fd;font-style:italic">principalId</span>, <span style="color:#8be9fd;font-style:italic">contributorRoleDefinition</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:#50fa7b">resourceGroup</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:#8be9fd;font-style:italic">contributorRoleDefinition</span>.<span style="color:#8be9fd;font-style:italic">id</span>
</span></span><span style="display:flex;"><span>		<span style="color:#8be9fd;font-style:italic">principalId</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><p>I think there is a very fair maintainability argument in favour of the second example. The intent is clearer and there is less mental translation required when reading the template.</p>
<h3 id="the-trade-off">The Trade-Off</h3>
<p>This is where I would urge a little caution.</p>
<p>Whilst <code>roleDefinitions()</code> improves readability, it also introduces a dependency on the display name of the role definition. The underlying role definition ID may remain stable, but the role display name can change over time, even when the underlying role definition ID remains the same.</p>
<p>Is that likely to happen frequently? No, probably not.</p>
<p>Is it impossible? Also no.</p>
<p>That is the trade-off.</p>
<p>If a built-in role display name changes and your template is resolving the role definition by name, then your deployment can break even though the underlying role definition itself still exists and still has the same stable ID.</p>
<p>So I think the balanced view here is:</p>
<ul>
<li>If readability and authoring clarity are your primary concern, <code>roleDefinitions()</code> is a very welcome improvement</li>
<li>If you are optimising for resilience and want to minimise dependency on mutable display names, stable GUID-based references are still a valid approach</li>
</ul>
<p>This is also a good argument for centralising stable role definition GUIDs if you choose to keep using them, for example through <a href="/post/2025/08/bicep-tips-and-tricks-shared-variables/">shared variables</a> or imports. That way you still gain maintainability benefits without scattering raw IDs throughout your codebase.</p>
<h2 id="recommended-practice">Recommended Practice</h2>
<p>My current view would be:</p>
<ul>
<li>Always generate the role assignment name using <code>guid(scope, principalId, roleDefinitionId)</code>
<ul>
<li>Treat that pattern as the standard regardless of whether you are using Portal, Bicep, AzCLI, PowerShell, or Terraform</li>
</ul>
</li>
<li>Use <code>roleDefinitions()</code> where readability and maintainability are more valuable than the small risk of role display name changes</li>
<li>Use stable GUID-based role definition references where deployment resilience is the higher priority</li>
</ul>
<p>The important point is not that one of these approaches is universally right and the other universally wrong. It is that you should make the choice consciously.</p>
<p>What I do think should now be treated as non-negotiable is the role assignment naming pattern. That should follow the platform standard.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Recent changes around role assignments are moving things in the right direction.</p>
<p>The improved <code>RoleAssignmentExists</code> message makes diagnosis easier. The defined standard for role assignment ID generation improves conformance across teams and tooling. And the <code>roleDefinitions()</code> function in Bicep improves readability and maintainability when you want friendlier authoring.</p>
<p>For me, the key takeaway is fairly simple: adopt the standard assignment naming pattern everywhere, and then decide whether friendly-name lookup or stable GUID references are the better fit for your environment.</p>
<p>Hope this helps, and happy Bicep-ing!</p>
]]></content:encoded></item><item><title>Azure Logic Apps Standard | Testing Series</title><link>https://andrewilson.co.uk/post/2026/07/azure-logic-app-testing/</link><pubDate>Tue, 28 Jul 2026 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2026/07/azure-logic-app-testing/</guid><description>&lt;p&gt;Over the last few years, Azure Logic Apps Standard has become a core building block for many integration workloads. The platform gives us flexibility, connector richness, and scalable runtime options, but as workflow solutions grow, so does the risk profile.&lt;/p&gt;
&lt;p&gt;In many teams, validation still leans heavily on run history inspection and ad-hoc manual testing. That works in early stages, but it does not scale when you have multiple workflows, shared dependencies, environment-specific behavior, and frequent change. It also makes regression detection harder than it needs to be.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Over the last few years, Azure Logic Apps Standard has become a core building block for many integration workloads. The platform gives us flexibility, connector richness, and scalable runtime options, but as workflow solutions grow, so does the risk profile.</p>
<p>In many teams, validation still leans heavily on run history inspection and ad-hoc manual testing. That works in early stages, but it does not scale when you have multiple workflows, shared dependencies, environment-specific behavior, and frequent change. It also makes regression detection harder than it needs to be.</p>
<p>I want to put this series together because Logic Apps teams need a practical, repeatable testing approach that fits the way Standard workflows are designed and delivered.</p>
<h2 id="why-this-series-is-needed">Why This Series Is Needed</h2>
<p>The challenge is not that testing options do not exist. The challenge is that testing is often fragmented across techniques, tools, and stages of delivery.</p>
<p>From a delivery perspective, we need faster confidence loops before deployment, stronger validation after deployment, and clearer boundaries between unit-level behavior and integration-level behavior. Without that, each workflow change carries avoidable uncertainty.</p>
<p>The goal of this series is to bring those pieces together into a coherent testing strategy that teams can adopt incrementally.</p>
<h2 id="testing-layers-for-logic-apps-standard">Testing Layers for Logic Apps Standard</h2>
<p>To make this practical, I will frame testing as layers that move from fast feedback to high confidence in real-world behavior.</p>
<ol>
<li>Workflow design practices for testability and manageability - Shape workflow architecture so tests stay reliable, maintainable, and useful over time.</li>
<li>Mock output testing in workflows - Validate control flow and branch logic quickly by simulating action outputs and edge-case responses.</li>
<li>Unit tests from Standard workflow definitions in Visual Studio Code - Use workflow definitions as testable assets and automate checks for expected behavior under controlled inputs.</li>
<li>Integration tests - Validate connector interactions, contracts, and downstream dependencies in representative environments.</li>
<li>Agent loop testing options - Explore iterative, agent-assisted test patterns for scenario generation, replay, and change validation.</li>
</ol>
<h2 id="design-principles-to-keep-in-focus">Design Principles to Keep in Focus</h2>
<p>As this series progresses, a few principles will stay constant:</p>
<ol>
<li>Prefer deterministic workflow boundaries - Keep triggers, actions, and side effects structured so the same input consistently drives predictable outcomes.</li>
<li>Separate orchestration from connector-heavy implementation details - Where possible, isolate complex connector behavior behind clean workflow steps to simplify test setup and reduce blast radius.</li>
<li>Treat observability as part of test design - Meaningful tracking properties, correlation IDs, and consistent result surfaces improve both debugging and test assertions.</li>
<li>Design for environment parity, not environment identity - Your test strategy should tolerate differences between dev, test, and prod while still validating the behavior that truly matters.</li>
</ol>
<h2 id="in-summary">In Summary</h2>
<p>I am aiming to keep the series practical and implementation-oriented.</p>
<p>If your team has ever said &ldquo;it worked in one run, but we are not sure why it failed later,&rdquo; this series is for you.</p>
]]></content:encoded></item><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>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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 style="color:#8be9fd;font-style:italic">@</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$setupDeclarations</span> = <span style="color:#8be9fd;font-style:italic">@</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;-webkit-text-size-adjust:none;"><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 style="color:#8be9fd;font-style:italic">@</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;-webkit-text-size-adjust:none;"><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 App Registrations | Display Name Guidance</title><link>https://andrewilson.co.uk/post/2026/05/azure-application-registrations-same-displayname/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2026/05/azure-application-registrations-same-displayname/</guid><description>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;I recently had to troubleshoot an issue where two App Registrations in the same tenant had the same display name.&lt;/p&gt;
&lt;p&gt;Azure allows this. Operationally, it hurts.&lt;/p&gt;
&lt;p&gt;When names are duplicated, diagnosis gets slower and riskier because engineers cannot trust what they see first in the portal.&lt;/p&gt;
&lt;h2 id="why-it-matters"&gt;Why It Matters&lt;/h2&gt;
&lt;p&gt;Display names are not unique identifiers. They are labels.&lt;/p&gt;
&lt;p&gt;That creates avoidable failure modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Wrong app selected during incident response&lt;/li&gt;
&lt;li&gt;Secrets/certificates updated on the wrong registration&lt;/li&gt;
&lt;li&gt;Permission checks performed against the wrong object&lt;/li&gt;
&lt;li&gt;Longer time to restore service because every step needs extra ID validation&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-is-actually-unique"&gt;What Is Actually Unique&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Display name: not unique&lt;/li&gt;
&lt;li&gt;Application (client) ID: unique&lt;/li&gt;
&lt;li&gt;Object ID: unique&lt;/li&gt;
&lt;li&gt;Service principal object ID: unique per tenant instance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Humans start with names; Azure enforces uniqueness with IDs. Good naming closes that gap.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>I recently had to troubleshoot an issue where two App Registrations in the same tenant had the same display name.</p>
<p>Azure allows this. Operationally, it hurts.</p>
<p>When names are duplicated, diagnosis gets slower and riskier because engineers cannot trust what they see first in the portal.</p>
<h2 id="why-it-matters">Why It Matters</h2>
<p>Display names are not unique identifiers. They are labels.</p>
<p>That creates avoidable failure modes:</p>
<ul>
<li>Wrong app selected during incident response</li>
<li>Secrets/certificates updated on the wrong registration</li>
<li>Permission checks performed against the wrong object</li>
<li>Longer time to restore service because every step needs extra ID validation</li>
</ul>
<h2 id="what-is-actually-unique">What Is Actually Unique</h2>
<ul>
<li>Display name: not unique</li>
<li>Application (client) ID: unique</li>
<li>Object ID: unique</li>
<li>Service principal object ID: unique per tenant instance</li>
</ul>
<p>Humans start with names; Azure enforces uniqueness with IDs. Good naming closes that gap.</p>
<h2 id="good-practice-guidance">Good Practice Guidance</h2>
<h3 id="1-adopt-a-naming-standard">1. Adopt a Naming Standard</h3>
<p>Use a format that encodes identity and purpose, for example:</p>
<p><code>&lt;org&gt;-&lt;workload&gt;-&lt;environment&gt;-&lt;purpose&gt;</code></p>
<blockquote>
<p>Example:</p>
<p>contoso-orders-prod-api</p>
</blockquote>
<h3 id="2-make-environment-explicit">2. Make Environment Explicit</h3>
<p>Always include dev/test/stage/prod in the display name, especially for production.</p>
<h3 id="3-avoid-generic-labels">3. Avoid Generic Labels</h3>
<p>Names like api, webapp, or integration will collide over time.</p>
<h3 id="4-add-ownership-metadata">4. Add Ownership Metadata</h3>
<p>Capture owning team, support channel, and source repo in notes or linked documentation.</p>
<h3 id="5-review-regularly">5. Review Regularly</h3>
<p>Run periodic tenant hygiene checks for:</p>
<ul>
<li>Duplicate names</li>
<li>Missing owner data</li>
<li>Stale or unused app registrations</li>
</ul>
<h2 id="if-you-already-have-duplicates">If You Already Have Duplicates</h2>
<ol>
<li>Inventory duplicates.</li>
<li>Map each app to workload and owner.</li>
<li>Rename to standard format.</li>
<li>Update runbooks and operational docs.</li>
<li>Add pre-create checks so the issue does not return.</li>
</ol>
<h2 id="in-short">In Short</h2>
<p>Yes, you can rename later. In practice, teams usually do not until an incident forces it.</p>
<p>Just because duplicate display names are allowed does not mean they are a good idea.</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>&lt;hr&gt;
&lt;p&gt;⚠️ &lt;strong&gt;NOTE&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Microsoft guidance is clear that Azure RBAC should be used for data plane authorization moving forward, instead of legacy access policies&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/key-vault/general/rbac-access-policy"&gt;Azure role-based access control (Azure RBAC) vs. access policies (legacy)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/key-vault/general/rbac-guide"&gt;Provide access to Key Vault keys, certificates, and secrets with Azure role-based access control&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h2 id="why-define-and-stick-to-your-authoring-practices"&gt;Why Define and Stick to Your Authoring Practices&lt;/h2&gt;
&lt;p&gt;Defining and following authoring practices ensures:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consistency across your codebase and team&lt;/li&gt;
&lt;li&gt;Easier onboarding for new contributors&lt;/li&gt;
&lt;li&gt;Fewer errors and less technical debt&lt;/li&gt;
&lt;li&gt;Improved maintainability and clarity&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below are some key practices and common anti-patterns to consider.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>MSTest | Controlling Parallelism of Test Runs</title><link>https://andrewilson.co.uk/post/2026/02/mstest-controlling-parallelism/</link><pubDate>Mon, 09 Feb 2026 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2026/02/mstest-controlling-parallelism/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I have been writing integration tests using MSTest recently and have encountered the need to control the level of parallelism required. MSTest, by default, runs your tests in parallel to maximise performance and reduce overall test execution time. This is great for independent unit tests, but it can quickly become a problem when you&amp;rsquo;re writing integration tests that access shared resources like databases, APIs, or cloud services. I recently encountered this issue when my integration test suite started failing randomly due to parallel tests competing for the same data or creating conflicting test data. The solution? Learning to control MSTest&amp;rsquo;s parallelism settings.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>I have been writing integration tests using MSTest recently and have encountered the need to control the level of parallelism required. MSTest, by default, runs your tests in parallel to maximise performance and reduce overall test execution time. This is great for independent unit tests, but it can quickly become a problem when you&rsquo;re writing integration tests that access shared resources like databases, APIs, or cloud services. I recently encountered this issue when my integration test suite started failing randomly due to parallel tests competing for the same data or creating conflicting test data. The solution? Learning to control MSTest&rsquo;s parallelism settings.</p>
<p>This post covers the various options available for controlling parallelism in your MSTest projects, from completely disabling parallel execution to fine-tuning which tests run in parallel and which ones run sequentially.</p>
<h2 id="the-problem-scenario">The Problem Scenario</h2>
<p>Imagine you have a suite of integration tests for a customer management system. You have 20 integration tests that interact with a shared test database. Without parallelism control, MSTest might run all 20 tests simultaneously:</p>
<ul>
<li>Test A creates a customer with email &ldquo;<a href="mailto:test@example.com">test@example.com</a>&rdquo;</li>
<li>Test B simultaneously tries to create the same customer, causing a duplicate key error</li>
<li>Test C queries the database expecting 5 records, but gets 25 because other tests are also inserting data</li>
<li>Test D rolls back a transaction that Test E is still trying to read from</li>
</ul>
<p>The result? Intermittent test failures that are hard to debug and unreliable CI/CD pipelines. By controlling parallelism, you can ensure tests that share resources run sequentially while still leveraging parallel execution where it&rsquo;s safe.</p>
<h2 id="configuration-options">Configuration Options</h2>
<p>MSTest provides two main approaches for configuring parallelism:</p>
<ol>
<li><strong>Per unit test file</strong> - Add the <code>Parallelize</code> attribute directly in your test files</li>
<li><strong>Global configuration</strong> - Set it once in the <code>MSTestSettings.cs</code> configuration file for all tests</li>
</ol>
<h2 id="understanding-the-parallelize-attribute">Understanding the Parallelize Attribute</h2>
<p>The <code>Parallelize</code> attribute controls how tests run in parallel:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#50fa7b">[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)]</span>
</span></span></code></pre></div><h3 id="parameters">Parameters</h3>
<ul>
<li>
<p><strong>Workers</strong>: Specifies the number of threads to run the tests. Set to <code>0</code> to automatically use the number of CPU cores available on your machine. You can also specify an explicit number like <code>Workers = 4</code> to limit parallelism.</p>
</li>
<li>
<p><strong>Scope</strong>: Determines the level at which tests are parallelized:</p>
<ul>
<li><code>ExecutionScope.MethodLevel</code> - Runs all test methods in parallel, regardless of which class they belong to</li>
<li><code>ExecutionScope.ClassLevel</code> - Runs test classes in parallel, but tests within the same class execute sequentially. Use this when tests within a class have interdependencies or shared state.</li>
</ul>
</li>
</ul>
<h3 id="applying-per-test-file">Applying Per Test File</h3>
<p>Add the assembly attribute at the top of 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;-webkit-text-size-adjust:none;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#50fa7b">[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">namespace</span> Test.UnitTests
</span></span><span style="display:flex;"><span>{
</span></span></code></pre></div><h3 id="global-configuration-with-mstestsettingscs">Global Configuration with MSTestSettings.cs</h3>
<p>For project-wide settings, create or update your <code>MSTestSettings.cs</code> 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;-webkit-text-size-adjust:none;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#50fa7b">[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)]</span>
</span></span></code></pre></div><p>This approach keeps parallelization settings consistent across all test files in your project.</p>
<h2 id="running-tests-sequentially">Running Tests Sequentially</h2>
<p>To run all unit tests sequentially, simply remove the <code>Parallelize</code> assembly attribute from either the individual test file or the <code>MSTestSettings.cs</code> file, depending on which tests you want to run sequentially.</p>
<h2 id="mixed-parallelism-running-specific-tests-sequentially">Mixed Parallelism: Running Specific Tests Sequentially</h2>
<p>In some scenarios, you may want most tests to run in parallel, but certain tests need to execute sequentially (e.g., tests that access shared resources or databases). To achieve this:</p>
<ol>
<li>Keep the <code>Parallelize</code> assembly attribute in place</li>
<li>Mark specific test methods with the <code>[DoNotParallelize]</code> attribute</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;-webkit-text-size-adjust:none;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#50fa7b">[TestClass]</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">public</span> <span style="color:#ff79c6">class</span> <span style="color:#50fa7b">DatabaseTests</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">    [TestMethod]</span>
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">    [DoNotParallelize]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">public</span> <span style="color:#8be9fd;font-style:italic">async</span> Task TestDatabaseConnection()
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">// This test will run sequentially</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">
</span></span></span><span style="display:flex;"><span><span style="color:#50fa7b">    [TestMethod]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">public</span> <span style="color:#8be9fd;font-style:italic">async</span> Task TestDataRetrieval()
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">// This test will run in parallel with other parallel tests</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="controlling-test-execution-order">Controlling Test Execution Order</h2>
<p>While the best practice is to write independent unit tests that don&rsquo;t rely on execution order, there are legitimate cases where ordering is necessary, such as:</p>
<ul>
<li>Integration tests with setup/teardown dependencies</li>
<li>Tests that validate a sequence of operations</li>
<li>End-to-end scenarios that must execute in a specific order</li>
</ul>
<p>For more details on ordering tests, see the <a href="https://learn.microsoft.com/en-us/dotnet/core/testing/order-unit-tests?pivots=mstest">official Microsoft documentation on ordering unit tests</a>.</p>
<h3 id="ensuring-consistent-ordering">Ensuring Consistent Ordering</h3>
<p>To guarantee that test ordering works consistently in both Visual Studio Test Explorer and command-line test runs, add a <code>.runsettings</code> file to the root of your test project:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#ff79c6">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">&lt;RunSettings&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&lt;MSTest&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;OrderTestsByNameInClass&gt;</span>true<span style="color:#ff79c6">&lt;/OrderTestsByNameInClass&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&lt;/MSTest&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">&lt;/RunSettings&gt;</span>
</span></span></code></pre></div><p>This ensures tests within a class are ordered alphabetically by method name. You can then control order by naming your test methods accordingly (e.g., <code>Test01_FirstStep</code>, <code>Test02_SecondStep</code>).</p>
<h2 id="best-practices">Best Practices</h2>
<ol>
<li><strong>Default to parallel execution</strong> for unit tests to maximize performance</li>
<li><strong>Apply <code>[DoNotParallelize]</code></strong> selectively to tests where there are specific dependencies</li>
<li><strong>Avoid test ordering</strong> when possible by ensuring tests are independent</li>
<li><strong>Monitor test execution time</strong> and adjust the <code>Workers</code> parameter if needed</li>
</ol>
<h2 id="conclusion">Conclusion</h2>
<p>MSTest provides flexible options for controlling test parallelism, allowing you to balance execution speed with reliability.
Start with parallel execution at the method level, and only introduce restrictions where necessary based on your specific test requirements.</p>
<p>Hope this helps and Happy Testing.</p>
]]></content:encoded></item><item><title>Azure Logic Apps Standard | Send Custom Events to Application Insights</title><link>https://andrewilson.co.uk/post/2026/02/logic-app-standard-send-custom-events-application-insights/</link><pubDate>Tue, 03 Feb 2026 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2026/02/logic-app-standard-send-custom-events-application-insights/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;When building integration workflows with Azure Logic Apps Standard, there&amp;rsquo;s often a need to track custom business events that sit between pure technical telemetry and business process monitoring. Recently, while authoring a Logic App Standard workflow, I needed to track the total number of items processed along with a breakdown of successful versus failed processing attempts.&lt;/p&gt;
&lt;p&gt;While Logic Apps provides excellent run history and built-in diagnostics, custom events allow you to capture specific business actions and metrics that align with your reporting requirements. In this post, I&amp;rsquo;ll show you how to send custom events directly to Azure Application Insights using the ingestion endpoint from the Application Insights connection string.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>When building integration workflows with Azure Logic Apps Standard, there&rsquo;s often a need to track custom business events that sit between pure technical telemetry and business process monitoring. Recently, while authoring a Logic App Standard workflow, I needed to track the total number of items processed along with a breakdown of successful versus failed processing attempts.</p>
<p>While Logic Apps provides excellent run history and built-in diagnostics, custom events allow you to capture specific business actions and metrics that align with your reporting requirements. In this post, I&rsquo;ll show you how to send custom events directly to Azure Application Insights using the ingestion endpoint from the Application Insights connection string.</p>
<h2 id="the-use-case">The Use Case</h2>
<p>I needed to answer questions like:</p>
<ul>
<li>How many items were processed in total for a workflow run?</li>
<li>What&rsquo;s the success vs. failure rate for different item types?</li>
<li>Can I correlate these metrics with other application telemetry?</li>
</ul>
<p>Standard Logic Apps diagnostics don&rsquo;t easily provide this level of custom business metric tracking, making Application Insights custom events the perfect solution.</p>
<h2 id="understanding-the-application-insights-ingestion-endpoint">Understanding the Application Insights Ingestion Endpoint</h2>
<p>The Application Insights connection string contains several components, including the ingestion endpoint. It looks something like this:</p>
<pre tabindex="0"><code>InstrumentationKey=&lt;key&gt;;IngestionEndpoint=https://&lt;region&gt;.in.applicationinsights.azure.com/;LiveEndpoint=https://&lt;region&gt;.livediagnostics.monitor.azure.com/;ApplicationId=&lt;ID&gt;

// Broken Down
//------------
InstrumentationKey=&lt;key&gt;

IngestionEndpoint=https://&lt;region&gt;.in.applicationinsights.azure.com/

LiveEndpoint=https://&lt;region&gt;.livediagnostics.monitor.azure.com/

ApplicationId=&lt;ID&gt;
</code></pre><p>The Ingestion Endpoint component as the name suggests is used for the ingest of telemetry/metrics/events/etc. The component points to a regional Application Insights endpoint with the Instrumentation key supplied later in in the ingestion request for targeting your specific Application Insights instance.</p>
<p>The missing part to this endpoint that allows you to post your telemetry is a trailing <code>v2/track</code>. Combined this appears as follows:
<code>https://&lt;region&gt;.in.applicationinsights.azure.com/v2/track</code></p>
<p>We&rsquo;ll use the <code>IngestionEndpoint</code> combined with the <code>InstrumentationKey</code> to send custom events via HTTP requests.</p>
<h2 id="implementation">Implementation</h2>
<h3 id="step-1-configure-application-insights-connection-in-bicep">Step 1: Configure Application Insights Connection in Bicep</h3>
<p>First, we need to extract the ingestion endpoint and instrumentation key from Application Insights and make them available to our Logic App as application settings:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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">resource</span> <span style="color:#8be9fd;font-style:italic">applicationInsights</span> <span style="color:#f1fa8c">&#39;Microsoft.Insights/components@2020-02-02&#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">applicationInsightsName</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">logicAppDeployment</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/sites@2025-03-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">kind</span>: <span style="color:#f1fa8c">&#39;functionapp,workflowapp&#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">siteConfig</span>: {
</span></span><span style="display:flex;"><span>      ...
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">appSettings</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;appInsightsIngestUrl&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#8be9fd;font-style:italic">value</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#50fa7b">split</span>(<span style="color:#50fa7b">filter</span>(<span style="color:#50fa7b">split</span>(<span style="color:#8be9fd;font-style:italic">applicationInsights</span>.<span style="color:#8be9fd;font-style:italic">properties</span>.<span style="color:#8be9fd;font-style:italic">ConnectionString</span>, <span style="color:#f1fa8c">&#39;;&#39;</span>), <span style="color:#8be9fd;font-style:italic">val</span> =&gt; <span style="color:#50fa7b">contains</span>(<span style="color:#8be9fd;font-style:italic">val</span>, <span style="color:#f1fa8c">&#39;IngestionEndpoint=&#39;</span>))[<span style="color:#8be9fd;font-style:italic">0</span>], <span style="color:#f1fa8c">&#39;=&#39;</span>)[<span style="color:#8be9fd;font-style:italic">1</span>]<span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">v2/track&#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;appInsightsKey&#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">applicationInsights</span>.<span style="color:#8be9fd;font-style:italic">properties</span>.<span style="color:#8be9fd;font-style:italic">InstrumentationKey</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>The Bicep code above parses the connection string to extract the ingestion endpoint and appends the required <code>v2/track</code> path. It also captures the instrumentation key needed to point to your instance.</p>
<h3 id="step-2-create-logic-app-parameters">Step 2: Create Logic App Parameters</h3>
<p>Add parameters to your Logic App Workflow&rsquo;s <code>parameters.json</code> file to reference these application settings:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-Json" data-lang="Json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;AppInsightsIngestionUrl&#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;value&#34;</span>: <span style="color:#f1fa8c">&#34;@appsetting(&#39;appInsightsIngestUrl&#39;)&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;AppInsightsKey&#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;value&#34;</span>: <span style="color:#f1fa8c">&#34;@appsetting(&#39;appInsightsKey&#39;)&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>These parameters allow your workflow to access the ingestion URL and key at runtime.</p>
<h3 id="step-3-add-the-workflow-actions-and-event-payload">Step 3: Add the Workflow Actions and Event Payload</h3>
<p>In your Logic App workflow, use a <strong>Compose</strong> action to build the Application Insights event payload. The payload follows the Application Insights telemetry schema:</p>
<p>
  <img src="/images/posts/2026/02/CustomEventsLGActions.png" alt="CustomEventsLGActions">

</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-Json" data-lang="Json"><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;Microsoft.ApplicationInsights.@{parameters(&#39;AppInsightsKey&#39;)}.Event&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;time&#34;</span>: <span style="color:#f1fa8c">&#34;@{utcNow()}&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;iKey&#34;</span>: <span style="color:#f1fa8c">&#34;@{parameters(&#39;AppInsightsKey&#39;)}&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&#34;data&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;baseType&#34;</span>: <span style="color:#f1fa8c">&#34;EventData&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;baseData&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;ver&#34;</span>: <span style="color:#f1fa8c">&#34;2&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;LogicAppWorkflowName&#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;workflowRunId&#34;</span>: <span style="color:#f1fa8c">&#34;@{workflow().run.name}&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;ItemsProcessed&#34;</span>: <span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;Type1Count&#34;</span>:  <span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;Type1CountFailures&#34;</span>:  <span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;Type2Count&#34;</span>:  <span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;Type2CountFailures&#34;</span>:  <span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;Type3Count&#34;</span>:  <span style="color:#f1fa8c">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;Type3CountFailures&#34;</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><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Key components of the payload:</p>
<ul>
<li><strong>name</strong>: Event identifier following Application Insights naming convention</li>
<li><strong>time</strong>: Timestamp in UTC format</li>
<li><strong>iKey</strong>: Your instrumentation key</li>
<li><strong>data.baseData.name</strong>: The custom event name that will appear in Application Insights</li>
<li><strong>data.baseData.properties</strong>: Your custom properties containing business metrics</li>
</ul>
<h3 id="step-4-send-the-event-via-http">Step 4: Send the Event via HTTP</h3>
<p>Add an <strong>HTTP</strong> action to send the composed payload to Application Insights:</p>
<p>
  <img src="/images/posts/2026/02/CustomEventsLGHTTP.png" alt="CustomEventsLGHTTPAction">

</p>
<p>Configure the HTTP action with:</p>
<ul>
<li><strong>Method</strong>: POST</li>
<li><strong>URI</strong>: <code>@{parameters('AppInsightsIngestionUrl')}</code></li>
<li><strong>Headers</strong>: <code>Content-Type: application/json</code></li>
<li><strong>Body</strong>: Output from the Compose action</li>
</ul>
<p>The HTTP action will receive a 200 OK response when the event is successfully ingested.</p>
<p>The response body should indicate a successful ingestion 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;-webkit-text-size-adjust:none;"><code class="language-Json" data-lang="Json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;itemsReceived&#34;</span>: <span style="color:#bd93f9">1</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;itemsAccepted&#34;</span>: <span style="color:#bd93f9">1</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;errors&#34;</span>: []
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="step-5-query-your-events">Step 5: Query Your Events</h3>
<p>After your workflow runs, navigate to Application Insights and use the following KQL query to view your custom events:</p>
<pre tabindex="0"><code class="language-kusto" data-lang="kusto">customEvents
| where name == &#34;LogicAppWorkflowName&#34;
| project timestamp, 
          workflowRunId = tostring(customDimensions.workflowRunId),
          itemsProcessed = toint(customDimensions.ItemsProcessed),
          type1Success = toint(customDimensions.Type1Count),
          type1Failures = toint(customDimensions.Type1CountFailures)
| order by timestamp desc
</code></pre><p>You can create custom dashboards, alerts, and reports based on these events.</p>
<h2 id="summary">Summary</h2>
<p>By leveraging the Application Insights ingestion endpoint directly via HTTP actions, you can easily track custom business metrics from your Logic Apps Standard workflows without external dependencies. This approach lets you correlate workflow execution with wider integration components/applications and build comprehensive dashboards based on your specific use case.</p>
<p>Hope this helps, and keep Workflow-ing!</p>
]]></content:encoded></item><item><title>Azure Function Apps | OkObjectResult Returns Empty JSON After Moving to .NET 9 Isolated Worker Runtime</title><link>https://andrewilson.co.uk/post/2025/12/azure-function-apps-okobjectresult-returning-empty-json-object/</link><pubDate>Wed, 03 Dec 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/12/azure-function-apps-okobjectresult-returning-empty-json-object/</guid><description>&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;I recently upgraded an Azure Function from .NET 8 to .NET 9, and at the same time migrated from the in-process worker to the isolated worker model. After the upgrade, my function that returned &lt;code&gt;OkObjectResult&lt;/code&gt; started returning an empty JSON object &lt;code&gt;{}&lt;/code&gt; instead of the expected data.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#50fa7b"&gt;[Function(&amp;#34;MyFunction&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;public&lt;/span&gt; IActionResult Run([HttpTrigger(AuthorizationLevel.Function, &lt;span style="color:#f1fa8c"&gt;&amp;#34;post&amp;#34;&lt;/span&gt;)] HttpRequest req)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8be9fd"&gt;var&lt;/span&gt; data = &lt;span style="color:#ff79c6"&gt;new&lt;/span&gt; MyResponse { Name = &lt;span style="color:#f1fa8c"&gt;&amp;#34;Test&amp;#34;&lt;/span&gt;, Value = &lt;span style="color:#bd93f9"&gt;123&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;return&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;new&lt;/span&gt; OkObjectResult(data); &lt;span style="color:#6272a4"&gt;// Returns {} instead of expected JSON&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="why-this-happens"&gt;Why This Happens&lt;/h2&gt;
&lt;p&gt;There are two key changes that caused this issue:&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-problem">The Problem</h2>
<p>I recently upgraded an Azure Function from .NET 8 to .NET 9, and at the same time migrated from the in-process worker to the isolated worker model. After the upgrade, my function that returned <code>OkObjectResult</code> started returning an empty JSON object <code>{}</code> instead of the expected data.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#50fa7b">[Function(&#34;MyFunction&#34;)]</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">public</span> IActionResult Run([HttpTrigger(AuthorizationLevel.Function,  <span style="color:#f1fa8c">&#34;post&#34;</span>)] HttpRequest req)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd">var</span> data = <span style="color:#ff79c6">new</span> MyResponse { Name = <span style="color:#f1fa8c">&#34;Test&#34;</span>, Value = <span style="color:#bd93f9">123</span> };
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">return</span> <span style="color:#ff79c6">new</span> OkObjectResult(data); <span style="color:#6272a4">// Returns {} instead of expected JSON</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="why-this-happens">Why This Happens</h2>
<p>There are two key changes that caused this issue:</p>
<h3 id="1-in-process-to-isolated-worker-migration">1. In-Process to Isolated Worker Migration</h3>
<p>The isolated worker process runs in a separate process from the Functions host. Unlike the in-process model, it doesn&rsquo;t inherit any configuration or serialization settings. You&rsquo;re starting with a clean slate and need to explicitly configure everything.</p>
<h3 id="2-json-serializer-change">2. JSON Serializer Change</h3>
<p>Starting with <a href="https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-3.0?view=aspnetcore-9.0#new-json-serialization">ASP.NET Core 3.0</a>, Microsoft replaced Newtonsoft.Json with System.Text.Json as the default serializer. While both serialize JSON, they have different behaviors:</p>
<ul>
<li><strong>Newtonsoft.Json</strong>: More lenient, serializes public properties and fields</li>
<li><strong>System.Text.Json</strong>: Stricter, only serializes public properties by default</li>
</ul>
<p>When using <code>OkObjectResult</code> (an ASP.NET Core MVC type) in an isolated worker, you need to explicitly configure MVC services. Without this configuration, the serialization doesn&rsquo;t work as expected.</p>
<h2 id="the-solutions">The Solutions</h2>
<p>You have two approaches to fix this, each with different trade-offs:</p>
<h3 id="option-1-use-native-isolated-worker-types-recommended">Option 1: Use Native Isolated Worker Types (Recommended)</h3>
<p>Embrace the isolated worker model by using the native types designed for it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#50fa7b">[Function(&#34;MyFunction&#34;)]</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">public</span> <span style="color:#8be9fd;font-style:italic">async</span> Task&lt;HttpResponseData&gt; Run([HttpTrigger(AuthorizationLevel.Function, <span style="color:#f1fa8c">&#34;post&#34;</span>)] HttpRequestData req)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd">var</span> data = <span style="color:#ff79c6">new</span> MyResponse { Name = <span style="color:#f1fa8c">&#34;Test&#34;</span>, Value = <span style="color:#bd93f9">123</span> };
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd">var</span> response = req.CreateResponse(HttpStatusCode.OK);
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">await</span> response.WriteAsJsonAsync(data); <span style="color:#6272a4">// Uses System.Text.Json by default</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">return</span> response;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>Advantages:</strong></p>
<ul>
<li><strong>Better Performance</strong>: System.Text.Json is significantly faster than Newtonsoft.Json</li>
<li><strong>Lower Costs</strong>: Reduced latency means less execution time, which directly reduces Azure Functions costs (billed per execution time)</li>
<li><strong>Lighter Dependencies</strong>: No need for additional MVC services or packages</li>
<li><strong>Future-Proof</strong>: Aligned with the modern .NET isolated worker architecture</li>
<li><strong>Native Support</strong>: Uses types specifically designed for isolated workers</li>
</ul>
<p><strong>Disadvantages:</strong></p>
<ul>
<li>Requires code changes to migrate from <code>IActionResult</code> to <code>HttpResponseData</code></li>
<li>May need adjustments if you rely on specific Newtonsoft.Json features</li>
</ul>
<h3 id="option-2-add-mvc-services-with-newtonsoftjson-quick-fix">Option 2: Add MVC Services with Newtonsoft.Json (Quick Fix)</h3>
<p>If you need a quick fix or have complex serialization requirements, you can <a href="https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide?tabs=ihostapplicationbuilder%2Cwindows#json-serialization-with-aspnet-core-integration">add MVC services with Newtonsoft.Json support</a> to your <code>Program.cs</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;-webkit-text-size-adjust:none;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#ff79c6">using</span> Microsoft.Extensions.Hosting;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd">var</span> builder = FunctionsApplication.CreateBuilder(args);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Add MVC services and configure Newtonsoft.Json</span>
</span></span><span style="display:flex;"><span>builder.Services.AddMvc().AddNewtonsoftJson();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>builder.Build().Run();
</span></span></code></pre></div><p>You&rsquo;ll also need to add the NuGet package:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson
</span></span></code></pre></div><p><strong>Advantages:</strong></p>
<ul>
<li>Minimal code changes - keeps existing <code>OkObjectResult</code> code working</li>
<li>Maintains backward compatibility with Newtonsoft.Json serialization behavior</li>
<li>Good for quick migration when you have tight deadlines</li>
</ul>
<p><strong>Disadvantages:</strong></p>
<ul>
<li><strong>Higher Costs</strong>: Slower serialization means longer execution time and higher Azure Functions bills</li>
<li><strong>Additional Dependencies</strong>: Requires MVC framework which adds overhead</li>
<li><strong>Technical Debt</strong>: You&rsquo;re opting back into older patterns instead of embracing the new architecture</li>
</ul>
<h2 id="which-should-you-choose">Which Should You Choose?</h2>
<p><strong>For new projects or if you can afford the refactoring time</strong>: Go with Option 1 (native types). The performance benefits and cost savings will compound over time, especially for high-traffic functions.</p>
<p><strong>For quick migrations with tight deadlines</strong>: Option 2 can get you unstuck quickly, but consider it temporary. Plan to refactor to native types when time permits.</p>
<h2 id="takeaway">Takeaway</h2>
<p>When migrating to .NET 9 isolated worker Functions, you&rsquo;re working in a fresh environment that requires explicit configuration. While adding Newtonsoft.Json gets things working quickly, embracing the native isolated worker types with System.Text.Json offers better performance and lower costs (⚠️important factors when Azure Functions are billed per execution time). Choose the approach that balances your immediate needs with long-term architecture goals.</p>
<p>Hope this helps, Happy Coding.</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>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Building on our previous exploration of &lt;a href="https://andrewilson.co.uk/post/2025/08/bicep-tips-and-tricks-typed-variables/"&gt;Typed Variables&lt;/a&gt;, today we&amp;rsquo;re diving into one of my favorite patterns for creating maintainable and reusable Bicep templates: the &lt;strong&gt;Shared Variable File Pattern&lt;/strong&gt;. This approach transforms your templates from being tightly coupled to specific configurations into truly agnostic, environment-ready solutions.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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></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:#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></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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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></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></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></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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;In late &lt;a href="https://github.com/Azure/bicep/releases/tag/v0.36.1"&gt;May this year&lt;/a&gt;, an exciting but semi overlooked feature was released, and I absolutely love it - &lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/variables#typed-variables"&gt;Typed Variables&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h2 id="why-use-typed-variables"&gt;Why Use Typed Variables&lt;/h2&gt;
&lt;p&gt;Before I get going, let me walk through why you should consider using typed variables:&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;This week is a simple one, but works wonders in maintainability and consistency.&lt;/p&gt;
&lt;p&gt;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:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Multi-Environment Deployments&lt;/p&gt;
&lt;p&gt;Different environments (dev, staging, prod) that share common configuration but need environment-specific values:&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is a Naming Convention and Why Do We Need One?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;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;&lt;em&gt;Core Parameters&lt;/em&gt;&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. These may include but are not limited to:&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="the-first-of-many"&gt;The first of many&lt;/h2&gt;
&lt;p&gt;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. From one Bicep nerd to another, I hope you find these tips useful, happy templating!&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>Automating web.config Environment Transforms in Azure DevOps Pipelines for App Services</title><link>https://andrewilson.co.uk/post/2025/07/webconfig-environment-transform-azure-devops-app-service/</link><pubDate>Wed, 02 Jul 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/07/webconfig-environment-transform-azure-devops-app-service/</guid><description>&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;Modernising legacy applications is always a journey, and recently I tackled moving an existing WCF web service into Azure App Service. If you’ve worked with WCF, you’ll know the &lt;code&gt;web.config&lt;/code&gt; file is the nerve centre, handling everything from parameters to connection strings.&lt;/p&gt;
&lt;p&gt;But here’s the catch, this service needs to run in multiple environments (Development, Test, and Production) each with its own unique settings. That means multiple config files:&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>Modernising legacy applications is always a journey, and recently I tackled moving an existing WCF web service into Azure App Service. If you’ve worked with WCF, you’ll know the <code>web.config</code> file is the nerve centre, handling everything from parameters to connection strings.</p>
<p>But here’s the catch, this service needs to run in multiple environments (Development, Test, and Production) each with its own unique settings. That means multiple config files:</p>
<ul>
<li>Web.Dev.Config</li>
<li>Web.Test.Config</li>
<li>Web.Production.Config</li>
</ul>
<h2 id="the-challenge">The challenge</h2>
<p>I didn’t want to build and maintain three separate web packages, one for each environment. That’s a recipe for drift and deployment headaches. Instead, I wanted a single build artifact that could be promoted through environments, swapping out only the environment-specific config as needed.</p>
<p>Enter <a href="https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/transform-webconfig?view=aspnetcore-9.0">web.config transformations</a>. With these, you can:</p>
<ul>
<li>Keep a base <code>web.config</code></li>
<li>Layer on environment-specific transforms (<code>Web.Dev.Config</code>, etc.)</li>
</ul>
<p>But when should the transformation happen? There are two main options, each with its own pros and cons:</p>
<ol>
<li>
<p><strong>At build time (using MSBuild):</strong></p>
<ul>
<li>The transformation is applied as part of the build process, so the output artifact already contains the environment-specific configuration.</li>
<li>This approach is simple if you only ever deploy to one environment per build, but it means you need to build a separate artifact for each environment. This breaks the principle of binary promotion, where the same artifact is promoted through Dev, Test, and Production. It also increases the risk of inconsistencies between builds.</li>
</ul>
</li>
<li>
<p><strong>At deployment (using the AzureRMWebAppDeployment@4 Azure DevOps Task):</strong></p>
<ul>
<li>The transformation is applied as part of the deployment process, allowing you to use a single, environment-agnostic build artifact and inject the correct configuration at deploy time.</li>
<li>This is the preferred approach for most modern pipelines, as it enables true binary promotion, reduces build times, and ensures that what you test is exactly what you deploy to production. It also aligns with best practices for repeatable, reliable deployments.</li>
</ul>
</li>
</ol>
<h2 id="solution-deploy-time-transforms">Solution: deploy-time transforms</h2>
<p>Here’s how I set it up:</p>
<h3 id="build-pipeline">Build pipeline</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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 style="color:#ff79c6">stages</span>:
</span></span><span style="display:flex;"><span>- <span style="color:#ff79c6">stage</span>: <span style="color:#f1fa8c">&#39;Build_Packages&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">jobs</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">job</span>: <span style="color:#f1fa8c">&#39;Build_WCFService&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">checkout</span>: self
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchDepth</span>: <span style="color:#bd93f9">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: VSBuild@1
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Build the WCF Service Solution&#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">solution</span>: <span style="color:#f1fa8c">&#39;$(Build.SourcesDirectory)\*.sln&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">msbuildArgs</span>: <span style="color:#f1fa8c">&#39;/p:OutputPath=$(Build.ArtifactStagingDirectory)\WCFService /p:IsTransformWebConfigDisabled=true&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">platform</span>: <span style="color:#f1fa8c">&#39;any cpu&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">vsVersion</span>: <span style="color:#f1fa8c">&#39;latest&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">configuration</span>: <span style="color:#f1fa8c">&#39;Release&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">clean</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>: ArchiveFiles@2
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Archive WCF Service Build Output&#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">rootFolderOrFile</span>: <span style="color:#f1fa8c">&#39;$(Build.ArtifactStagingDirectory)\WCFService\_PublishedWebsites\WCFService.Web&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">includeRootFolder</span>: <span style="color:#ff79c6">false</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">archiveType</span>: <span style="color:#f1fa8c">&#39;zip&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">archiveFile</span>: <span style="color:#f1fa8c">&#39;$(Build.ArtifactStagingDirectory)\WCFService\WCFService.zip&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">replaceExistingArchive</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 WCF Service 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)\WCFService\WCFService.zip&#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;WCFService&#39;</span>
</span></span></code></pre></div><h3 id="release-pipeline">Release pipeline</h3>
<p>Each environment (Dev, Test, Production) will get its own stage. The key is to ensure the correct environment name is set so the right transform is applied.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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 style="color:#ff79c6">stage</span>: <span style="color:#f1fa8c">&#39;Dev&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy to Dev Environment&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">variables</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">group</span>: <span style="color:#f1fa8c">&#39;Dev&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">dependsOn</span>: <span style="color:#f1fa8c">&#39;Build_Packages&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">condition</span>: succeeded()
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">jobs</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">deployment</span>: Deploy_to_Dev
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">environment</span>: WCFServiceDev
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">strategy</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">runOnce</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">deploy</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>              - <span style="color:#ff79c6">template</span>: EnvironmentDeploy.azurepipelinetemplate.yml
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">parameters</span>:
</span></span><span style="display:flex;"><span>                  <span style="color:#ff79c6">ARMConn</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:#ff79c6">stage</span>: <span style="color:#f1fa8c">&#39;Test&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy to Test Environment&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">variables</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">group</span>: <span style="color:#f1fa8c">&#39;Test&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">dependsOn</span>: <span style="color:#f1fa8c">&#39;Dev&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">condition</span>: succeeded()
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">jobs</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">deployment</span>: Deploy_to_Test
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">environment</span>: WCFServiceTest
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">strategy</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">runOnce</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">deploy</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>              - <span style="color:#ff79c6">template</span>: EnvironmentDeploy.azurepipelinetemplate.yml
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">parameters</span>:
</span></span><span style="display:flex;"><span>                  <span style="color:#ff79c6">ARMConn</span>: <span style="color:#f1fa8c">&#39;Test&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- <span style="color:#ff79c6">stage</span>: <span style="color:#f1fa8c">&#39;Production&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy to Production Environment&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">variables</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">group</span>: <span style="color:#f1fa8c">&#39;Prod&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">dependsOn</span>: <span style="color:#f1fa8c">&#39;Test&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">condition</span>: succeeded()
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">jobs</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">deployment</span>: Deploy_to_Production
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">environment</span>: WCFServiceProd
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">strategy</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">runOnce</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">deploy</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>              - <span style="color:#ff79c6">template</span>: EnvironmentDeploy.azurepipelinetemplate.yml
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">parameters</span>:
</span></span><span style="display:flex;"><span>                  <span style="color:#ff79c6">ARMConn</span>: <span style="color:#f1fa8c">&#39;Production&#39;</span>
</span></span></code></pre></div><h3 id="deployment-template">Deployment template</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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 style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">download</span>: current
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Download the wcf Service Web package&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">artifact</span>: WCFService
</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>: AzureRMWebAppDeployment@4
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy WCF Service Web App&#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">ConnectionType</span>: <span style="color:#f1fa8c">&#39;AzureRM&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">azureSubscription</span>: ${{ parameters.ARMConn }}
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">appType</span>: <span style="color:#f1fa8c">&#39;webApp&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">WebAppName</span>: <span style="color:#f1fa8c">&#39;$(armOutput.AppServiceName)&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">Package</span>: <span style="color:#f1fa8c">&#39;$(Pipeline.Workspace)/WCFService/WCFService.zip&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">enableXmlTransform</span>: <span style="color:#ff79c6">true</span>
</span></span></code></pre></div><p>Here’s the bit that tripped me up: in multi-stage pipelines, the <code>Release.EnvironmentName</code> variable isn’t set by default. Without it, Azure DevOps doesn’t know which transform to apply. The fix? Explicitly set the variable in each stage:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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 style="color:#ff79c6">stage</span>: <span style="color:#f1fa8c">&#39;Dev&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy to Dev Environment&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">variables</span>:
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">name</span>: <span style="color:#f1fa8c">&#39;Release.EnvironmentName&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">value</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 style="color:#ff79c6">stage</span>: <span style="color:#f1fa8c">&#39;Test&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy to Test Environment&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">variables</span>:
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">name</span>: <span style="color:#f1fa8c">&#39;Release.EnvironmentName&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">value</span>: <span style="color:#f1fa8c">&#39;Test&#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:#ff79c6">stage</span>: <span style="color:#f1fa8c">&#39;Production&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy to Production Environment&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">variables</span>:
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">name</span>: <span style="color:#f1fa8c">&#39;Release.EnvironmentName&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">value</span>: <span style="color:#f1fa8c">&#39;Production&#39;</span>
</span></span><span style="display:flex;"><span>  ...
</span></span></code></pre></div><h2 id="troubleshooting-and-tips">Troubleshooting and tips</h2>
<p><strong>Common pitfalls:</strong></p>
<ul>
<li>Ensure your transform files (e.g., <code>Web.Dev.config</code>) are included in your build artifacts. If they’re missing, check your <code>.csproj</code> or project file to confirm they’re marked as <code>Content</code> and set to <code>Copy to Output Directory</code> if needed.</li>
<li>File names are case-sensitive on some systems, double-check spelling and casing.</li>
<li>If transforms aren’t being applied, verify that the <code>Release.EnvironmentName</code> variable is set correctly and matches your transform file naming.</li>
</ul>
<p><strong>Security note:</strong>
Avoid storing secrets or sensitive values directly in your config files. Use Azure Key Vault or pipeline secrets for sensitive data, and reference them via environment variables or pipeline variables where possible.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>With this approach, you get a single, reusable build artifact and environment-specific configuration at deployment, no more juggling multiple packages. It’s a small change that makes your pipeline cleaner and your deployments more reliable.</p>
<p>Hope this helps, happy deploying!</p>
]]></content:encoded></item><item><title>How to Set Up Manual Approval for Azure App Service Slot Swaps in Azure DevOps Pipelines</title><link>https://andrewilson.co.uk/post/2025/06/manual-approval-azure-app-service-slot-swap-azure-devops/</link><pubDate>Fri, 06 Jun 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/06/manual-approval-azure-app-service-slot-swap-azure-devops/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Deploying updates to production environments demands both speed and control. Azure App Service deployment slots, combined with Azure DevOps pipelines, offer a powerful way to manage releases, enabling teams to validate changes in a live-like environment before they go public. However, ensuring that only reviewed and approved changes reach your users is critical for maintaining reliability and compliance.&lt;/p&gt;
&lt;p&gt;Azure App Service Slots unlock powerful advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Site Review&lt;/strong&gt;. Preview and test your site before it goes live, ensuring everything works as expected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Warmed-up Instances&lt;/strong&gt;. Swap with confidence. Your instances are pre-warmed, so there’s no downtime, seamless traffic redirection, and zero dropped requests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instant Rollback&lt;/strong&gt;. If something goes wrong after a swap, simply swap back to restore your last known good version in seconds.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Best of all, deployment slots come at no extra cost. Check the &lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits#azure-app-service-limits"&gt;Azure App Service limits&lt;/a&gt; to see how many slots your App Service Tier supports.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>Deploying updates to production environments demands both speed and control. Azure App Service deployment slots, combined with Azure DevOps pipelines, offer a powerful way to manage releases, enabling teams to validate changes in a live-like environment before they go public. However, ensuring that only reviewed and approved changes reach your users is critical for maintaining reliability and compliance.</p>
<p>Azure App Service Slots unlock powerful advantages:</p>
<ul>
<li><strong>Site Review</strong>. Preview and test your site before it goes live, ensuring everything works as expected.</li>
<li><strong>Warmed-up Instances</strong>. Swap with confidence. Your instances are pre-warmed, so there’s no downtime, seamless traffic redirection, and zero dropped requests.</li>
<li><strong>Instant Rollback</strong>. If something goes wrong after a swap, simply swap back to restore your last known good version in seconds.</li>
</ul>
<p>Best of all, deployment slots come at no extra cost. Check the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits#azure-app-service-limits">Azure App Service limits</a> to see how many slots your App Service Tier supports.</p>
<h2 id="automatic-vs-manual-slot-swaps">Automatic vs. Manual Slot Swaps</h2>
<p>When it comes to swapping slots, there are two main approaches: <strong>automatic</strong> and <strong>manual</strong>.</p>
<ul>
<li><strong>Automatic swaps</strong> are ideal when you want to quickly promote changes without a review, while still benefiting from pre-warmed instances and instant rollback.</li>
<li><strong>Manual swaps</strong> give you the opportunity to review and verify your site before it goes live, adding an extra layer of confidence.</li>
</ul>
<p>But how do you keep a manual swap process governed and controlled? That’s where Azure DevOps pipelines and approvals come in.</p>
<p>In this article, you’ll learn how to set up a robust, approval-driven slot swap process in Azure DevOps.</p>
<h2 id="defining-azure-resources-with-bicep">Defining Azure Resources with Bicep</h2>
<p>First, we need to define the resources needed in Azure. To do this I am going to use Infrastructure as Code (IaC).
The template shown below creates the following resources:</p>
<ul>
<li>App Service Plan.</li>
<li>App Service linked to the App Service Plan
<ul>
<li>Sub Resource that defines the App Service App Settings</li>
</ul>
</li>
<li>App Service Deployment Slot. <em>Only created for Production environments - this is enforced through a conditional on the resource</em></li>
</ul>
<h3 id="bicep-template-app-service-and-deployment-slot">Bicep Template: App Service and Deployment Slot</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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">App</span> <span style="color:#8be9fd;font-style:italic">Service</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">// ** 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;Org Project 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">projectName</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;Application Name sitting within the project&#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">applicationName</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 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">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 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">env</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;App Service Plan sku 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">appServicePlanSkuName</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;App Service Plan sku tier&#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">appServicePlanSkuTier</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;Name of the deployment slot&#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">deploymentSlotName</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;staging&#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">appServicePlanName</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;asp-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">env</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 style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">appServiceName</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#f1fa8c">&#39;app-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">env</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 style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Deploy an Azure App Service Plan&#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">AppServicePlan</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/serverfarms@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">appServicePlanName</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 style="color:#8be9fd;font-style:italic">project</span>: <span style="color:#8be9fd;font-style:italic">projectName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">application</span>: <span style="color:#8be9fd;font-style:italic">applicationName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 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:#8be9fd;font-style:italic">appServicePlanSkuName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">tier</span>: <span style="color:#8be9fd;font-style:italic">appServicePlanSkuTier</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 an Azure App Service&#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">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 style="color:#8be9fd;font-style:italic">project</span>: <span style="color:#8be9fd;font-style:italic">projectName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">application</span>: <span style="color:#8be9fd;font-style:italic">applicationName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">serverFarmId</span>: <span style="color:#8be9fd;font-style:italic">AppServicePlan</span>.<span style="color:#8be9fd;font-style:italic">id</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">httpsOnly</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">resource</span> <span style="color:#8be9fd;font-style:italic">AppServiceAppSettings</span> <span style="color:#f1fa8c">&#39;config@2024-11-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></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 a deployment slot for the App Service&#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">deploymentSlot</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/sites/slots@2024-11-01&#39;</span> = <span style="color:#8be9fd;font-style:italic">if</span> (<span style="color:#8be9fd;font-style:italic">env</span> <span style="color:#ff79c6">==</span> <span style="color:#f1fa8c">&#39;prod&#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">deploymentSlotName</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">AppService</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 style="color:#8be9fd;font-style:italic">project</span>: <span style="color:#8be9fd;font-style:italic">projectName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">application</span>: <span style="color:#8be9fd;font-style:italic">applicationName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 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 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:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Outputs the App Service Name&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">output</span> <span style="color:#8be9fd;font-style:italic">appServiceName</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#8be9fd;font-style:italic">appServiceName</span>
</span></span></code></pre></div><h3 id="important-note-avoiding-autoswap-for-manual-swaps">Important Note: Avoiding AutoSwap for Manual Swaps</h3>
<p>Make sure that the DeploymentSlot resource does not have AutoSwap setup as this will counteract when Manual Swap is used. Such that the following process would be observed:</p>
<ol>
<li>Deploy to Staging slot.</li>
<li>AutoSwap kicks in and swaps Staging with Production.</li>
<li>After Manual Approval, Slots are swapped again</li>
<li>You are now in the originating position with the new site still in Staging and Production with the old.</li>
</ol>
<h2 id="designing-the-azure-devops-pipeline">Designing the Azure DevOps Pipeline</h2>
<p>Next we will setup our Azure DevOps Yaml pipeline. The pipeline’s goal is to orchestrate artifact building and environment deployments while adhering to DevOps best practices, such as <strong>binary promotion</strong>.</p>
<h3 id="pipeline-stages-explained">Pipeline Stages Explained</h3>
<p>The pipeline has the following stages:</p>
<ol>
<li>
<p><strong>Build_Packages</strong>. Build a single set of artifacts that have been tested and versioned. (<em>Best practice of Binary Promotion</em>)</p>
<p>The stage has two jobs to handle both Azure Bicep Templates and the App Service Web Package.</p>
</li>
<li>
<p><strong>Development</strong>. Deploy to the Development Environment using an environment agnostic pipeline template*.</p>
</li>
<li>
<p><strong>Staging</strong>. Deploy to the Staging Environment using an environment agnostic pipeline template*.</p>
</li>
<li>
<p><strong>Production</strong>. Deploy to the Production Environment using an environment agnostic pipeline template*.</p>
</li>
</ol>
<p>*The environment agnostic pipeline template is a reusable template for all environments.</p>
<h3 id="pipeline-yaml-build-and-environment-deployments">Pipeline YAML: Build and Environment Deployments</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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 style="color:#6272a4"># Orchestrating Template: Build And Environment Deployments</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">stages</span>:
</span></span><span style="display:flex;"><span>- <span style="color:#ff79c6">stage</span>: <span style="color:#f1fa8c">&#39;Build_Packages&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">jobs</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">job</span>: <span style="color:#f1fa8c">&#39;Test_Build_And_Version_Bicep_Templates&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">steps</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>: 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><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">job</span>: <span style="color:#f1fa8c">&#39;Build_and_Version_AppService&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">steps</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>: PublishPipelineArtifact@1
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Publish App Service 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)/AppService.zip&#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;AppService&#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:#ff79c6">stage</span>: <span style="color:#f1fa8c">&#39;Development&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy to Dev Environment&#39;</span>
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">jobs</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">deployment</span>: Deploy_to_Dev
</span></span><span style="display:flex;"><span>      ...
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>              - <span style="color:#ff79c6">template</span>: EnvironmentDeploy.azurepipelinetemplate.yml
</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">stage</span>: <span style="color:#f1fa8c">&#39;Staging&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy to Staging Environment&#39;</span>
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">jobs</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">deployment</span>: Deploy_to_Staging
</span></span><span style="display:flex;"><span>      ...
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>              - <span style="color:#ff79c6">template</span>: EnvironmentDeploy.azurepipelinetemplate.yml
</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">stage</span>: <span style="color:#f1fa8c">&#39;Production&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy to Production Environment&#39;</span>
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">jobs</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">deployment</span>: Deploy_to_Production
</span></span><span style="display:flex;"><span>      ...
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>              - <span style="color:#ff79c6">template</span>: EnvironmentDeploy.azurepipelinetemplate.yml
</span></span><span style="display:flex;"><span>                ...
</span></span></code></pre></div><p>Once the Build_Packages stage has completed successfully, we now have two pipeline artifacts, ARMtemplates and AppService. In each environment deploy these two artifacts will be downloaded and used alongside environment specific Library Variable Groups to create specific environment deployments.</p>
<h3 id="environment-agnostic-deployment-template">Environment-Agnostic Deployment Template</h3>
<p>The environment agnostic template is shown below and has the following steps:</p>
<ol>
<li><strong>Artifact Downloads</strong>. Downloads the two pipeline artifacts, ARMtemplates and AppService.</li>
<li><strong>Azure Resource Group Deployment</strong>. Using the built ARM template, specific environment library variable group and ARM Connection.</li>
<li><strong>Custom PowerShell Script to Obtain Azure Deployment Outputs</strong>. Deployment output such as appServiceName will be created as job variables ready for future tasks to access.</li>
<li><strong><a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/azure-web-app-v1?view=azure-pipelines">Azure Web App Deploy</a> (Specific for Dev and Staging)</strong>. This task deploys straight to the production slot for non production environments.</li>
<li><strong><a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/azure-web-app-v1?view=azure-pipelines">Azure Web App Deploy</a> (Specific for Prod)</strong>. This task deploys to the staging slot ready for a manual slot swap.</li>
</ol>
<h3 id="pipeline-yaml-template-reusable-environment-agnostic-deployments">Pipeline YAML Template: Reusable Environment-Agnostic Deployments</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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 style="color:#6272a4"># Environment Deploy Template: Deploy Azure Resources and Obtain Outputs, Deploy App Service</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">download</span>: current
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Download ARM templates&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">artifact</span>: ARMtemplates
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">download</span>: current
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Download the App Service Web package&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">artifact</span>: AppService
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">task</span>: AzureResourceManagerTemplateDeployment@3
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy App Service ARM template&#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">deploymentScope</span>: <span style="color:#f1fa8c">&#39;Resource Group&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">azureResourceManagerConnection</span>: ${{ parameters.ARMConn}}
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">subscriptionId</span>: <span style="color:#f1fa8c">&#39;$(subscriptionId)&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">action</span>: <span style="color:#f1fa8c">&#39;Create Or Update Resource Group&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">resourceGroupName</span>: <span style="color:#f1fa8c">&#39;$(resourceGroupName)&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">location</span>: <span style="color:#f1fa8c">&#39;$(location)&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">templateLocation</span>: <span style="color:#f1fa8c">&#39;Linked artifact&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">csmFile</span>: <span style="color:#f1fa8c">&#39;$(Pipeline.Workspace)/ARMtemplates/AppService.azuredeploy.json&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">deploymentMode</span>: <span style="color:#f1fa8c">&#39;Incremental&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">deploymentOutputs</span>: deploymentOutputs
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">overrideParameters</span>: &gt;-<span style="color:#f1fa8c">
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">        ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">task</span>: PowerShell@2
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">name</span>: armOutput
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Obtain Azure Deployment outputs&#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">targetType</span>: <span style="color:#f1fa8c">&#39;inline&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">script</span>: |<span style="color:#f1fa8c">
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">        if (![string]::IsNullOrEmpty( &#39;$(deploymentOutputs)&#39; )) {
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">          $DeploymentOutputs = convertfrom-json &#39;$(deploymentOutputs)&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">          $DeploymentOutputs.PSObject.Properties | ForEach-Object {
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              $keyname = $_.Name
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              $value = $_.Value.value
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              Write-Host &#34;The value of [$keyName] is [$value]&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              Write-Host &#34;##vso[task.setvariable variable=$keyname;isOutput=true]$value&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">          }
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">        }</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">task</span>: AzureWebApp@1
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy App Service - Dev and Staging Environment Only&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">condition</span>: ne(variables[&#39;env&#39;], &#39;prod&#39;)
</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>: ${{ parameters.ARMConn }}
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">appType</span>: <span style="color:#f1fa8c">&#39;webApp&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">appName</span>: <span style="color:#f1fa8c">&#39;$(armOutput.appServiceName)&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">package</span>: <span style="color:#f1fa8c">&#39;$(Pipeline.Workspace)/AppService/AppService.zip&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">task</span>: AzureWebApp@1
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Deploy App Service to Staging Slot - Prod Environment Only&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">condition</span>: eq(variables[&#39;env&#39;], &#39;prod&#39;)
</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>: ${{ parameters.ARMConn }}
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">appType</span>: <span style="color:#f1fa8c">&#39;webApp&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">appName</span>: <span style="color:#f1fa8c">&#39;$(armOutput.appServiceName)&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">deployToSlotOrASE</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">resourceGroupName</span>: <span style="color:#f1fa8c">&#39;$(resourceGroupName)&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">slotName</span>: <span style="color:#f1fa8c">&#39;$(slotName)&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">package</span>: <span style="color:#f1fa8c">&#39;$(Pipeline.Workspace)/AppService/AppService.zip&#39;</span>
</span></span></code></pre></div><h3 id="accessing-arm-output-variables-across-jobs">Accessing ARM Output Variables Across Jobs</h3>
<p>Given that I will also need to access the appServiceName ARM Output that has been outputted from the armOutput task as a variable in future jobs, I have used the <a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/process/set-variables-scripts?view=azure-devops&amp;tabs=bash#set-an-output-variable-for-use-in-future-jobs"><code>Output Variable for use in future jobs</code></a> configuration.</p>
<p>Rather than job only:</p>
<blockquote>
<p>##vso[task.setvariable variable=$keyname]$value</p>
</blockquote>
<p>It uses the IsOutput so we can setup dependencies and access later:</p>
<blockquote>
<p>##vso[task.setvariable variable=$keyname;<code>isOutput=true</code>]$value</p>
</blockquote>
<p><strong>Note</strong>: Accessing a Output Variable is different to a non-output variable:</p>
<blockquote>
<p>Non-output Variable: <code>'$(appServiceName)'</code></p>
<p>Output Variable: <code>'$(armOutput.appServiceName)'</code>, you will need to reference the task name prior to the variable.</p>
</blockquote>
<h3 id="manual-slot-swap-with-azure-devops-approvals">Manual Slot Swap with Azure DevOps Approvals</h3>
<p>At this point, we now have our App Service resources deployed and the Site deployed to the production slots for Dev and Staging, and deployed to the Staging slot for Production.</p>
<p>But now down to the real question, how do I manually swap the slots using some Azure DevOps governance goodness?</p>
<p>To do this we are going to use the Azure DevOps Environment Approvals which will allow specific groups or users to approve the slot swap. Following this we will use the <a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/azure-app-service-manage-v0?view=azure-pipelines">AzureAppServiceManage@0</a> task to conduct the slot swap.</p>
<h3 id="setting-up-azure-devops-environments-and-approvals">Setting Up Azure DevOps Environments and Approvals</h3>
<p>To setup an environment and approvals:</p>
<ol>
<li>Sign in to your Azure DevOps organisation and open your project.</li>
<li>Select <strong>Pipelines</strong> &gt; <strong>Environments</strong> &gt; <strong>Create Environment</strong>.</li>
<li>Enter information for the environment, and then select <strong>Create</strong>. (For our purposes leave Resource as None)</li>
<li>In your Environment, Select <strong>Approvals and checks</strong> tab, and then select the + sign to add a new check.</li>
<li>Select <strong>Approvals</strong>, and then select <strong>Next</strong>.</li>
<li>Add users or groups as your designated <strong>Approvers</strong>, and if desired the following
<ol>
<li>Instructions to approvers</li>
<li>Allow approvers to approve their own runs</li>
</ol>
</li>
<li>Make sure to Save.</li>
</ol>
<p>Environments can be assigned to stages and jobs, but not to individual tasks. This means the slot swap task cannot reside within the environment-agnostic deployment template; it must be a separate, follow-on job.</p>
<p>To cater for the slot swap and environment approval, we are going to amend the orchestrating template to add another Job after the deployment Job in the Production deploy stage. This is shown below.</p>
<h3 id="adding-the-slot-swap-job-to-the-pipeline">Adding the Slot Swap Job to the Pipeline</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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 style="color:#6272a4"># Orchestrating Template: Build And Environment Deployments</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">stage</span>: <span style="color:#f1fa8c">&#39;Production&#39;</span>
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">jobs</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">deployment</span>: Deploy_to_Production
</span></span><span style="display:flex;"><span>      ...
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>              - <span style="color:#ff79c6">template</span>: EnvironmentDeploy.azurepipelinetemplate.yml
</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">deployment</span>: Slot_Swap_to_Production
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">dependsOn</span>: Deploy_to_Production
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">condition</span>: succeeded()
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">environment</span>: ProductionEnv
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">variables</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">appServiceName</span>: $[ dependencies.Deploy_to_Production.outputs[&#39;Deploy_to_Production.armOutput.appServiceName&#39;] ]
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">strategy</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">runOnce</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">deploy</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>              - <span style="color:#ff79c6">task</span>: AzureAppServiceManage@0
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Swap App Service Staging Slot to Production&#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;XXXYYYZZZ&#39;</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#ff79c6">action</span>: <span style="color:#f1fa8c">&#39;Swap Slots&#39;</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#ff79c6">WebAppName</span>: <span style="color:#f1fa8c">&#39;$(appServiceName)&#39;</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#ff79c6">ResourceGroupName</span>: <span style="color:#f1fa8c">&#39;$(resourceGroupName)&#39;</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#ff79c6">SourceSlot</span>: <span style="color:#f1fa8c">&#39;$(slotName)&#39;</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#ff79c6">SwapWithProduction</span>: <span style="color:#ff79c6">true</span>
</span></span></code></pre></div><p>As can be seen in the amended template above, the Slot_Swap_to_Production job runs after the Deploy_to_Production job and expects it to have succeeded as a condition to running. The Slot_Swap_to_Production job also references the environment ProductionEnv for Approval prior to running.</p>
<p><strong>Note</strong>: The first time the environment is used on the pipeline, the pipeline will ask for permission to use the resource.</p>
<h3 id="referencing-output-variables-in-the-slot-swap-job">Referencing Output Variables in the Slot Swap Job</h3>
<p>The Azure App Service Manage task requires the <code>WebAppName</code>, which is set as an output variable in the environment-agnostic YAML pipeline template. To use this value in the <code>Slot_Swap_to_Production</code> job, define a new variable that references the output variable from the previous job. Once defined, you can use it within the current job as a standard variable (e.g., <code>$(appServiceName)</code>).</p>
<p>The variable reference uses the following syntax:</p>
<blockquote>
<p>[dependencies.<code>JobName</code>.outputs[&rsquo;<code>JobName</code>.<code>TaskName</code>.<code>VariableName</code>]]</p>
</blockquote>
<p>You now have a pipeline that at the point of the slot swap job will wait for an appropriate approval prior to running and swapping your staging slot to production.</p>
<h2 id="summary">Summary</h2>
<p>By combining Azure App Service deployment slots, Infrastructure as Code, and Azure DevOps environment approvals, you can create a robust, auditable deployment pipeline. This approach ensures that production releases are gated by manual approval, reducing risk and enabling rapid rollback if needed.</p>
<p>Ready to take your deployments to the next level? Try setting up manual slot swaps with approvals in your next release pipeline, and let your team experience stress-free, governed production launches.</p>
]]></content:encoded></item><item><title>Agent Loop | Azure Logic Apps Just Got Smarter</title><link>https://andrewilson.co.uk/post/2025/05/agent-loop-announcement-microsoft-logic-apps/</link><pubDate>Wed, 21 May 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/05/agent-loop-announcement-microsoft-logic-apps/</guid><description>&lt;h2 id="microsoft-announcement"&gt;Microsoft Announcement&lt;/h2&gt;
&lt;p&gt;Microsoft has just introduced something quite revolutionary into the world of integration: &lt;strong&gt;Agent Loop&lt;/strong&gt;, a new capability in Azure Logic Apps that lets you build AI-powered agents directly into your workflows. On the surface, it might seem like another incremental feature. But dig a little deeper, and the implications are huge!&lt;/p&gt;
&lt;p&gt;Let’s break it down.&lt;/p&gt;
&lt;h2 id="what-is-agent-loop"&gt;What Is Agent Loop?&lt;/h2&gt;
&lt;p&gt;Agent Loop is Microsoft’s way of embedding &lt;strong&gt;advanced AI decision-making&lt;/strong&gt; into Logic Apps workflows. Think of it like adding a smart co-pilot into your integration flows, an agent that can &lt;strong&gt;retain context, reason over time, and take meaningful actions&lt;/strong&gt;. It uses an LLM like Azure OpenAI under the hood, paired with a memory store that persists throughout the workflow, allowing the agent to understand, adapt, and make decisions across multiple steps.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="microsoft-announcement">Microsoft Announcement</h2>
<p>Microsoft has just introduced something quite revolutionary into the world of integration: <strong>Agent Loop</strong>, a new capability in Azure Logic Apps that lets you build AI-powered agents directly into your workflows. On the surface, it might seem like another incremental feature. But dig a little deeper, and the implications are huge!</p>
<p>Let’s break it down.</p>
<h2 id="what-is-agent-loop">What Is Agent Loop?</h2>
<p>Agent Loop is Microsoft’s way of embedding <strong>advanced AI decision-making</strong> into Logic Apps workflows. Think of it like adding a smart co-pilot into your integration flows, an agent that can <strong>retain context, reason over time, and take meaningful actions</strong>. It uses an LLM like Azure OpenAI under the hood, paired with a memory store that persists throughout the workflow, allowing the agent to understand, adapt, and make decisions across multiple steps.</p>
<p>This isn’t just a chat feature bolted onto Logic Apps. This is <strong>a new integration pattern</strong>.</p>
<h2 id="why-this-matters">Why This Matters</h2>
<h3 id="1-from-orchestrators-to-orchestrators-with-brains">1. From Orchestrators to Orchestrators-With-Brains</h3>
<p>Traditionally, Logic Apps has been a powerful tool for orchestrating workflows, but each step is stateless and reactive. With Agent Loop, Microsoft is blurring the lines between <strong>workflow automation and autonomous decision-making</strong>. Your integration logic can now evolve based on prior inputs, contextual signals, or even unstructured content.</p>
<p>Imagine an agent that:</p>
<ul>
<li>Detects anomalies in data feeds and adjusts routing accordingly</li>
<li>Analyses unstructured text (emails, tickets, PDFs) and initiates remediation</li>
<li>Talks to legacy systems, interprets responses, and adapts its logic in real time</li>
</ul>
<p>This isn’t just modernisation, it’s <strong>next-gen augmentation</strong>.</p>
<h3 id="2-for-biztalk-migrations-this-changes-the-equation">2. For BizTalk Migrations, This Changes the Equation</h3>
<p>Many of the workflows built in BizTalk were complex not just because of their integration points, but because of the <strong>decision logic</strong> baked into orchestration layers. Until now, moving those workflows into Azure meant carefully untangling rulesets, mappings, and dependencies.</p>
<p>Agent Loop gives you a new option, <strong>wrap legacy logic in an AI reasoning layer</strong>, preserve existing behaviour where needed, and <strong>inject flexibility</strong> where it&rsquo;s long overdue. That’s a big deal for organisations plotting their <strong>BizTalk to Azure Integration Services (AIS)</strong> migration.</p>
<h3 id="3-bridging-structured-and-unstructured-worlds">3. Bridging Structured and Unstructured Worlds</h3>
<p>In most enterprises, structured data lives in databases, but the real mess, the emails, PDFs, meeting notes, customer queries, is where value is trapped. Agent Loop lets you embed GPT-style agents that can process unstructured inputs, make sense of them, and act accordingly.</p>
<p>In effect, it bridges the gap between <strong>API-driven systems and human language interactions</strong>, a holy grail for integration architecture.</p>
<h2 id="what-could-this-enable">What Could This Enable?</h2>
<p>Some early scenarios come to mind:</p>
<ul>
<li><strong>Intelligent triage</strong>: Auto-routing support tickets based on nuanced analysis, not just keywords</li>
<li><strong>Contract processing</strong>: Reading legal documents and extracting obligations for ERP updates</li>
<li><strong>Integration self-healing</strong>: Agents that spot failed API calls and retry or switch endpoints contextually</li>
<li><strong>Customer 360 augmentation</strong>: Combining CRM data with email sentiment to tailor outreach strategies</li>
</ul>
<p>What’s powerful here is not just the AI, it’s that the AI lives inside your <strong>governed, observable, enterprise-ready integration fabric</strong>. That’s the kind of maturity architects have been waiting for.</p>
<h2 id="the-bigger-picture">The Bigger Picture</h2>
<p>This move by Microsoft is a signal, <strong>AI isn’t a separate strategy anymore, it’s becoming part of the plumbing</strong>. And for IT leaders, that means a shift in how we think about capability building.</p>
<p>Where before we asked, “What can be automated?”, now we ask, “What can be <strong>intelligently</strong> automated?”
Where we once feared black-box AI, now we can build AI agents that live inside controlled workflows.</p>
<h2 id="a-word-of-caution">A Word of Caution</h2>
<p>This is still early days. Agent Loop is in public preview, and like all AI features, there are concerns around predictability, testing, and operational risk. Governance, testing harnesses, and monitoring patterns will also need to be matured alongside adoption.</p>
<p>But make no mistake, this is a foundational shift.</p>
<h2 id="getting-started">Getting Started</h2>
<p>Agent Loop is already available in Logic Apps Standard! Here are some Microsoft resources to help you begin:</p>
<ul>
<li><strong>Documentation</strong>: Explore the <a href="https://aka.ms/agentloopconcepts">agent loop concepts</a> and <a href="https://aka.ms/AgentLoopHowTo">detailed guide with step-by-step instructions</a> on how to configure and use Agent Loop.</li>
<li><strong>Samples &amp; Demos</strong>: Watch<a href="https://aka.ms/agentloopdemos"> pre-recorded demos</a> showcasing both conversational and autonomous agent scenarios built with Agent Loop. You&rsquo;ll also get a preview of exciting features coming soon.</li>
</ul>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>If you’re leading a modernisation initiative, working through a BizTalk migration, or trying to unify integration and automation under a future-proof architecture, Agent Loop deserves your attention.</p>
<p>It’s one of those moments where <strong>what looks like a feature is actually a new paradigm</strong>. The integration space just got a whole lot smarter,and that changes everything.</p>
<h2 id="further-reading">Further Reading:</h2>
<p><a href="https://techcommunity.microsoft.com/blog/integrationsonazureblog/%F0%9F%93%A2announcing-agent-loop-build-ai-agents-in-azure-logic-apps-%F0%9F%A4%96/4415052">Official Announcement – Microsoft Tech Community</a></p>
]]></content:encoded></item><item><title>The Requirements Ambiguity Paradox</title><link>https://andrewilson.co.uk/post/2025/05/requirements-ambiguity-paradox/</link><pubDate>Fri, 16 May 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/05/requirements-ambiguity-paradox/</guid><description>&lt;h2 id="the-requirements-ambiguity-paradox"&gt;The Requirements Ambiguity Paradox&lt;/h2&gt;
&lt;p&gt;With the context of software requirements, I regularly encounter an understood but often ignored scenario: the less detailed the initial requirements, the more expansive the client&amp;rsquo;s expectations become as the project progresses. It&amp;rsquo;s a phenomenon I refer to as the &lt;strong&gt;Requirements Ambiguity Paradox&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Imagine initiating a project with a brief requirements document, aiming for agility and speed. At first glance, this seems efficient.&lt;/p&gt;
&lt;p&gt;However, this brevity often leads to divergent interpretations:&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-requirements-ambiguity-paradox">The Requirements Ambiguity Paradox</h2>
<p>With the context of software requirements, I regularly encounter an understood but often ignored scenario: the less detailed the initial requirements, the more expansive the client&rsquo;s expectations become as the project progresses. It&rsquo;s a phenomenon I refer to as the <strong>Requirements Ambiguity Paradox</strong>.</p>
<p>Imagine initiating a project with a brief requirements document, aiming for agility and speed. At first glance, this seems efficient.</p>
<p>However, this brevity often leads to divergent interpretations:</p>
<ul>
<li><strong>Developers</strong> assume a minimal scope, focusing on core functionalities.</li>
<li><strong>Clients</strong> envision a comprehensive solution, expecting features beyond the initial outline.</li>
</ul>
<p>This disconnect sets the stage for scope creep, misaligned expectations, and potential project overruns.</p>
<h2 id="the-paradox-in-action">The Paradox in Action</h2>
<p>Here&rsquo;s how the paradox typically unfolds:</p>
<ol>
<li><strong>Minimal Requirements</strong>: To expedite the project kickoff, the team drafts a concise requirements document.</li>
<li><strong>Divergent Assumptions</strong>: Stakeholders interpret the brief differently, leading to varying expectations.</li>
<li><strong>Feature Requests Multiply</strong>: As development progresses, clients request additional features, believing they were implied.</li>
<li><strong>Project Strain</strong>: The team faces increased workload, extended timelines, and potential budget overruns. This can lead to reduced morale, client dissatisfaction, and even jeopardise the project&rsquo;s success.</li>
</ol>
<p>Ironically, the initial attempt to streamline the process results in greater complexity and resource expenditure.</p>
<h2 id="navigating-the-paradox">Navigating the Paradox</h2>
<p>To mitigate this paradox, consider the following strategies:</p>
<ul>
<li><strong>Define a Clear MVP</strong>: Establish a well-articulated Minimum Viable Product, outlining core functionalities and deliverables.</li>
<li><strong>Employ User Stories</strong>: Utilise user stories with explicit acceptance criteria to capture requirements comprehensively.</li>
<li><strong>Implement Agile Contracts</strong>: Adopt flexible contracts that accommodate evolving requirements while maintaining scope control.</li>
<li><strong>Foster Collaborative Requirement Gathering</strong>: Engage stakeholders in the requirements gathering process to ensure shared understanding and alignment.</li>
</ul>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>The Requirements Ambiguity Paradox serves as a reminder that clarity at the project&rsquo;s inception is paramount. While brevity may seem efficient, detailed and collaborative requirement documentation often leads to smoother project execution and stakeholder satisfaction.</p>
<p>Tighten up your requirements now, your future self, your team, and your client will thank you.</p>
]]></content:encoded></item><item><title>Automating Semantic Versioning in Azure DevOps CI/CD Pipelines with GitVersion</title><link>https://andrewilson.co.uk/post/2025/05/cicd-and-automatic-semantic-versioning/</link><pubDate>Wed, 07 May 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/05/cicd-and-automatic-semantic-versioning/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Versioning is the unsung hero of software development—often overlooked but absolutely essential. Imagine trying to manage a project without a clear way to track changes, communicate updates, or ensure compatibility. Chaos, right? That’s where versioning steps in, providing structure and clarity.&lt;/p&gt;
&lt;p&gt;In this post, I’ll share how I streamline versioning in my projects by combining the power of &lt;strong&gt;Semantic Versioning (SemVer)&lt;/strong&gt; with &lt;strong&gt;GitVersion&lt;/strong&gt;, an automation tool that eliminates the manual effort of version management. Whether you&amp;rsquo;re just beginning your journey or tackling the complexities of feature-rich projects, this post will show you how to automate semantic versioning in &lt;strong&gt;Azure DevOps&lt;/strong&gt; for consistency, traceability, and peace of mind.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>Versioning is the unsung hero of software development—often overlooked but absolutely essential. Imagine trying to manage a project without a clear way to track changes, communicate updates, or ensure compatibility. Chaos, right? That’s where versioning steps in, providing structure and clarity.</p>
<p>In this post, I’ll share how I streamline versioning in my projects by combining the power of <strong>Semantic Versioning (SemVer)</strong> with <strong>GitVersion</strong>, an automation tool that eliminates the manual effort of version management. Whether you&rsquo;re just beginning your journey or tackling the complexities of feature-rich projects, this post will show you how to automate semantic versioning in <strong>Azure DevOps</strong> for consistency, traceability, and peace of mind.</p>
<h2 id="why-version">Why Version?</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>
<h2 id="what-is-semantic-versioning">What is Semantic Versioning?</h2>
<p>Semantic Versioning (often abbreviated as SemVer) is a versioning scheme that provides a standardised way to communicate the nature of changes in a software release. It uses a three-part version number format: <code>MAJOR.MINOR.PATCH</code>, where each part conveys specific information about the release.</p>
<h3 id="breakdown-of-semantic-versioning">Breakdown of Semantic Versioning:</h3>
<ol>
<li>
<p><strong>MAJOR Version</strong>: Incremented when there are breaking changes that are incompatible with previous versions.</p>
<ul>
<li>Example: <code>1.0.0</code> → <code>2.0.0</code></li>
</ul>
</li>
<li>
<p><strong>MINOR Version</strong>: Incremented when new features are added in a backward-compatible manner.</p>
<ul>
<li>Example: <code>1.0.0</code> → <code>1.1.0</code></li>
</ul>
</li>
<li>
<p><strong>PATCH Version</strong>: Incremented when backward-compatible bug fixes or small improvements are made.</p>
<ul>
<li>Example: <code>1.0.0</code> → <code>1.0.1</code></li>
</ul>
</li>
</ol>
<h3 id="benefits-of-semantic-versioning">Benefits of Semantic Versioning:</h3>
<ul>
<li><strong>Clarity</strong>: It clearly communicates the scope and impact of changes to users and developers.</li>
<li><strong>Predictability</strong>: Helps teams and users understand what to expect from a new release.</li>
<li><strong>Dependency Management</strong>: Makes it easier to specify compatible versions of libraries or APIs.</li>
<li><strong>Automation</strong>: Tools like GitVersion can automate the generation of semantic versions in CI/CD pipelines, ensuring consistency and reducing manual effort.</li>
</ul>
<h2 id="versioning-with-gitversion">Versioning with GitVersion</h2>
<p><a href="https://gitversion.net/docs/">GitVersion</a> is an open source tool that can be used to automate semantic versioning by analysing our Git repository&rsquo;s history. It streamlines the versioning process, ensuring consistency and reducing manual errors.</p>
<p>GitVersion has the following key features:</p>
<ul>
<li><strong>Semantic Versioning (SemVer)</strong>: Automatically calculates version numbers based on Git history, adhering to SemVer principles.</li>
<li><strong>Branching Strategy Support</strong>: Compatible with Continuous Delivery, GitFlow, GitHub Flow, and Mainline development workflows.</li>
<li><strong>Continuous Integration (CI) Friendly</strong>: Integrates seamlessly with CI/CD pipelines, generating version numbers for builds and releases.</li>
<li><strong>Flexible Configuration</strong>: Highly configurable to suit various project needs and versioning schemes.</li>
</ul>
<h3 id="gitversion-configuration">GitVersion Configuration</h3>
<p>GitVersion uses a <a href="https://gitversion.net/docs/reference/configuration">configuration file</a> (GitVersion.yml) to define how version numbers are calculated based on your Git repository&rsquo;s history and branching strategy. This file allows us to customise the behaviour of GitVersion to suit our project&rsquo;s needs.</p>
<h2 id="how-i-branch-and-configure-gitversion-for-projects">How I Branch and Configure GitVersion for Projects</h2>
<p>My projects tend to use the following setup:</p>
<ul>
<li><strong>Continuous Delivery Branch Model</strong>. This model has the following features:
<ul>
<li><strong>Main branch as release-ready</strong>: The <code>main</code> branch is always in a deployable state.</li>
<li><strong>Frequent Deployments</strong>: Changes are deployed to production or staging environments frequently, often automatically.</li>
<li><strong>Short-Lived Feature Branches</strong>: Feature branches are merged into <code>main</code> after review and testing.</li>
<li><strong>Automated Pipelines</strong>: CI/CD pipelines handle building, testing, and deploying changes seamlessly.</li>
</ul>
</li>
<li>SemVer is incremented using the following strategies:
<ul>
<li><strong>TaggedCommit</strong>. This strategy uses Git tags to determine the version. If a commit is tagged with a semantic version (e.g., 1.0.0), GitVersion will use that tag as the base for calculating the next version.</li>
<li><strong>Fallback</strong>. This strategy is used when no other versioning information (e.g., tags or branch-specific rules) is available. It serves as a default versioning mechanism, especially for newly initiated projects.</li>
</ul>
</li>
<li>Two defined branches for GitVersion increments:</li>
</ul>
<table>
	<thead>
			<tr>
					<th><strong>Branch</strong></th>
					<th><strong>Increment</strong></th>
					<th><strong>When</strong></th>
					<th><strong>Prevent Increment If Commit Tagged</strong></th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Main</td>
					<td>Major (X.0.0)</td>
					<td>Manually using tags for significant breaking changes.</td>
					<td>✅ Yes</td>
			</tr>
			<tr>
					<td>Main</td>
					<td>Minor (0.X.0)</td>
					<td>Automatically on new commits for backward-compatible features.</td>
					<td>✅ Yes</td>
			</tr>
			<tr>
					<td>Pull Request</td>
					<td>Patch (0.0.X)</td>
					<td>Automatically on pull request creation or updates for backward-compatible bug fixes or small improvements.</td>
					<td>✅ Yes</td>
			</tr>
	</tbody>
</table>
<h3 id="gitconfig-setup">GitConfig Setup</h3>
<p>The described GitVersion configuration looks 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;-webkit-text-size-adjust:none;"><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 style="color:#ff79c6">strategies</span>:
</span></span><span style="display:flex;"><span>- TaggedCommit
</span></span><span style="display:flex;"><span>- Fallback
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">branches</span>:
</span></span><span style="display:flex;"><span>   <span style="color:#ff79c6">main</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">increment</span>: Minor
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">prevent-increment</span>:
</span></span><span style="display:flex;"><span>         <span style="color:#ff79c6">when-current-commit-tagged</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>   <span style="color:#ff79c6">pull-request</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">increment</span>: Patch
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">prevent-increment</span>:
</span></span><span style="display:flex;"><span>         <span style="color:#ff79c6">when-current-commit-tagged</span>: <span style="color:#ff79c6">true</span>
</span></span></code></pre></div><table>
	<thead>
			<tr>
					<th><strong>Configuration</strong></th>
					<th><strong>Details</strong></th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><strong>Strategies</strong></td>
					<td>- <code>TaggedCommit</code>: Uses Git tags to determine the version.</td>
			</tr>
			<tr>
					<td></td>
					<td>- <code>Fallback</code>: Provides a default versioning mechanism when no other information is available.</td>
			</tr>
			<tr>
					<td><strong>Branches</strong></td>
					<td></td>
			</tr>
			<tr>
					<td><code>main</code></td>
					<td>- <strong>Increment</strong>: <code>Minor</code> (0.X.0)</td>
			</tr>
			<tr>
					<td></td>
					<td>- <strong>Prevent Increment</strong>: Enabled when the current commit is tagged.</td>
			</tr>
			<tr>
					<td><code>pull-request</code></td>
					<td>- <strong>Increment</strong>: <code>Patch</code> (0.0.X)</td>
			</tr>
			<tr>
					<td></td>
					<td>- <strong>Prevent Increment</strong>: Enabled when the current commit is tagged.</td>
			</tr>
	</tbody>
</table>
<h3 id="azure-devops-cicd-pipeline-setup">Azure DevOps CI/CD Pipeline Setup</h3>
<p>The CI/CD pipeline would be configured 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;-webkit-text-size-adjust:none;"><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 style="color:#ff79c6">name</span>: $(Build.DefinitionName)_$(GitVersion.FullSemVer)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">trigger</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">branches</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">include</span>: [ main ] <span style="color:#6272a4"># branch names which will trigger a build</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">pr</span>: <span style="color:#6272a4"># will trigger on PR</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">branches</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">include</span>: [ main ] <span style="color:#6272a4"># branch names which will trigger a build.</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">variables</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">name</span>: Source_Branch_Ref
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">value</span>: $[replace(coalesce(variables[&#39;System.PullRequest.SourceBranch&#39;], variables[&#39;Build.SourceBranch&#39;]), &#39;refs/heads/&#39;, &#39;&#39;)]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">resources</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">repositories</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">repository</span>: Source_Branch
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">type</span>: git
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">name</span>: GitVersionSemVerWithTags
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">ref</span>: <span style="color:#f1fa8c">&#34;$(Source_Branch_Ref)&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">stages</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">stage</span>: <span style="color:#f1fa8c">&#39;Build_Packages&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">jobs</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ff79c6">job</span>: <span style="color:#f1fa8c">&#39;Increment_Version&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">condition</span>: and(succeeded(), or(eq(variables[&#39;Build.Reason&#39;], &#39;PullRequest&#39;), and(eq(variables[&#39;Build.SourceBranch&#39;], &#39;refs/heads/main&#39;), ne(variables[&#39;Build.Reason&#39;], &#39;Manual&#39;))))
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">pool</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">vmImage</span>: <span style="color:#f1fa8c">&#39;windows-latest&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">checkout</span>: Source_Branch
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">persistCredentials</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchTags</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchDepth</span>: <span style="color:#bd93f9">0</span> <span style="color:#6272a4"># Ensure we fetch all Git history for Semver</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: gitversion/setup@3
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Get current version of GitVersion&#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">versionSpec</span>: <span style="color:#f1fa8c">&#39;6.0.x&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: gitversion/execute@3
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Run GitVersion to generate SEMVER&#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.SourcesDirectory)\&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">            useConfigFile: true
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">            configFilePath: &#39;</span>$(Build.SourcesDirectory)\GitVersion.yml&#39;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: PowerShell@2
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Increment the Version using Git Tag&#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">targetType</span>: <span style="color:#f1fa8c">&#39;inline&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">script</span>: |<span style="color:#f1fa8c">
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              cd &#39;$(Build.SourcesDirectory)&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              git config --global user.email &#34;$(Build.RequestedForEmail)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              git config --global user.name &#34;$(Build.RequestedFor)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              git tag -a &#34;$(GitVersion.MajorMinorPatch)&#34; -m &#34;Released by $(Build.RequestedFor)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              git push origin tag &#34;$(GitVersion.MajorMinorPatch)&#34;</span>
</span></span></code></pre></div><h4 id="key-steps-in-the-pipeline">Key Steps in the Pipeline</h4>
<ol>
<li><strong>Pipeline Name</strong>: Combines the build definition name with the full semantic version: <code>$(Build.DefinitionName)_$(GitVersion.FullSemVer)</code>.</li>
<li><strong>Trigger</strong>: Builds are triggered on changes to the <code>main</code> branch.</li>
<li><strong>Pull Request Trigger</strong>: Builds are triggered on pull requests targeting the <code>main</code> branch.</li>
<li><strong>Variables</strong>: Defines <code>Source_Branch_Ref</code> to extract the source branch reference for the build or pull request.</li>
<li><strong>Resources</strong>: Dynamically sets the branch reference for the Git repository (<code>Source_Branch</code>) to <code>$(Source_Branch_Ref)</code>.</li>
<li><strong>Job: Increment_Version</strong>: Executes if the build succeeds and is triggered by a pull request or the <code>main</code> branch but not part of a <code>Manual Trigger</code>.</li>
<li><strong>Checkout</strong>: Checks out the <code>Source_Branch</code> repository with full Git history and tags for versioning.</li>
<li><strong>GitVersion Setup</strong>: Installs GitVersion (<code>6.0.x</code>) to calculate semantic versions.</li>
<li><strong>GitVersion Execute</strong>: Runs GitVersion using the <code>GitVersion.yml</code> configuration file to generate the semantic version.</li>
<li><strong>PowerShell Script</strong>:
<ul>
<li>Configures Git by setting the user email and name to match the build requester.</li>
<li>Creates or updates a Git tag with the calculated version (<code>$(GitVersion.MajorMinorPatch)</code>).</li>
<li>Pushes the tag to the remote repository, ensuring the version is recorded.</li>
</ul>
</li>
</ol>
<blockquote>
<p>⚠️ <strong>Important Note</strong><br>
The <code>Source_Branch_Ref</code> variable and resource in this pipeline are configured as shown above because pull requests (PRs) create their own Git branches which are a merger of the source branch and main. When tags are created during the pipeline execution, they are placed on the PR branch by default, not the source branch where GitVersion calculates automatic increments.<br>
By setting up the <code>Source_Branch_Ref</code> variable and dynamically referencing the source branch in the <code>resources</code> section, the tag is placed on the source branch instead of the PR branch. This ensures that version increments are correctly applied to the source branch, maintaining accurate semantic versioning.<br>
The PR branch should still be used for building and validating solution artifacts.</p>
</blockquote>
<blockquote>
<p>⚠️ <strong>Important Pipeline Permissions</strong><br>
The Azure DevOps [Project] Build Service must have the following Repository Permissions:</p>
<ul>
<li><strong>Contribute</strong>: Permission to push changes to the repository.</li>
<li><strong>Create Tag</strong>: Permission to create and update tags in the repository.</li>
</ul>
</blockquote>
<h3 id="using-gitversion-to-version-artifacts">Using GitVersion to Version Artifacts</h3>
<p>Versioning artifacts involves two phases:</p>
<ol>
<li><strong>Generate</strong> a semantic version to use. This can be achieved by leveraging the GitVersion task.</li>
<li><strong>Applying</strong> the sematic version to artifacts. This can be achieved by using a task such as <a href="https://marketplace.visualstudio.com/items?itemName=richardfennellBM.BM-VSTS-Versioning-Task">VersionJSONFile@3</a>.</li>
</ol>
<p>As an example, when working with ARM templates in Azure, it&rsquo;s important to version your artifacts for traceability and consistency. Using GitVersion in your Azure DevOps pipeline, you can  generate a semantic version and apply it to the <code>contentVersion</code> field of your ARM template. This guarantees that each deployment is uniquely identifiable.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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 style="color:#ff79c6">job</span>: <span style="color:#f1fa8c">&#39;Test_and_VersionBicepTemplates&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">pool</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">vmImage</span>: <span style="color:#f1fa8c">&#39;windows-latest&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">checkout</span>: self
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">path</span>: <span style="color:#f1fa8c">&#39;./s/selfBranch/&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">persistCredentials</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchTags</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchDepth</span>: <span style="color:#bd93f9">0</span> <span style="color:#6272a4"># Ensure we fetch all Git history for Semver</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">checkout</span>: Source_Branch
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">path</span>: <span style="color:#f1fa8c">&#39;./s/versionBranch/&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">persistCredentials</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchTags</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchDepth</span>: <span style="color:#bd93f9">0</span> <span style="color:#6272a4"># Ensure we fetch all Git history for Semver</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4"># GitVersion task is needed in each job where the variables are referenced</span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: gitversion/setup@3
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Get current version of GitVersion&#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">versionSpec</span>: <span style="color:#f1fa8c">&#39;6.0.x&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: gitversion/execute@3
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Run GitVersion to generate SEMVER&#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;$(System.DefaultWorkingDirectory)/versionBranch/&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">useConfigFile</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">configFilePath</span>: <span style="color:#f1fa8c">&#39;$(System.DefaultWorkingDirectory)/versionBranch/GitVersion.yml&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: BicepInstall@0
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">version</span>: <span style="color:#bd93f9">0.35.1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: BicepBuild@0
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">process</span>: <span style="color:#f1fa8c">&#34;single&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">sourceFile</span>: <span style="color:#f1fa8c">&#39;$(Build.SourcesDirectory)\selfBranch\Deployment\azuredeploy.bicep&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">stdout</span>: <span style="color:#ff79c6">false</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">outputFile</span>: <span style="color:#f1fa8c">&#39;$(Build.ArtifactStagingDirectory)\ARMOutput\azuredeploy.json&#39;</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.ArtifactStagingDirectory)\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>: <span style="color:#f1fa8c">&#39;$(GitVersion.AssemblySemFileVer)&#39;</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>: <span style="color:#f1fa8c">&#39;\d+\.\d+\.\d+\.\d+&#39;</span>
</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></span><span style="display:flex;"><span>            <span style="color:#ff79c6">OutputVersion</span>: <span style="color:#f1fa8c">&#39;OutputedVersion&#39;</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 Versioned Solution Templates build artefact&#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">&#34;$(Build.ArtifactStagingDirectory)/ARMOutput&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">publishLocation</span>: <span style="color:#f1fa8c">&#34;pipeline&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">artifactName</span>: <span style="color:#f1fa8c">&#34;ARM-Templates&#34;</span>
</span></span></code></pre></div><h4 id="key-steps-in-the-pipeline-1">Key Steps in the Pipeline</h4>
<ol>
<li>
<p><strong>Checkout Repositories</strong></p>
<ul>
<li>The pipeline checks out two branches:
<ul>
<li><strong>Self Branch</strong>: Contains the Bicep files that will be compiled into ARM templates.</li>
<li><strong>Source Branch</strong>: Used for versioning and fetching Git history.</li>
</ul>
</li>
<li>Full Git history and tags are fetched (<code>fetchTags: true</code>, <code>fetchDepth: 0</code>) to ensure accurate semantic version calculation.</li>
</ul>
</li>
<li>
<p><strong>Generate Semantic Version with GitVersion</strong></p>
<ul>
<li><strong>GitVersion Setup</strong>: The <code>gitversion/setup@3</code> task installs GitVersion (<code>6.0.x</code>).</li>
<li><strong>GitVersion Execution</strong>: The <code>gitversion/execute@3</code> task calculates the semantic version based on the repository&rsquo;s history and configuration (<code>GitVersion.yml</code>).
<ul>
<li>The version is exposed as <a href="https://gitversion.net/docs/reference/variables">pipeline variables</a>, such as <code>$(GitVersion.AssemblySemFileVer)</code>.</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>Build ARM Templates</strong></p>
<ul>
<li>The <code>BicepBuild</code> task compiles the Bicep file (<code>azuredeploy.bicep</code>) into an ARM template (<code>azuredeploy.json</code>).</li>
<li>The compiled ARM template is stored in the <code>$(Build.ArtifactStagingDirectory)/ARMOutput</code> directory.</li>
</ul>
</li>
<li>
<p><strong>Version Stamp the ARM Template</strong></p>
<ul>
<li>The <code>VersionJSONFile</code> task updates the <code>contentVersion</code> field in the ARM template (<code>azuredeploy.json</code>) with the semantic version generated by GitVersion.
<ul>
<li><strong>Inputs</strong>:
<ul>
<li><code>VersionNumber</code>: Uses <code>$(GitVersion.AssemblySemFileVer)</code> (Provides a 4-digit version format (<code>MAJOR.MINOR.PATCH.0</code>), which is ideal for ARM template versioning e.g., <code>1.2.3.0</code>).</li>
<li><code>Field</code>: Specifies the <code>contentVersion</code> field in the ARM template to be updated.</li>
<li><code>VersionRegex</code>: Ensures only valid version formats (<code>\d+\.\d+\.\d+\.\d+</code>) are replaced.</li>
</ul>
</li>
<li>This step ensures that the ARM template is uniquely versioned for traceability.</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>Publish the Versioned ARM Template</strong></p>
<ul>
<li>The <code>PublishPipelineArtifact</code> task publishes the versioned ARM template as a pipeline artifact.
<ul>
<li>The artifact is stored under the name <code>ARM-Templates</code> and can be used in subsequent deployment stages.</li>
</ul>
</li>
</ul>
</li>
</ol>
<h2 id="summary">Summary</h2>
<p>Automating semantic versioning in Azure DevOps CI/CD pipelines with GitVersion is a game-changer for maintaining consistency, traceability, and efficiency in your development workflow. By leveraging tools like GitVersion, you can eliminate the manual effort of version management, ensure accurate versioning across branches, and streamline the deployment of versioned artifacts like ARM templates.</p>
<p>Whether you&rsquo;re managing simple projects or complex, feature-rich solutions, adopting semantic versioning practices ensures that your team and stakeholders have a clear understanding of changes, compatibility, and release impact. With the strategies and pipeline configurations shared in this post, you’re now equipped to implement a robust versioning system that aligns with industry best practices.</p>
<p>If you found this post useful, consider sharing it with your team or network to help others streamline their versioning workflows. Happy automating!</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>&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;The Bicep &lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/existing-resource"&gt;existing&lt;/a&gt; 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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>Logic App | Access Key Revocation and Regeneration</title><link>https://andrewilson.co.uk/post/2025/02/logic-app-standard-sas-revocation-and-regeneration/</link><pubDate>Fri, 28 Feb 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/02/logic-app-standard-sas-revocation-and-regeneration/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;In previous articles I have subtly referenced risks and best practices regarding HTTP triggered workflows and their use of Access Keys for security, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Some Potential Risks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a &lt;strong&gt;Key is leaked&lt;/strong&gt;, it can be used by anyone who obtains it to call your Logic App Workflow.&lt;/li&gt;
&lt;li&gt;If a &lt;strong&gt;Key has expired or been invalidated&lt;/strong&gt; then services, applications, and or users who have not been provided a new key will cease to be able to invoke your workflow.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Some Access Key Best Practices to mitigate risks:&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>In previous articles I have subtly referenced risks and best practices regarding HTTP triggered workflows and their use of Access Keys for security, such as:</p>
<ul>
<li>
<p>Some Potential Risks:</p>
<ul>
<li>If a <strong>Key is leaked</strong>, it can be used by anyone who obtains it to call your Logic App Workflow.</li>
<li>If a <strong>Key has expired or been invalidated</strong> then services, applications, and or users who have not been provided a new key will cease to be able to invoke your workflow.</li>
</ul>
</li>
<li>
<p>Some Access Key Best Practices to mitigate risks:</p>
<ul>
<li><strong>Have a revocation plan</strong> - Make sure that you are prepared to respond if a Key is compromised.</li>
<li><strong>Have a rotation plan</strong> - Look to replace the Key with new ones at regular intervals.</li>
</ul>
</li>
</ul>
<p>Given the risks and best practices described, I wanted to put together a solution whereby the regeneration and issue of the access keys could be automated. This allows for keys to be regenerated on a regular schedule provided they might need to comply with security policies. Further to this, regenerating the access keys means old ones are invalidated and thus protects you in the incident were a key is compromised.</p>
<h2 id="solution">Solution</h2>
<p>
  <img src="/images/posts/2025/02/accesskeyrevocationandregeneration.png" alt="Access Key Revocation and Regeneration">

</p>
<p>To automate the regeneration of the access keys, I am going to make use of the <a href="https://learn.microsoft.com/en-us/rest/api/appservice/workflows/regenerate-access-key?view=rest-appservice-2024-04-01&amp;tabs=HTTP">Azure Rest API</a>
and the following components:</p>
<ul>
<li><strong>Azure Key Vault</strong> - Used as secure storage for the workflow access keys.</li>
<li><strong>Identity and Access Management</strong> - Services, applications, and users will gain access to the access key secrets in Key Vault through Identity and role assignment at the secret level (Principal of Least Privilege).</li>
<li><strong>PowerShell Script</strong> - A script developed to invoke the Regenerate Access Key Azure Rest API.</li>
<li><strong>Bicep Template</strong> - A template developed to obtain and re-issue the workflow access keys to Key Vault.</li>
<li><strong>DevOps Pipeline</strong> - Using an automated pipeline to manually or on schedule invoke the PowerShell script and redeploy the Bicep template.</li>
</ul>
<blockquote>
<p>⚠️ <strong>Note</strong></p>
<ul>
<li>
<p>For automated roll out of access keys to services, applications, or users, their reference of the key vault secrets must not be directly tied to a version of the secret, rather always point to current.</p>
</li>
<li>
<p>Key vault reference values are cached in App Services and refetched every 24 hours.</p>
<ul>
<li>To refetch values immediately, the following options are available:
<ul>
<li>
<p>Manually through the Portal by using the &ldquo;Pull Reference Values&rdquo; under &ldquo;Environment Variables&rdquo;.</p>
</li>
<li>
<p>Update the App Config. Any configuration change to the app causes an app restart and an immediate refetch of all referenced secrets.</p>
</li>
<li>
<p><strong>Preferred</strong> - Pipeline powershell task after roll out that invokes a refetch.</p>
<p><code>az rest --method post --url https://management.azure.com/[Resurce ID]/config/configreferences/appsettings/refresh?api-version=2022-03-01 </code></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</blockquote>
<p><strong>Script to Regenerate a Workflow Access Key</strong></p>
<p>The following PowerShell script is used to Regenerate a specific Logic App Workflow Access Key. By regenerating a new key, the previous will also be invalidated.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-PowerShell" data-lang="PowerShell"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$SubscriptionId</span> = <span style="color:#f1fa8c">&#39;{SubscriptionID}&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$ResourceGroupName</span> = <span style="color:#f1fa8c">&#39;{ResourceGroupName}&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$LogicAppName</span> = <span style="color:#f1fa8c">&#39;{Standard Logic App Name}&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$WorkflowName</span> = <span style="color:#f1fa8c">&#39;{Workflow Name}&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># Not required if automated and using as part of a DevOps pipeline.</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Connect-AzAccount</span> -Subscription <span style="color:#8be9fd;font-style:italic">$SubscriptionId</span> 
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$accessToken</span> = <span style="color:#8be9fd;font-style:italic">Get-AzAccessToken</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$request</span> = <span style="color:#8be9fd;font-style:italic">@</span>{
</span></span><span style="display:flex;"><span>    Method = <span style="color:#f1fa8c">&#39;POST&#39;</span>
</span></span><span style="display:flex;"><span>    Uri = <span style="color:#f1fa8c">&#34;https://management.azure.com/subscriptions/</span>$(<span style="color:#8be9fd;font-style:italic">$SubscriptionId</span>)<span style="color:#f1fa8c">/resourceGroups/</span>$(<span style="color:#8be9fd;font-style:italic">$ResourceGroupName</span>)<span style="color:#f1fa8c">/providers/Microsoft.Web/sites/</span>$(<span style="color:#8be9fd;font-style:italic">$LogicAppName</span>)<span style="color:#f1fa8c">/hostruntime/runtime/webhooks/workflow/api/management/workflows/</span>$(<span style="color:#8be9fd;font-style:italic">$WorkflowName</span>)<span style="color:#f1fa8c">/regenerateAccessKey?api-version=2024-04-01&#34;</span>
</span></span><span style="display:flex;"><span>    Body = <span style="color:#8be9fd;font-style:italic">@</span>{
</span></span><span style="display:flex;"><span>        keyType = <span style="color:#f1fa8c">&#39;Primary&#39;</span>
</span></span><span style="display:flex;"><span>    } | <span style="color:#8be9fd;font-style:italic">ConvertTo-Json</span>
</span></span><span style="display:flex;"><span>    Headers = <span style="color:#8be9fd;font-style:italic">@</span>{
</span></span><span style="display:flex;"><span>        Authorization = <span style="color:#f1fa8c">&#34;Bearer </span>$(<span style="color:#8be9fd;font-style:italic">$accessToken</span>.Token)<span style="color:#f1fa8c">&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f1fa8c">&#34;Content-Type&#34;</span> = <span style="color:#f1fa8c">&#34;application/json&#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:#8be9fd;font-style:italic">Invoke-RestMethod</span> <span style="color:#8be9fd;font-style:italic">@request</span>
</span></span></code></pre></div><p><strong>Bicep Template to Obtain and Surface Workflow Access Keys to Key Vault</strong></p>
<p>The following Bicep template is used to obtain the workflow access key and create the secret in key vault (if already deployed, then a new version).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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">Workflow</span> <span style="color:#8be9fd;font-style:italic">Access</span> <span style="color:#8be9fd;font-style:italic">Keys</span> <span style="color:#8be9fd;font-style:italic">Role</span> <span style="color:#8be9fd;font-style:italic">Out</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">// ** 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;Object type used to identify a Workflow and Trigger&#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">workflowName</span>: <span style="color:#f1fa8c">&#39;The name of the workflow within your Standard Logic App.&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">workflowTrigger</span>: <span style="color:#f1fa8c">&#39;The HTTP trigger name within the workflow&#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">workflow</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">workflowName</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">workflowTrigger</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;Array of Standard Logic App Workflows&#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">workflowArray</span> = <span style="color:#8be9fd;font-style:italic">workflow</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;Name of the Logic App to place workflow(s) sig into KeyVault&#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;Name of the Key Vault to place secrets into&#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">keyVaultName</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;Array of Workflows to obtain sigs from.&#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">workflows</span> <span style="color:#8be9fd;font-style:italic">workflowArray</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:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Retrieve the existing 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">logicApp</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/sites@2024-04-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">LogicAppName</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 existing 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;Create the Logic App workflow access key as a secret - Deployment principle requires RBAC permissions to do this&#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">vaultLogicAppKey</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults/secrets@2023-07-01&#39;</span> = [<span style="color:#8be9fd;font-style:italic">for</span> <span style="color:#8be9fd;font-style:italic">workflow</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">workflows</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">logicApp</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">workflow</span>.<span style="color:#8be9fd;font-style:italic">workflowName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-sig&#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">tags</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">ResourceType</span>: <span style="color:#f1fa8c">&#39;LogicAppStandard&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">ResourceName</span>: <span style="color:#8be9fd;font-style:italic">logicApp</span>.<span style="color:#8be9fd;font-style:italic">name</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 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">listCallbackUrl</span>(<span style="color:#50fa7b">resourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Web/sites/hostruntime/webhooks/api/workflows/triggers&#39;</span>, <span style="color:#8be9fd;font-style:italic">logicApp</span>.<span style="color:#8be9fd;font-style:italic">name</span>, <span style="color:#f1fa8c">&#39;runtime&#39;</span>, <span style="color:#f1fa8c">&#39;workflow&#39;</span>, <span style="color:#f1fa8c">&#39;management&#39;</span>, <span style="color:#8be9fd;font-style:italic">workflow</span>.<span style="color:#8be9fd;font-style:italic">workflowName</span>, <span style="color:#8be9fd;font-style:italic">workflow</span>.<span style="color:#8be9fd;font-style:italic">workflowTrigger</span>), <span style="color:#f1fa8c">&#39;2022-09-01&#39;</span>).<span style="color:#8be9fd;font-style:italic">queries</span>.<span style="color:#8be9fd;font-style:italic">sig</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 that the access key is compromised due to a user/component/service being compromised, then the action would be two fold:</p>
<ol>
<li>Revoke the user/component/service access to the secret in key vault.</li>
<li>Regenerate and issue a new access key.</li>
</ol>
<p>Hope this helps and have fun.</p>
]]></content:encoded></item><item><title>Logic App | Try-Catch Pattern, Nested Scopes, And Compensating Transaction Pattern</title><link>https://andrewilson.co.uk/post/2025/01/logic-app-nested-scopes-and-compensating-transaction-pattern/</link><pubDate>Wed, 08 Jan 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/01/logic-app-nested-scopes-and-compensating-transaction-pattern/</guid><description>&lt;h2 id="the-try-catch-pattern"&gt;The Try-Catch Pattern&lt;/h2&gt;
&lt;p&gt;Following the idea of defensive programming or as I like to call it for Logic Apps (&lt;em&gt;being low code&lt;/em&gt;): &lt;em&gt;defensive processing&lt;/em&gt;, it is considered good practice to wrap your workflows in a try-catch pattern to handle the unexpected. The pattern makes use of a mixture of Run After conditions and the Scope block.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Run After Conditions&lt;/strong&gt; | &lt;em&gt;used to define the execution order based on the state of the previous action or scope&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scope Block&lt;/strong&gt; | &lt;em&gt;provides the ability to group a series of actions. If any action within the scope fails, the whole scope will fail (unless handled via other means)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By placing your actions within a &amp;ldquo;try&amp;rdquo; scope with the assumption that each action will run after the success of the previous, if there is an action failure, the scope will fail also.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-try-catch-pattern">The Try-Catch Pattern</h2>
<p>Following the idea of defensive programming or as I like to call it for Logic Apps (<em>being low code</em>): <em>defensive processing</em>, it is considered good practice to wrap your workflows in a try-catch pattern to handle the unexpected. The pattern makes use of a mixture of Run After conditions and the Scope block.</p>
<ul>
<li><strong>Run After Conditions</strong> | <em>used to define the execution order based on the state of the previous action or scope</em></li>
<li><strong>Scope Block</strong> | <em>provides the ability to group a series of actions. If any action within the scope fails, the whole scope will fail (unless handled via other means)</em></li>
</ul>
<p>By placing your actions within a &ldquo;try&rdquo; scope with the assumption that each action will run after the success of the previous, if there is an action failure, the scope will fail also.</p>
<p>Using the Run After conditions on either a proceeding action or scope, you can &ldquo;catch&rdquo; any <code>has failed, is skipped, or has timed out</code> states from the &ldquo;try&rdquo; scope. In effect this makes up your simple try-catch block.</p>
<p><strong>Example</strong></p>
<p>
  <img src="/images/posts/2025/01/trycatch.png" alt="Simple Try-Catch">

</p>
<p>It is possible once you have entered into the catch scope to retrieve details of the resulting try scope by using the expression <code>@result('Scope-Try')</code>. The expression will provide an array of all the actions and their details including if they were successful or failed with a given exception message.</p>
<p>This is really useful but how does it hold up when nested you may ask&hellip;</p>
<h2 id="nested-try-catch-scopes">Nested Try-Catch Scopes</h2>
<p>Nested try-catch scopes can be handy when you have smaller segments of the larger workflow that can be tried as a group; preventing unnecessary complexity in handling issues and failures later in the workflow or as a whole.</p>
<p><strong>Example</strong>

  <img src="/images/posts/2025/01/nestedtrycatch.png" alt="Nested Try-Catch">

</p>
<p>This example still has its overarching try-catch due to the possibility of either sub catches containing actions that can fail, be skipped, or time out.</p>
<p>Nesting try-catch scopes offers the same capabilities as un-nested. However, there are caveats to remember when nesting in this way:</p>
<ul>
<li><strong>Complexity</strong> | readability and maintainability is always paramount when designing workflows.
<ul>
<li>Make sure to appropriately and consistently title actions and scopes.</li>
<li><a href="https://blog.sandro-pereira.com/2022/02/28/logic-app-best-practices-tips-and-tricks-3-add-comments/">Add comments</a>, especially on scopes as this can aid in describing the processing and functionality that sits within.</li>
</ul>
</li>
<li><strong>Intentional flow review</strong> | given the added complexity through nesting and run after configurations, thorough review and testing of processing flow should be conducted.
<ul>
<li>A method of doing this is to <a href="https://learn.microsoft.com/en-us/azure/logic-apps/test-logic-apps-mock-data-static-results?tabs=standard">test/mock outputs on actions</a>.</li>
</ul>
</li>
<li><strong>Handles the unexpected</strong> | as with the single try-catch, this is not a method in handling non-&ldquo;happy path&rdquo; results from processing, rather the unexpected failures, skips, or timeouts.</li>
</ul>
<p>As referred to in the last point in the caveats, if the try-catch both singular and nested aids in the unexpected, how can I add to this pattern to help with handling &ldquo;un-happy&rdquo; paths. This cues the Compensating Transaction pattern&hellip;</p>
<h2 id="compensating-transaction-pattern">Compensating Transaction Pattern</h2>
<p>The <a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction">Compensating Transaction pattern</a> is useful when you are trying to attain an eventually consistent operation that consists of multiple steps. If one or more of the steps fail, this pattern can be used to undo the steps performed.</p>
<p>One of the most basic forms of compensation or undo is to notify for manual remediation. This is the scenario that I will use as an example.</p>
<p><strong>Scenario</strong></p>
<p>Let&rsquo;s assume that a taxi booking process needs to be automated as a logic app workflow. There are four steps involved in this process:</p>
<ol>
<li>Check to see if the customer is an existing customer, and if so obtain an enriched form of their details from our system.</li>
<li>Check if the time slot is available with a local driver.</li>
<li>If the time slot is available, book the time slot with the local driver.</li>
<li>Send a notification to the customer regarding details of the booked time slot and their driver.</li>
</ol>
<p>If there are any problems with processing these four steps, a notification will need to be sent for manual remediation. This must include:</p>
<ul>
<li>The steps that were successful</li>
<li>The step that was not able to be performed</li>
<li>The steps that were skipped after the one that failed to be processed</li>
</ul>
<p>In this scenario and example, the use of the try-catch pattern (nested) is used to catch the unexpected problems in processing. The addition of the Condition action allows us to perform process validation (<em>the action may have succeeded, but did it return the result we desired?</em>). The example further uses the Condition action to validate if the workflow should continue onto the next processing step given the previous may have failed.</p>
<p><strong>Example</strong>

  <img src="/images/posts/2025/01/CompensatingTransactionPattern.png" alt="Compensating Transaction pattern">

</p>
<blockquote>
<p>The example shows a subset of the steps implemented as the rest are a repeat demonstration.</p>
</blockquote>
<p>Given this workflow design, we can process faults in a controlled manner both expected and unexpected. We can control which processing steps should be performed given an error may have occurred, and we can through our transaction log give a full account allowing for full remediation.</p>
<p>There are caveats to remember when implementing this pattern and example:</p>
<ul>
<li><strong>Complexity</strong> | readability and maintainability is always paramount when designing workflows.
<ul>
<li>Make sure to appropriately and consistently title actions and scopes.</li>
<li><a href="https://blog.sandro-pereira.com/2022/02/28/logic-app-best-practices-tips-and-tricks-3-add-comments/">Add comments</a>, especially on scopes as this can aid in describing the processing and functionality that sits within.</li>
</ul>
</li>
<li><strong>Intentional flow review</strong> | given the added complexity through nesting, conditions, and run after configurations, thorough review and testing of processing flow should be conducted.
<ul>
<li>A method of doing this is to <a href="https://learn.microsoft.com/en-us/azure/logic-apps/test-logic-apps-mock-data-static-results?tabs=standard">test/mock outputs on actions</a>.</li>
</ul>
</li>
</ul>
<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>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;</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>&lt;p&gt;&lt;a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend"&gt;
&lt;img src="https://img.shields.io/badge/Repo-Easy%20Auth%20With%20Standard%20Logic%20App%20And%20APIM-blue?logo=github&amp;amp;style=for-the-badge" alt="GitHub Repository"&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;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 &lt;a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview#best-practices-when-using-sas"&gt;best practices&lt;/a&gt; that you need to be abiding by. Such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Some Potential Risks:&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>&lt;p&gt;&lt;a href="https://github.com/Andrew-D-Wilson/Function-App-APIM-Backend/"&gt;
&lt;img src="https://img.shields.io/badge/Repo-Easy%20Auth%20With%20Function%20Apps%20And%20APIM-blue?logo=github&amp;amp;style=for-the-badge" alt="GitHub Repository"&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://andrewilson.co.uk/post/2024/10/function-app-apim-backend/"&gt;recent work&lt;/a&gt; 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 &lt;a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview#best-practices-when-using-sas"&gt;best practices&lt;/a&gt; that you need to be abiding by. Such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Some Potential Risks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a &lt;strong&gt;SAS is leaked&lt;/strong&gt;, it can be used by anyone who obtains it to call your Function.&lt;/li&gt;
&lt;li&gt;If a &lt;strong&gt;SAS expires&lt;/strong&gt; and the API Management API has not been updated to make use of the updated SAS, then the integration functionality will be hindered.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Some SAS Best Practices to mitigate risks:&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>Azure API Management | Function App Backend</title><link>https://andrewilson.co.uk/post/2024/10/function-app-apim-backend/</link><pubDate>Tue, 01 Oct 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/10/function-app-apim-backend/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/Andrew-D-Wilson/Function-App-APIM-Backend"&gt;
&lt;img src="https://img.shields.io/badge/Repo-Function--App--APIM--Backend-blue?logo=github&amp;amp;style=for-the-badge" alt="GitHub Repository"&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Following on from a previous set of posts from earlier this year where I detailed how to securely implement Logic App Standard backends in Azure API Management, there has been questions on how this would be achieved in a similar manner with Azure Function Apps.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;To read-up on how this was achieved with Standard Logic Apps have a look at the following:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://andrewilson.co.uk/post/2024/01/standard-logic-app-apim-backend/"&gt;Azure API Management | Logic App (Standard) Backend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andrewilson.co.uk/post/2024/02/standard-logic-app-apim-backend-swagger/"&gt;Azure API Management | Logic App (Standard) Backend Using a Swagger Definition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andrewilson.co.uk/post/2024/02/standard-logic-app-easy-auth-apim/"&gt;Easy Auth | Standard Logic App with Azure API Management&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At a high level comparison with Azure Logic Apps, Azure Functions are a developer-centric serverless compute offering allowing authors to write code in languages such as C#, Java, Javascript, Python, and PowerShell. Azure Functions are best suited for stateless computation and application specific tasks.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p><a href="https://github.com/Andrew-D-Wilson/Function-App-APIM-Backend">
  <img src="https://img.shields.io/badge/Repo-Function--App--APIM--Backend-blue?logo=github&amp;style=for-the-badge" alt="GitHub Repository">

</a></p>
<p>Following on from a previous set of posts from earlier this year where I detailed how to securely implement Logic App Standard backends in Azure API Management, there has been questions on how this would be achieved in a similar manner with Azure Function Apps.</p>
<p><em>To read-up on how this was achieved with Standard Logic Apps have a look at the following:</em></p>
<ul>
<li><a href="/post/2024/01/standard-logic-app-apim-backend/">Azure API Management | Logic App (Standard) Backend</a></li>
<li><a href="/post/2024/02/standard-logic-app-apim-backend-swagger/">Azure API Management | Logic App (Standard) Backend Using a Swagger Definition</a></li>
<li><a href="/post/2024/02/standard-logic-app-easy-auth-apim/">Easy Auth | Standard Logic App with Azure API Management</a></li>
</ul>
<p>At a high level comparison with Azure Logic Apps, Azure Functions are a developer-centric serverless compute offering allowing authors to write code in languages such as C#, Java, Javascript, Python, and PowerShell. Azure Functions are best suited for stateless computation and application specific tasks.</p>
<p>Azure Logic Apps are similar in that they too are a serverless offering but more specifically built as a workflow integration platform. It&rsquo;s a low code/no code user-friendly development option for designing workflows, integrating different systems, and building business process automation.</p>
<p>Given that Standard Logic Apps share the same compute platform as Azure Function Apps, some of what this post will aim to achieve will be similar to that achieved with the posts highlighted above, but with enough differences that I would suggest reading on.</p>
<p>The method explored here (<em>Linking a Azure Function App as an APIM API Backend</em>) aims to be configurable (<em>Both in Deployment and API setup</em>), and secure; ensuring Principal of Least Privilege (PoLP). The diagram below provides an overview of what is to be achieved:</p>
<p>
  <img src="/images/posts/2024/10/AzureAPIManagement-FunctionAppOverview.png" alt="Overview">

</p>
<p>The overall design aims to abstract the backend from the API Operations, i.e. the backend points to the Azure Function App and the individual operations point to the respective HTTP triggered functions. The design also specifies granular access to the Function specific Shared-Access-Signature (SAS) key as opposed to the Function App SAS key. Providing access to the Function App Host or Admin key is simpler in deployment configuration but has a security concern in allowing access to any Function within the Function App. By utilising the specific Function host SAS key means that only specific access is granted; following in principal of least privilege and lessening the blast radius if a breach of security were to occur. Further to this, the Function SAS key will be held in the applications specific KeyVault where access to this secret will be conducted over Role Based Access Control (RBAC) restricted to the specific secret (again following PoLP). <em>To see further details on this, see <a href="/post/2023/11/rbac-key-vault-specific-secret/">Azure RBAC Key Vault | Role Assignment for Specific Secret</a></em>.</p>
<p>As with my previous posts, I have opted for Infrastructure as Code (IaC) as my method of implementation, specifically Bicep. I have broken down the implementation of the diagram above into two parts, Application Deployment, and API Deployment.</p>
<h2 id="application-deployment">Application Deployment</h2>
<p>The following diagram demonstrates how the application backend has been deployed.

  <img src="/images/posts/2024/10/AzureAPIManagement-FunctionAppAppDeployment.png" alt="ApplicationDeployment">

</p>
<p>The deployment is split into three stages:</p>
<ol>
<li>Deploy the Core Application Components.</li>
<li>Deploy the Functions to the recently deployed Function App.</li>
<li>Store the Function specific SAS keys in KeyVault for later secure access.</li>
</ol>
<p>In turn the Bicep for <strong>step 1</strong> is 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;-webkit-text-size-adjust:none;"><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">Application</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">// ** 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;A prefix used to identify the application resources&#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">applicationPrefixName</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 name of the application used for tags&#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">applicationName</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 resources will be deployed to - defaulting to the resource group location&#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;The environment that the resources are being deployed 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">env</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:#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">applicationKeyVaultName</span> = <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationPrefixName</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">env</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">kv&#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">funcApplicationAppServicePlanName</span> = <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationPrefixName</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">env</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">asp&#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">funcStorageAccountName</span> = <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationPrefixName</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">env</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">st&#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">applicationFunctionAppName</span> = <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationPrefixName</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">env</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">func&#39;</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">isProduction</span> = <span style="color:#8be9fd;font-style:italic">env</span> <span style="color:#ff79c6">==</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:#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;Deploy the Application Specific 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">applicationKeyVaultDeploy</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:#8be9fd;font-style:italic">applicationKeyVaultName</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 style="color:#8be9fd;font-style:italic">Application</span>: <span style="color:#8be9fd;font-style:italic">applicationName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 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">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">enableRbacAuthorization</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">enableSoftDelete</span>: <span style="color:#8be9fd;font-style:italic">isProduction</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 App Service Plan used for 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">funcAppServicePlanDeploy</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/serverfarms@2023-12-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">funcApplicationAppServicePlanName</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 style="color:#8be9fd;font-style:italic">Application</span>: <span style="color:#8be9fd;font-style:italic">applicationName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 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;Y1&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">tier</span>: <span style="color:#f1fa8c">&#39;Dynamic&#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 style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Deploy the Storage Account used for 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">funcStorageAccountDeploy</span> <span style="color:#f1fa8c">&#39;Microsoft.Storage/storageAccounts@2023-05-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">funcStorageAccountName</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 style="color:#8be9fd;font-style:italic">Application</span>: <span style="color:#8be9fd;font-style:italic">applicationName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 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 style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">supportsHttpsTrafficOnly</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">minimumTlsVersion</span>: <span style="color:#f1fa8c">&#39;TLS1_2&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">defaultToOAuthAuthentication</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><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@2023-12-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">tags</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Application</span>: <span style="color:#8be9fd;font-style:italic">applicationName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 style="color:#8be9fd;font-style:italic">kind</span>: <span style="color:#f1fa8c">&#39;functionapp&#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">serverFarmId</span>: <span style="color:#8be9fd;font-style:italic">funcAppServicePlanDeploy</span>.<span style="color:#8be9fd;font-style:italic">id</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">publicNetworkAccess</span>: <span style="color:#f1fa8c">&#39;Enabled&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">httpsOnly</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">resource</span> <span style="color:#8be9fd;font-style:italic">config</span> <span style="color:#f1fa8c">&#39;config@2022-09-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 style="color:#8be9fd;font-style:italic">FUNCTIONS_EXTENSION_VERSION</span>: <span style="color:#f1fa8c">&#39;~4&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">FUNCTIONS_WORKER_RUNTIME</span>: <span style="color:#f1fa8c">&#39;dotnet&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">WEBSITE_NODE_DEFAULT_VERSION</span>: <span style="color:#f1fa8c">&#39;~18&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">AzureWebJobsStorage</span>: <span style="color:#f1fa8c">&#39;DefaultEndpointsProtocol=https;AccountName=</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">funcStorageAccountDeploy</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">;AccountKey=</span><span style="color:#f1fa8c">${</span><span style="color:#50fa7b">listKeys</span>(<span style="color:#8be9fd;font-style:italic">funcStorageAccountDeploy</span>.<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#f1fa8c">&#39;2019-06-01&#39;</span>).<span style="color:#8be9fd;font-style:italic">keys</span>[<span style="color:#8be9fd;font-style:italic">0</span>].<span style="color:#8be9fd;font-style:italic">value</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">;EndpointSuffix=core.windows.net&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">WEBSITE_CONTENTAZUREFILECONNECTIONSTRING</span>: <span style="color:#f1fa8c">&#39;DefaultEndpointsProtocol=https;AccountName=</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">funcStorageAccountDeploy</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">;AccountKey=</span><span style="color:#f1fa8c">${</span><span style="color:#50fa7b">listKeys</span>(<span style="color:#8be9fd;font-style:italic">funcStorageAccountDeploy</span>.<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#f1fa8c">&#39;2019-06-01&#39;</span>).<span style="color:#8be9fd;font-style:italic">keys</span>[<span style="color:#8be9fd;font-style:italic">0</span>].<span style="color:#8be9fd;font-style:italic">value</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">;EndpointSuffix=core.windows.net&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">WEBSITE_CONTENTSHARE</span>: <span style="color:#8be9fd;font-style:italic">funcStorageAccountDeploy</span>.<span style="color:#8be9fd;font-style:italic">name</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">// ** 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">applicationFunctionAppName</span> <span style="color:#8be9fd;font-style:italic">string</span>  = <span style="color:#8be9fd;font-style:italic">applicationFunctionAppName</span>
</span></span></code></pre></div><p><strong>Step 2</strong> is the deployment of the Functions such as this simple C# Hello World request response:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-C#" data-lang="C#"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">using</span> ...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">namespace</span> HelloWorldFunctions
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">public</span> <span style="color:#8be9fd;font-style:italic">static</span> <span style="color:#ff79c6">class</span> <span style="color:#50fa7b">HelloWorld</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">        [FunctionName(&#34;HelloWorld&#34;)]</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">public</span> <span style="color:#8be9fd;font-style:italic">static</span> <span style="color:#8be9fd;font-style:italic">async</span> Task&lt;IActionResult&gt; Run(
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">            [HttpTrigger(AuthorizationLevel.Function, &#34;get&#34;, &#34;post&#34;, Route = null)]</span> HttpRequest req,
</span></span><span style="display:flex;"><span>            ILogger log)
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            log.LogInformation(<span style="color:#f1fa8c">&#34;C# HTTP trigger function processed a request.&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd">string</span> name = req.Query[<span style="color:#f1fa8c">&#34;name&#34;</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd">string</span> requestBody = <span style="color:#ff79c6">await</span> <span style="color:#ff79c6">new</span> StreamReader(req.Body).ReadToEndAsync();
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd">dynamic</span> data = JsonConvert.DeserializeObject(requestBody);
</span></span><span style="display:flex;"><span>            name = name ?? data?.name;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd">string</span> responseMessage = <span style="color:#8be9fd">string</span>.IsNullOrEmpty(name)
</span></span><span style="display:flex;"><span>                ? <span style="color:#f1fa8c">&#34;This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.&#34;</span>
</span></span><span style="display:flex;"><span>                : <span style="color:#f1fa8c">$&#34;Hello, {name}. This HTTP triggered function executed successfully.&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">return</span> <span style="color:#ff79c6">new</span> OkObjectResult(responseMessage);
</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><strong>Step 3</strong> demonstrated below takes a number of functions, retrieves their SAS keys and creates them as secrets in the Application KeyVault.</p>
<blockquote>
<p><strong>Note</strong>: To obtain the Function specific SAS key, the Function must have already been deployed to the Function App.</p>
</blockquote>
<p>The following Bicep is used to obtain the Function specific SAS key:
<code>listKeys(resourceId('Microsoft.Web/sites/functions', applicationFunctionAppName, function),'2023-12-01').default</code></p>
<blockquote>
<p>⚠️ <strong>Important</strong></p>
<p>This implementation utilises the default Function SAS key, so special consideration should be taken in your own implementation regarding SAS expiration, revocation, and rotation.</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;-webkit-text-size-adjust:none;"><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">Application</span> <span style="color:#8be9fd;font-style:italic">Secrets</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">// ** 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;Name of the Function App to add as a backend&#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">applicationFunctionAppName</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 name of the functions in the function app to add secrets for&#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">functions</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;Name of the Key Vault to place secrets into&#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">keyVaultName</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:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Retrieve the existing 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;Vault the Functions key as a secret - Deployment principle requires RBAC permissions to do this&#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">vaultFunctionsKey</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults/secrets@2023-07-01&#39;</span> = [<span style="color:#8be9fd;font-style:italic">for</span> <span style="color:#8be9fd;font-style:italic">function</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">functions</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">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">function</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">keyVault</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">ResourceType</span>: <span style="color:#f1fa8c">&#39;FunctionApp&#39;</span>
</span></span><span style="display:flex;"><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">applicationFunctionAppName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">function</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:#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:#50fa7b">resourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Web/sites/functions&#39;</span>, <span style="color:#8be9fd;font-style:italic">applicationFunctionAppName</span>, <span style="color:#8be9fd;font-style:italic">function</span>),<span style="color:#f1fa8c">&#39;2023-12-01&#39;</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><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><h2 id="api-deployment">API Deployment</h2>
<p>The following  diagram demonstrates how API Management and the Function App backend API have been deployed.</p>
<p>
  <img src="/images/posts/2024/10/AzureAPIManagement-FunctionAppAPIDeployment.png" alt="APIDeployment">

</p>
<p>The deployment is split into two stages:</p>
<ol>
<li>Deploy an API Management Service Instance.</li>
<li>Deploy respective Backend, Named Values, API, API Operations, and Policies.</li>
</ol>
<p>The deployment of the API and its operations pointing at the Azure Function App requires the following components:</p>
<ol>
<li><strong>Azure Role Assignment</strong> - This is the authorisation system that we will use to assign APIMs <a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview">System Assigned Managed Identity</a> access to the applications Key Vault, specifically the SAS Key secrets.</li>
<li><strong>APIM API and API Operations</strong> - Represents a set of available operations with each containing a reference to a backend service that implements the API.</li>
<li><strong>APIM Named Values</strong> - This is a global collection of name/value pairs within the APIM Instance. Using APIM Policies we can use Named Values to further API configuration. Named Values can store constant string values, secrets, or more importantly Key Vault references to secrets.</li>
<li><strong>APIM Backend</strong> - APIM Backend is an HTTP service that implements a front-end API. Setting up the Backend means that we can abstract backend service information, promoting reusability and improved governance.</li>
<li><strong>APIM Policies</strong> - Policies are statements that are run sequentially on a given request or response for an API. These statements further our ability to configure the API and its abilities such as adding further parameters, setting a backend, making use of configured Named Values.</li>
</ol>
<p>The Bicep for <strong>Step 1</strong> is 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;-webkit-text-size-adjust:none;"><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">APIM</span> <span style="color:#8be9fd;font-style:italic">Instance</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">// ** 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;A prefix used to identify the api resources&#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">apiPrefixName</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 resources will be deployed to - defaulting to the resource group location&#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;The environment that the resources are being deployed 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">env</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 apim publisher email&#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">apimPublisherEmail</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 apim publisher 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">apimPublisherName</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:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">apimInstanceName</span> = <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">apiPrefixName</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">env</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">apim&#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:#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 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 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 style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 style="color:#8be9fd;font-style:italic">sku</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">capacity</span>: <span style="color:#8be9fd;font-style:italic">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;Consumption&#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 style="color:#8be9fd;font-style:italic">publisherEmail</span>: <span style="color:#8be9fd;font-style:italic">apimPublisherEmail</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">publisherName</span>: <span style="color:#8be9fd;font-style:italic">apimPublisherName</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><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">apimInstanceName</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#8be9fd;font-style:italic">apimInstanceName</span>
</span></span></code></pre></div><p>The Bicep for <strong>step 2</strong> makes use of module deployments and policies loaded as text into variables.
Further to this, the Function operations are defined within a JSON control file so that the Bicep templates can remain agnostic to operation implementation. The JSON control file also allows the definition of multiple API operations per Function due to a function allowing multiple HTTP Methods such as GET and POST.</p>
<p>The structure of the operations within the Control file allow for APIM to provide a different Operation name to that specified on the backend such as:</p>
<ul>
<li>APIM /HWGET &ndash;&gt; Function /HellowWorld</li>
</ul>
<p>Each Operation will also detail which Function it is associated with as to utilise the correct Named Value in APIM containing reference to KeyVault Function SaS Key.</p>
<p>The JSON Control file is 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;-webkit-text-size-adjust:none;"><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;name&#34;</span>: <span style="color:#f1fa8c">&#34;HelloWorldGet&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;backendFunctionName&#34;</span>: <span style="color:#f1fa8c">&#34;HelloWorld&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;rewriteUrl&#34;</span>: <span style="color:#f1fa8c">&#34;/HelloWorld&#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;displayName&#34;</span>: <span style="color:#f1fa8c">&#34;Hello World GET&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;method&#34;</span>: <span style="color:#f1fa8c">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;urlTemplate&#34;</span>: <span style="color:#f1fa8c">&#34;/HWGET&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;description&#34;</span>: <span style="color:#f1fa8c">&#34;Hello World GET&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;templateParameters&#34;</span>: [],
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;request&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;queryParameters&#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;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 style="color:#ff79c6">&#34;required&#34;</span>: <span style="color:#ff79c6">false</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;headers&#34;</span>: []
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;responses&#34;</span>: [
</span></span><span style="display:flex;"><span>                {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;statusCode&#34;</span>: <span style="color:#bd93f9">200</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:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;HelloWorldPost&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;backendFunctionName&#34;</span>: <span style="color:#f1fa8c">&#34;HelloWorld&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;rewriteUrl&#34;</span>: <span style="color:#f1fa8c">&#34;/HelloWorld&#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;displayName&#34;</span>: <span style="color:#f1fa8c">&#34;Hello World POST&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;method&#34;</span>: <span style="color:#f1fa8c">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;urlTemplate&#34;</span>: <span style="color:#f1fa8c">&#34;/HWPOST&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;description&#34;</span>: <span style="color:#f1fa8c">&#34;Hello World POST&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;templateParameters&#34;</span>: [],
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;request&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;queryParameters&#34;</span>: [],
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;headers&#34;</span>: []
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;responses&#34;</span>: [
</span></span><span style="display:flex;"><span>                {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;statusCode&#34;</span>: <span style="color:#bd93f9">200</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>The Main deployment template is 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;-webkit-text-size-adjust:none;"><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">Function</span> <span style="color:#8be9fd;font-style:italic">App</span> <span style="color:#8be9fd;font-style:italic">APIM</span> <span style="color:#8be9fd;font-style:italic">API</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;Name of the Function App to add as a backend&#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">functionAppName</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;Name of the APIM instance&#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">apimInstanceName</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;Name of the Key Vault instance&#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">keyVaultName</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;Name of the API to create in APIM&#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">apiName</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;APIM API path&#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">apimAPIPath</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;APIM API display 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">apimAPIDisplayName</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">// Function App Base URL</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">funcBaseUrl</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></span><span style="display:flex;"><span><span style="color:#6272a4">// Key Vault Read Access</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefinitionId</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 style="color:#6272a4">// All Operations Policy</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">apimAPIPolicyRaw</span> = <span style="color:#50fa7b">loadTextContent</span>(<span style="color:#f1fa8c">&#39;./APIM-Policies/APIMAllOperationsPolicy.xml&#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">apimAPIPolicy</span> = <span style="color:#50fa7b">replace</span>(<span style="color:#8be9fd;font-style:italic">apimAPIPolicyRaw</span>, <span style="color:#f1fa8c">&#39;__apiName__&#39;</span>, <span style="color:#8be9fd;font-style:italic">apiName</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Operation Policy Template</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">apimOperationPolicyRaw</span> = <span style="color:#50fa7b">loadTextContent</span>(<span style="color:#f1fa8c">&#39;./APIM-Policies/APIMOperationPolicy.xml&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Operation List and Details</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">apimApiOperations</span> = <span style="color:#50fa7b">loadJsonContent</span>(<span style="color:#f1fa8c">&#39;apimApiConfigurations/helloWorldApiOperationsConfiguration.json&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Obtain single distinct list of functions used in operations </span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">allFunction</span> = <span style="color:#50fa7b">map</span>(<span style="color:#8be9fd;font-style:italic">apimApiOperations</span>, <span style="color:#8be9fd;font-style:italic">op</span> =&gt; <span style="color:#8be9fd;font-style:italic">op</span>.<span style="color:#8be9fd;font-style:italic">backendFunctionName</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">uniqueFunctions</span> = <span style="color:#50fa7b">union</span>(<span style="color:#8be9fd;font-style:italic">allFunction</span>, <span style="color:#8be9fd;font-style:italic">allFunction</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;Retrieve the existing APIM Instance, will add APIs and Policies to this resource&#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 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">apimInstanceName</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 Function App API in APIM&#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">functionAppAPI</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis@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">displayName</span>: <span style="color:#8be9fd;font-style:italic">apimAPIDisplayName</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">path</span>: <span style="color:#8be9fd;font-style:italic">apimAPIPath</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">protocols</span>: [
</span></span><span style="display:flex;"><span>      <span style="color:#f1fa8c">&#39;https&#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:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Retrieve the existing 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">functionAppName</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 function App API operations&#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">functionAppAPIOperation</span> <span style="color:#f1fa8c">&#39;Modules/apimOperation.azuredeploy.bicep&#39;</span> = [
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">for</span> <span style="color:#8be9fd;font-style:italic">operation</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">apimApiOperations</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">operation</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-deploy&#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">parentName</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">apimInstance</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">functionAppAPI</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 style="color:#8be9fd;font-style:italic">apiManagementApiOperationDefinition</span>: <span style="color:#8be9fd;font-style:italic">operation</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;Retrieve the existing application Key Vault 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">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 existing function app func 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">vaultFunctionAppKey</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults/secrets@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">for</span> <span style="color:#8be9fd;font-style:italic">function</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">uniqueFunctions</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">functionAppName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">function</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">keyVault</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 app 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">for</span> (<span style="color:#8be9fd;font-style:italic">function</span>, <span style="color:#8be9fd;font-style:italic">index</span>) <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">uniqueFunctions</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">keyVaultSecretsUserRoleDefinitionId</span>, <span style="color:#8be9fd;font-style:italic">keyVault</span>.<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#8be9fd;font-style:italic">function</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 style="color:#8be9fd;font-style:italic">index</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></span><span style="display:flex;"><span>        <span style="color:#f1fa8c">&#39;Microsoft.Authorization/roleDefinitions&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefinitionId</span>
</span></span><span style="display:flex;"><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><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 values for the function app API keys&#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">functionAppBackendNamedValues</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">for</span> (<span style="color:#8be9fd;font-style:italic">function</span>, <span style="color:#8be9fd;font-style:italic">index</span>) <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">uniqueFunctions</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">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">function</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">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">function</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;functionApp&#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">&#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">function</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:#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">index</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><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 backend for the Function App 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">functionAppBackend</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:#8be9fd;font-style:italic">funcBaseUrl</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">resourceId</span>: <span style="color:#50fa7b">uri</span>(<span style="color:#50fa7b">environment</span>().<span style="color:#8be9fd;font-style:italic">resourceManager</span>, <span style="color:#8be9fd;font-style:italic">functionApp</span>.<span style="color:#8be9fd;font-style:italic">id</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></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 a policy for the function App API and all its operations - linking the function app 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">functionAppAPIAllOperationsPolicy</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis/policies@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;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">functionAppAPI</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">value</span>: <span style="color:#8be9fd;font-style:italic">apimAPIPolicy</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></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">functionAppBackend</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;Add query strings via policy&#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">operationPolicy</span> <span style="color:#f1fa8c">&#39;./Modules/apimOperationPolicy.azuredeploy.bicep&#39;</span> = [
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">for</span> (<span style="color:#8be9fd;font-style:italic">operation</span>, <span style="color:#8be9fd;font-style:italic">index</span>) <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">apimApiOperations</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;operationPolicy-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operation</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 style="color:#8be9fd;font-style:italic">params</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">parentStructureForName</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">apimInstance</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">functionAppAPI</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operation</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 style="color:#8be9fd;font-style:italic">functionRelativePath</span>: <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">rewriteUrl</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">rawPolicy</span>: <span style="color:#8be9fd;font-style:italic">apimOperationPolicyRaw</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">key</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">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">backendFunctionName</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 style="color:#8be9fd;font-style:italic">dependsOn</span>: [
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">functionAppAPIOperation</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">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span></code></pre></div><p>The APIM All Operations Policy template provides the link to the APIM Function App Backend as shown below:</p>
<blockquote>
<p>The Delete Set Header is used to remove subscription key headers from the forwarded request to the backend. For more information see <a href="/post/2023/11/apim-subscription-key-header/"><em>Azure API Management | Unintentional Pass through of Subscription Key Header</em></a>.</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;-webkit-text-size-adjust:none;"><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:#ff79c6">&lt;set-backend-service</span> <span style="color:#50fa7b">id=</span><span style="color:#f1fa8c">&#34;functionapp-backend-policy&#34;</span> <span style="color:#50fa7b">backend-id=</span><span style="color:#f1fa8c">&#34;__apiName__&#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;Ocp-Apim-Subscription-Key&#34;</span> <span style="color:#50fa7b">exists-action=</span><span style="color:#f1fa8c">&#34;delete&#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><p>The APIM Operation Policy template is used to conduct a uri rewrite to point to the specific Function backend as defined in the JSON Control file. The Policy also appends the Function specific SAS Key &ldquo;code&rdquo; Named Value ref to the query parameter set. The actual value is obtained on invocation from KeyVault.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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;code&#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>__key__<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><p>The API Operation Module deployment makes use of Bicep User Defined Types to conduct validation of the Operation Control JSON 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;-webkit-text-size-adjust:none;"><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">API</span> <span style="color:#8be9fd;font-style:italic">Operation</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">// ** 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:#6272a4">// TYPES: APIM API Operation</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// - API Operation Definition</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// - Query Parameter</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// - Template Parameter</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// - Header</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// - Response</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;APIM API Operation 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:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">apiOperationDefinition</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:#50fa7b">maxLength</span>(<span style="color:#8be9fd;font-style:italic">80</span>)
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The resource name&#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 backend function name&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">backendFunctionName</span>: <span style="color:#8be9fd;font-style:italic">string</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:#50fa7b">maxLength</span>(<span style="color:#8be9fd;font-style:italic">1000</span>)
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Relative URL rewrite template for the Function backend (in policy).&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">rewriteUrl</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 Operation Contract&#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">minLength</span>(<span style="color:#8be9fd;font-style:italic">1</span>)
</span></span><span style="display:flex;"><span>    @<span style="color:#50fa7b">maxLength</span>(<span style="color:#8be9fd;font-style:italic">300</span>)
</span></span><span style="display:flex;"><span>    @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Operation Name.&#39;</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:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;A Valid HTTP Operation Method. Typical Http Methods like GET, PUT, POST but not limited by only them.&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">method</span>: <span style="color:#8be9fd;font-style:italic">string</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:#50fa7b">maxLength</span>(<span style="color:#8be9fd;font-style:italic">1000</span>)
</span></span><span style="display:flex;"><span>    @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Relative URL template identifying the target resource for this operation. May include parameters. Example: /customers/{cid}/orders/{oid}/?date={date}&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">urlTemplate</span>: <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>    @<span style="color:#50fa7b">maxLength</span>(<span style="color:#8be9fd;font-style:italic">1000</span>)
</span></span><span style="display:flex;"><span>    @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Description of the operation. May include HTML formatting tags.&#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;Collection of URL template parameters.&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">templateParameters</span>: <span style="color:#8be9fd;font-style:italic">templateParameter</span>[]?
</span></span><span style="display:flex;"><span>    @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;An entity containing request details.&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">request</span>: {
</span></span><span style="display:flex;"><span>      @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Collection of operation request query parameters.&#39;</span>)
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">queryParameters</span>: <span style="color:#8be9fd;font-style:italic">queryParameter</span>[]?
</span></span><span style="display:flex;"><span>      @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Collection of operation request headers.&#39;</span>)
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">headers</span>: <span style="color:#8be9fd;font-style:italic">header</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 Operation responses.&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">responses</span>: <span style="color:#8be9fd;font-style:italic">response</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:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">queryParameter</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Parameter name.&#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;Parameter type.&#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">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Specifies whether parameter is required or not.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">required</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 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">templateParameter</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Parameter name.&#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;Parameter type.&#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">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Specifies whether parameter is required or not.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">required</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 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">header</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Header name.&#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;Header type.&#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">string</span>
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Specifies whether header is required or not.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">required</span>: <span style="color:#8be9fd;font-style:italic">bool</span>?
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Header values.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">values</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:#8be9fd;font-style:italic">type</span> <span style="color:#8be9fd;font-style:italic">response</span> = {
</span></span><span style="display:flex;"><span>  @<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Operation response HTTP status code.&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">statusCode</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">// ** 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;API Management Service API Name Path&#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">parentName</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;Definition of the operation to create&#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">apiManagementApiOperationDefinition</span> <span style="color:#8be9fd;font-style:italic">apiOperationDefinition</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:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Deploy function App API operation&#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">functionAppAPIGetOperation</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis/operations@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">parentName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">apiManagementApiOperationDefinition</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 style="color:#8be9fd;font-style:italic">properties</span>: <span style="color:#8be9fd;font-style:italic">apiManagementApiOperationDefinition</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 style="color:#6272a4">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span></code></pre></div><p>The API Operation Policy Module Deployment 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;-webkit-text-size-adjust:none;"><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">APIM</span> <span style="color:#8be9fd;font-style:italic">FUNC</span> <span style="color:#8be9fd;font-style:italic">API</span> <span style="color:#8be9fd;font-style:italic">Operation</span> <span style="color:#8be9fd;font-style:italic">Policy</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 Parent naming structure for the Policy&#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">parentStructureForName</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 function relative path&#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">functionRelativePath</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 raw policy document template&#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">rawPolicy</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 named value name for the workflow key&#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">key</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">// ** 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">policyURI</span> = <span style="color:#50fa7b">replace</span>(<span style="color:#8be9fd;font-style:italic">rawPolicy</span>, <span style="color:#f1fa8c">&#39;__uri__&#39;</span>, <span style="color:#8be9fd;font-style:italic">functionRelativePath</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">policyKEY</span> = <span style="color:#50fa7b">replace</span>(<span style="color:#8be9fd;font-style:italic">policyURI</span>, <span style="color:#f1fa8c">&#39;__key__&#39;</span>, <span style="color:#8be9fd;font-style:italic">key</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;Add query strings via policy&#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">operationPolicy</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis/operations/policies@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">parentStructureForName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/policy&#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">value</span>: <span style="color:#8be9fd;font-style:italic">policyKEY</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></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>Hope this helps, and have fun.</p>
]]></content:encoded></item><item><title>Speaking | Azure Security Do's and Don'ts: A Developer's Checklist for Secure Azure Applications</title><link>https://andrewilson.co.uk/post/2024/08/speaking-azure-security-dos-and-donts/</link><pubDate>Thu, 15 Aug 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/08/speaking-azure-security-dos-and-donts/</guid><description>&lt;p&gt;
&lt;img src="https://andrewilson.co.uk/images/posts/2024/08/AzureOnAir.png" alt="AzureOnAir"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=h5s7UIfnlUo&amp;amp;list=PLfIG-b2nAHLdP4fk7ck53BqXzyArGg8ss&amp;amp;index=6"&gt;
&lt;img src="https://img.shields.io/badge/WATCH_NOW:_Azure_Security_Do%27s_and_Don%27ts-darkred?logo=youtube&amp;amp;style=for-the-badge" alt="Static Badge"&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I recently had the privilege to be hosted on the &lt;a href="https://turbo360.com/podcast"&gt;Azure on Air&lt;/a&gt; podcast by the &lt;a href="https://turbo360.com/"&gt;Turbo360&lt;/a&gt; team. I had a great conversation with Lex discussing the importance of a &amp;ldquo;security first&amp;rdquo; mindset in the world of Azure solutions, and how this mindset should be carried out as a priority in every stage from Requirements Gathering, Design, Development, and Release.&lt;/p&gt;
&lt;p&gt;During our time together we discussed topics such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Three A&amp;rsquo;s&lt;/strong&gt;: Access, Authentication, and Authorisation:&lt;/p&gt;</description><content:encoded><![CDATA[<p>
  <img src="/images/posts/2024/08/AzureOnAir.png" alt="AzureOnAir">

</p>
<p><a href="https://www.youtube.com/watch?v=h5s7UIfnlUo&amp;list=PLfIG-b2nAHLdP4fk7ck53BqXzyArGg8ss&amp;index=6">
  <img src="https://img.shields.io/badge/WATCH_NOW:_Azure_Security_Do%27s_and_Don%27ts-darkred?logo=youtube&amp;style=for-the-badge" alt="Static Badge">

</a></p>
<p>I recently had the privilege to be hosted on the <a href="https://turbo360.com/podcast">Azure on Air</a> podcast by the <a href="https://turbo360.com/">Turbo360</a> team. I had a great conversation with Lex discussing the importance of a &ldquo;security first&rdquo; mindset in the world of Azure solutions, and how this mindset should be carried out as a priority in every stage from Requirements Gathering,  Design, Development, and Release.</p>
<p>During our time together we discussed topics such as:</p>
<ul>
<li>
<p><strong>The Three A&rsquo;s</strong>: Access, Authentication, and Authorisation:</p>
<p>By keeping these concepts in mind throughout, developers can ensure their solutions are secure and driven with defined access routes.</p>
</li>
<li>
<p><strong>Blast Radius</strong>: Minimise the Impact of Security Breaches:</p>
<p>Following the good practice of Principal of Least Privilege(PoLP), developers should consider the security impact of granting services and identities permissions and access that are beyond their required need.</p>
</li>
<li>
<p><strong>Managed Identities</strong>: Removing Manual Management:</p>
<p>Azure has made considerable efforts in Managed Identities and Role Based Access Control(RBAC), thus removing the need to use keys that need to be rolled over or may be visible and vulnerable to attackers.</p>
</li>
<li>
<p><strong>Observability</strong>: Tracking Integrations End-to-End:</p>
<p>Observability is critical in ensuring that Azure solutions are working as intended and within acceptable bounds. This is more achievable than ever with tools such as Application Insights and Log Analytics.</p>
</li>
</ul>
<p>Have a watch and enjoy.</p>
]]></content:encoded></item><item><title>webpack | Build Time Environment Variables With Azure DevOps Yaml CI/CD</title><link>https://andrewilson.co.uk/post/2024/07/webpack-build-time-environment-variables/</link><pubDate>Thu, 25 Jul 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/07/webpack-build-time-environment-variables/</guid><description>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;One of my recent projects has involved the use of a static module bundler called &lt;a href="https://webpack.js.org/"&gt;webpack&lt;/a&gt; to bundle a typescript site so I can serve static content from a &lt;a href="https://learn.microsoft.com/en-us/azure/static-web-apps/overview"&gt;Static Web App&lt;/a&gt; in Azure.&lt;/p&gt;
&lt;p&gt;For a while now the site content has not deviated between environments [ dev / test / prod ] and therefore we have simply built and bundled the site for deployment.&lt;/p&gt;
&lt;p&gt;Recent changes however have required that there be some deviation between environments, at which point the question raised, at what point should this deviation be set or retrieved?&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>One of my recent projects has involved the use of a static module bundler called <a href="https://webpack.js.org/">webpack</a> to bundle a typescript site so I can serve static content from a <a href="https://learn.microsoft.com/en-us/azure/static-web-apps/overview">Static Web App</a> in Azure.</p>
<p>For a while now the site content has not deviated between environments [ dev / test / prod ] and therefore we have simply built and bundled the site for deployment.</p>
<p>Recent changes however have required that there be some deviation between environments, at which point the question raised, at what point should this deviation be set or retrieved?</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#ff79c6">const</span> someConfigurationSetup <span style="color:#ff79c6">=</span> <span style="color:#f1fa8c">&#34;Static but deviates between environments&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">const</span> someConfigurationSetupPartTwo <span style="color:#ff79c6">=</span> <span style="color:#f1fa8c">&#34;Static but deviates between environments&#34;</span>;
</span></span></code></pre></div><p>Due to the site having a backing Azure Function serving its API calls, I had two available options:</p>
<ol>
<li>Is the deviation something that should be served at runtime?
<blockquote>
<p>Points to the Azure Function APIs.</p>
</blockquote>
</li>
<li>Is the deviation something that should be baked in at build time?
<blockquote>
<p>Points to the webpack bundler.</p>
</blockquote>
</li>
</ol>
<p>The deviation in this case does not change throughout the running of the site and also has no security constraints preventing the deviation from being served as static content. My choice therefore proceeded with the second option: webpack bundler.</p>
<p>But how do I achieve this with the webpack bundler? I have a Azure DevOps CI/CD Yaml pipeline that builds and packages the site using npm and the webpack bundler ready for the environment deployments. What is the most appropriate method that will fit in with my current methods?</p>
<h2 id="solution">Solution</h2>
<p>After some digging, I came across the <a href="https://webpack.js.org/plugins/environment-plugin/">webpack EnvironmentPlugin</a>. The use of the Environment Plugin allows me to specify environment variables within my typescript that will then be resolved by the webpack bundler at build time.</p>
<p>This changes the typescript shown above 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;-webkit-text-size-adjust:none;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#ff79c6">const</span> someConfigurationSetup <span style="color:#ff79c6">=</span> process.env.CONFIG_SETUP_ONE;
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">const</span> someConfigurationSetupPartTwo <span style="color:#ff79c6">=</span> process.env.CONFIG_SETUP_TWO;
</span></span></code></pre></div><p>By default if <code>process.env.VARIABLE</code> is not defined by your webpack.config.js, you will receive a ReferenceError: <code>process is not defined</code>.</p>
<p>Defining the environment variables in the webpack.config.js is done within the plugins section such as the following:</p>
<pre tabindex="0"><code class="language-webpack" data-lang="webpack">module.exports = async (env, options) =&gt; {

  const config = [
    {
      devtool: ...
      entry: {
        ...
      },
      resolve: {
        ...
      },
      module: {
        rules: [
          ...
        ],
      },
      plugins: [
        new DefinePlugin({
          &#39;process.env.CONFIG_SETUP_ONE&#39;: JSON.stringify(process.env.CONFIG_SETUP_ONE),
          &#39;process.env.CONFIG_SETUP_TWO&#39;: JSON.stringify(process.env.CONFIG_SETUP_TWO),
        }),
        ...
      ],
    },
    {
	...
    }
  ];

  return config;
};
</code></pre><p>With the plugin and variables defined, the webpack bundler will now resolve my variables with any environment variables defined through methods such as:</p>
<ol>
<li>Setting through PowerShell (<em>environment variable setup will differ per scripting language</em>):
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-PowerShell" data-lang="PowerShell"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$env:CONFIG_SETUP_ONE</span> = <span style="color:#f1fa8c">&#34;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$env:CONFIG_SETUP_TWO</span> = <span style="color:#f1fa8c">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>npm run build
</span></span></code></pre></div></li>
<li>Setting through <a href="https://webpack.js.org/plugins/environment-plugin/#dotenvplugin">.env file</a></li>
</ol>
<p>If no environment variables are found, the variable resolve will result in one of the following:</p>
<ol>
<li><code>undefined</code> for variables that must be provided during bundling.</li>
<li><code>null</code> if they are optional.</li>
</ol>
<p>In my case I would like the variables to be resolved within my Azure DevOps Yaml CI/CD Pipeline for the respective environment.</p>
<p>Thankfully, not much work was required here from my part. I am already making use of <a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups?view=azure-devops&amp;tabs=yaml">Library Variable Groups</a> within my pipeline for the respective environment stages.
In DevOps pipelines, variables are made available to scripts and tasks through environment variables such as the one I configured: CONFIG_SETUP_ONE.
This has resulted in no change to the existing yaml task for the resolve to work:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>      - <span style="color:#ff79c6">job</span>: <span style="color:#f1fa8c">&#39;Build_Test_Solution&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">pool</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">vmImage</span>: windows-latest
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">variables</span>:
</span></span><span style="display:flex;"><span>          - <span style="color:#ff79c6">group</span>: Dev <span style="color:#6272a4"># Holds the Environment Variables such as CONFIG_SETUP_ONE, CONFIG_SETUP_TWO</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          - <span style="color:#ff79c6">task</span>: Npm@1
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">displayName</span>: npm install
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">workingDir</span>: <span style="color:#f1fa8c">&#39;$(System.DefaultWorkingDirectory)/Project&#39;</span>
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">verbose</span>: <span style="color:#ff79c6">false</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          - <span style="color:#ff79c6">task</span>: Npm@1
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">displayName</span>: npm run build for test environment
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">command</span>: custom
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">workingDir</span>: <span style="color:#f1fa8c">&#39;$(System.DefaultWorkingDirectory)/Project&#39;</span>
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">verbose</span>: <span style="color:#ff79c6">false</span>
</span></span><span style="display:flex;"><span>              <span style="color:#ff79c6">customCommand</span>: run build
</span></span></code></pre></div><p>Hope this helps, and have fun.</p>
]]></content:encoded></item><item><title>Azure Role Based Access Control (RBAC) | Removing Orphaned Role Assignments</title><link>https://andrewilson.co.uk/post/2024/07/removing-orphaned-rbac-role-assignments/</link><pubDate>Thu, 04 Jul 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/07/removing-orphaned-rbac-role-assignments/</guid><description>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;Deploying solutions into Azure that rely on Role Based Access often involve us creating IaC automation for the assignment of roles, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A services access to Key Vault&lt;/li&gt;
&lt;li&gt;A services access to a Key Vault specific secret&lt;/li&gt;
&lt;li&gt;A services access to a storage account&lt;/li&gt;
&lt;li&gt;A services access to a Service Bus Queue or Topic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In many of these instances we may wish to leverage the source resource identity (&lt;em&gt;System Assigned Managed Identity&lt;/em&gt;) for the assigned access.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>Deploying solutions into Azure that rely on Role Based Access often involve us creating IaC automation for the assignment of roles, such as:</p>
<ul>
<li>A services access to Key Vault</li>
<li>A services access to a Key Vault specific secret</li>
<li>A services access to a storage account</li>
<li>A services access to a Service Bus Queue or Topic</li>
</ul>
<p>In many of these instances we may wish to leverage the source resource identity (<em>System Assigned Managed Identity</em>) for the assigned access.</p>
<p>But what happens when we delete the source resource, are the role assignments applied on the target resources removed?</p>
<p>The answer&hellip; No they are not.</p>
<p>Your target resources are left with orphaned role assignments. In many cases the use of User Assigned Managed Identity is used to avoid this particular issue as the identity exists regardless of the resource life cycle, at which point there is no orphaning.</p>
<p>But whats the big problem, just redeploy and everything should line up again right?</p>
<p>Unfortunately this is not the case. If you follow through with this sentiment, you will receive the following error from the Azure Resource Manager:</p>
<p><code>Tenant ID, application ID, principal ID, and scope are not allowed to be updated. (code: RoleAssignmentUpdateNotPermitted)</code></p>
<p>The reasoning behind is role assignments require a globally unique identifier (GUID) of which out of <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/scenarios-rbac#name">good practices you have made deterministic</a> for solution deployments. So when you have deleted and recreated the resource the underlying principal ID has now changed, but the role assignment name has not. This infers the problem, you cannot create two role assignments with the same name, even in different Subscriptions, followed with properties of an existing role assignment cannot be changed.</p>
<p>Surly there is a automated process in Azure that will remove these for me? Sadly this is not the case and it has pushed many in resulting to manually searching for and removing these orphaned assignments. This is tedious and time consuming, not to mention requiring the user to have <a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#general"><code>User Access Administrator</code></a> role access which you may not wish to grant to everyone.</p>
<h2 id="solution">Solution</h2>
<p>The solution I came up with uses PowerShell and the az cli to retrieve all role assignments for a given subscription. This is then filtered down to any orphaned assignments scoped to resources in a specific resource group (<em>scope can be changed to what you need</em>). Given that there are orphaned assignments these are then removed  through the az cli.</p>
<p>The script 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;-webkit-text-size-adjust:none;"><code class="language-PowerShell" data-lang="PowerShell"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$ResourceGroupName</span> = <span style="color:#f1fa8c">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$roleAssignments</span> = az role assignment list --all | <span style="color:#8be9fd;font-style:italic">ConvertFrom-Json</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$orphaned</span> = <span style="color:#8be9fd;font-style:italic">$roleAssignments</span> | <span style="color:#8be9fd;font-style:italic">Where-Object</span> { (<span style="color:#8be9fd;font-style:italic">$_</span>.principalName <span style="color:#ff79c6">-eq</span> <span style="color:#f1fa8c">&#34;&#34;</span>) <span style="color:#ff79c6">-and</span> (<span style="color:#8be9fd;font-style:italic">$_</span>.scope <span style="color:#ff79c6">-match</span> <span style="color:#f1fa8c">&#34;resourcegroups/</span><span style="color:#8be9fd;font-style:italic">$ResourceGroupName</span><span style="color:#f1fa8c">&#34;</span>) }
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$orphaned</span>.Count
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$orphaned</span> | <span style="color:#8be9fd;font-style:italic">ForEach-Object</span> { az role assignment delete --ids <span style="color:#8be9fd;font-style:italic">$_</span>.id }
</span></span></code></pre></div><p>To run this script you will need the following Roles:</p>
<ul>
<li>Directory.Read.All</li>
<li>User Access Administrator (<em>At the respective Scope</em>)</li>
</ul>
<p>To avoid assigning <code>User Access Administrator</code> to anyone who may need to run this script, I placed this script into a CI/CD pipeline of which the Pipeline Identity has the roles applied as shown above. This way, the development team can remove orphaned role assignments with ease without the need for elevation of privilege.</p>
<blockquote>
<p>⚠️ Best Practice of Least Privilege.</p>
</blockquote>
<p>Hope this helps, and have fun</p>
]]></content:encoded></item><item><title>Azure API Management | Enable Tracing for an API</title><link>https://andrewilson.co.uk/post/2024/06/apim-enable-tracing-for-an-api/</link><pubDate>Sun, 02 Jun 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/06/apim-enable-tracing-for-an-api/</guid><description>&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;For a while now I have made good use of the Trace functionality in the API Management (APIM) Test Client. If you haven&amp;rsquo;t, I would highly advise having a &lt;a href="https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-api-inspector#trace-a-call-in-the-portal"&gt;look&lt;/a&gt;. The Trace functionality allows us to unveil (debug) the complexity and inner workings of our reverse proxy APIs (their routing / hierarchical policies / alternate backends / caching / etc.). With the Portal this is fairly trivial to do, you simply:&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>For a while now I have made good use of the Trace functionality in the API Management (APIM) Test Client. If you haven&rsquo;t, I would highly advise having a <a href="https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-api-inspector#trace-a-call-in-the-portal">look</a>. The Trace functionality allows us to unveil (debug) the complexity and inner workings of our reverse proxy APIs (their routing / hierarchical policies / alternate backends / caching / etc.). With the Portal this is fairly trivial to do, you simply:</p>
<ol>
<li>Navigate to your API Management instance.</li>
<li>Select one of your APIs that you would like to Trace.</li>
<li>Select the Test tab.</li>
<li>Select one of the Operations to send a request to.</li>
<li>Select Trace.</li>
</ol>
<p>
  <img src="/images/posts/2024/06/APIMTracePortal.png" alt="API Tracing Portal">

</p>
<p>By enabling tracing on the API you are now able to inspect each part of the request:</p>
<ul>
<li><strong>Inbound</strong> | The original request received from the caller and the policies applied.</li>
<li><strong>Backend</strong> | The requests API Management sent to the backend and the response it received.</li>
<li><strong>Outbound</strong> | The policies applied to the response before sending back to the caller.</li>
<li><strong>On error</strong> | The errors that occurred during processing of the request and any policies applied to those errors.</li>
</ul>
<blockquote>
<p>⚠️ <strong>Important</strong></p>
<p>Be careful when enabling tracing as it can expose sensitive information in the trace data.</p>
</blockquote>
<h2 id="problem-space">Problem Space</h2>
<p>Although the APIM Test client has certainly proven useful, I have wondered if there are any other methods of enabling and obtaining the trace for a given API. More importantly, brining the development experience back to something that we are use to such as Visual Studio Code, Postman, etc.</p>
<p>Just over a week ago, I had a conversation with the APIM product group, of which they mentioned that the service has a REST API that can be used to enable trace functionality. After a bit of digging, I found the <a href="https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-api-inspector#enable-tracing-for-an-api">documentation</a> around this and thought I would share my findings and implementation with you.</p>
<blockquote>
<p>⚠️ <strong>Important</strong></p>
<p>Before we get started, please bare in mind:</p>
<ul>
<li>The API Management REST API version 2023-05-01-preview or later is required.</li>
<li>The Identity used to call the REST API must be assigned the Contributor or higher role on the API Management instance.</li>
</ul>
</blockquote>
<p>Enabling tracing on an API through the REST API is a four step process:</p>
<ol>
<li>We need to obtain trace credentials using the <a href="https://learn.microsoft.com/en-us/rest/api/apimanagement/gateway/list-debug-credentials?view=rest-apimanagement-2023-05-01-preview&amp;tabs=HTTP">List Debug Credentials API</a>.</li>
<li>Enable Tracing on your API by adding the Trace Credential token as a header (<code>Apim-Debug-Authorization</code>) to your request.</li>
<li>Given that the Trace Credential token was valid, we need to retrieve the Trace ID from the response header <code>Apim-Trace-Id</code>.
<blockquote>
<p>Invalid Cases Include:</p>
<ul>
<li>
<p><strong>Token expired</strong> | The response will include a <code>Apim-Debug-Authorization-Expired</code> header with information about expiration date.</p>
</li>
<li>
<p><strong>Token obtained for wrong API</strong> | The response will include a <code>Apim-Debug-Authorization-WrongAPI</code> header with an error message.</p>
</li>
</ul>
</blockquote>
</li>
<li>We need to retrieve the trace log by calling the <a href="https://learn.microsoft.com/en-us/rest/api/apimanagement/gateway/list-trace?view=rest-apimanagement-2023-05-01-preview&amp;tabs=HTTP">List Trace API</a> using the Trace ID from the previous step.</li>
</ol>
<p>In both steps 1 and 4, you will need to provide a valid Authorization Bearer token where the identity is assigned Contributor or higher on the APIM instance.</p>
<p>Given that this is not the simplicity that we know and love in the Test Client, making and orchestrating these various Rest calls can and most certainly will take time when conducted singularly. What we need is tooling or extensions to such tooling that will enable us to orchestrate these various calls so that we can obtain the trace logs with repeatable ease.</p>
<h2 id="my-solution">My Solution</h2>
<p>I required a solution that would allow me to orchestrate the various calls with repeatable ease whilst also keeping me close to the tooling that I know and love. My solution is the <a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client">REST Client</a> extension in VS Code. Using this extension I can:</p>
<ol>
<li>
<p>Orchestrate the various Rest calls required.</p>
</li>
<li>
<p>Use variables to make as much of the calls required somewhat generic.</p>
</li>
<li>
<p>The extension provides me the ability to obtain an AAD (Entra ID) token.</p>
<p>By default if a token has already been obtained, the extension will reuse the previous token for the specified directory from an in-memory cache. Expired tokens are refreshed automatically. (The token cache can be manually cleared, or is by default cleared upon closure of VS Code).</p>
</li>
</ol>
<p>The implementation that I have come to 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;-webkit-text-size-adjust:none;"><code class="language-http" data-lang="http"><span style="display:flex;"><span># ***************************************************************************************************************
</span></span><span style="display:flex;"><span># Enable tracing for an APIM API
</span></span><span style="display:flex;"><span># ------------------------------
</span></span><span style="display:flex;"><span>#
</span></span><span style="display:flex;"><span># ⚠️Note:
</span></span><span style="display:flex;"><span>#    The following steps require API Management REST API version 2023-05-01-preview or later. 
</span></span><span style="display:flex;"><span>#    You must be assigned the Contributor or higher role on the API Management instance to call the REST API.
</span></span><span style="display:flex;"><span>#
</span></span><span style="display:flex;"><span>#  Steps:
</span></span><span style="display:flex;"><span>#  1. Obtain trace credentials by calling the List debug credentials API.
</span></span><span style="display:flex;"><span>#  2. Call the API with the trace credentials.
</span></span><span style="display:flex;"><span>#  3. Retrieve trace logs.
</span></span><span style="display:flex;"><span>#
</span></span><span style="display:flex;"><span>#  Author: Andrew Wilson
</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># Custom Variables 
</span></span><span style="display:flex;"><span># -----------------
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@subscriptionId = REPLACEME
</span></span><span style="display:flex;"><span>@resourceGroup = REPLACEME
</span></span><span style="display:flex;"><span>@apimServiceName = REPLACEME
</span></span><span style="display:flex;"><span>@apiName = REPLACEME
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># -----------------
</span></span><span style="display:flex;"><span>#    Rest Steps
</span></span><span style="display:flex;"><span># -----------------
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>### 1.Obtain trace credentials by calling the List debug credentials API.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># @name TraceCreds
</span></span><span style="display:flex;"><span>POST https://management.azure.com/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroup}}/providers/Microsoft.ApiManagement/service/{{apimServiceName}}/gateways/managed/listDebugCredentials
</span></span><span style="display:flex;"><span>?api-version=2023-05-01-preview
</span></span><span style="display:flex;"><span>Content-Type: application/json
</span></span><span style="display:flex;"><span>Authorization: {{$aadToken}}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    &#34;credentialsExpireAfter&#34;: &#34;PT1H&#34;,
</span></span><span style="display:flex;"><span>    &#34;apiId&#34;: &#34;/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroup}}/providers/Microsoft.ApiManagement/service/{{apimServiceName}}/apis/{{apiName}}&#34;,
</span></span><span style="display:flex;"><span>    &#34;purposes&#34;: [&#34;tracing&#34;]
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>### 2.Call the API with the trace credentials.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># @name ApiOperationTracing
</span></span><span style="display:flex;"><span>Author your API request here but make sure to keep the following Header in place.
</span></span><span style="display:flex;"><span>Apim-Debug-Authorization: {{TraceCreds.response.body.$.token}}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>### 3.Retrieve Trace Logs
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span># @name ObtainedTraceLogs
</span></span><span style="display:flex;"><span>POST https://management.azure.com/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroup}}/providers/Microsoft.ApiManagement/service/{{apimServiceName}}/gateways/managed/listTrace
</span></span><span style="display:flex;"><span>?api-version=2023-05-01-preview
</span></span><span style="display:flex;"><span>Content-Type: application/json
</span></span><span style="display:flex;"><span>Authorization: {{$aadToken}}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    &#34;traceId&#34;: &#34;{{ApiOperationTracing.response.headers.Apim-Trace-Id}}&#34;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Have a play and see if this extends your tracing capabilities.</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>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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>Azure API Management | Unintentional Removal of Request Forwarding to Backend</title><link>https://andrewilson.co.uk/post/2024/05/apim-policy-hierarchy-request-forwarding/</link><pubDate>Fri, 03 May 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/05/apim-policy-hierarchy-request-forwarding/</guid><description>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;I have recently been working on an API scoped policy within API Management, the policy ideally should not be impacted by any policies defined higher up in the hierarchy.&lt;/p&gt;
&lt;p&gt;For reference, this means that any policies defined at the Product, Workspace, or Global level will not be inherited at the API scope for the given API Definition. See diagram below:&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://andrewilson.co.uk/images/posts/2024/05/APIMPolicyScopes.png" alt="Policy Scopes"&gt;
&lt;/p&gt;
&lt;p&gt;Ideally this means that my API traffic will start at my non-hierarchical policy definition, conduct any policy processing prior to being sent off to the backend, and then sent back to the calling outbound system.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>I have recently been working on an API scoped policy within API Management, the policy ideally should not be impacted by any policies defined higher up in the hierarchy.</p>
<p>For reference, this means that any policies defined at the Product, Workspace, or Global level will not be inherited at the API scope for the given API Definition. See diagram below:</p>
<p>
  <img src="/images/posts/2024/05/APIMPolicyScopes.png" alt="Policy Scopes">

</p>
<p>Ideally this means that my API traffic will start at my non-hierarchical policy definition, conduct any policy processing prior to being sent off to the backend, and then sent back to the calling outbound system.</p>
<blockquote>
<p>Example Policy definition with the removal of <code>&lt;base /&gt;</code> which indicates inheritance:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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></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></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></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></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></blockquote>
<p>In practice this is not what happened.</p>
<p>The API that I had defined should have successfully returned with a Status Code of 200 and a Json Payload. However, what I actually received was a successful API run with a return of Status Code 200 but no Json Payload.</p>
<p>Further investigation into this highlighted that the API request was never making it out to the Backend.</p>
<p>This being the case, a reviewal of the higher scoped policies showed that there was indeed a policy that is core to the APIM reverse proxy behaviour, and I had excluded this due to my lack of inheritance.</p>
<blockquote>
<p>Offending Policy that was excluded due to my lack of inheritance</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-xml" data-lang="xml"><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;forward-request</span> <span style="color:#ff79c6">/&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">&lt;/backend&gt;</span>
</span></span></code></pre></div></blockquote>
<p>For reference, the <a href="https://learn.microsoft.com/en-us/azure/api-management/forward-request-policy">forward-request policy</a> forwards the incoming request to the backend service specified in the request context. And more importantly the policy is included in the Global Backend Policy by default.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Two things came from this,</p>
<ol>
<li>I fixed my problem by applying the forward-request policy in my API policy definition.</li>
<li><strong>More importantly</strong>, Always review hierarchical policies when removing inheritance as there may be behaviour that you will require, even if you didn&rsquo;t place it there.</li>
</ol>
]]></content:encoded></item><item><title>Azure API Management | Governing Product Visibility and Access via Groups</title><link>https://andrewilson.co.uk/post/2024/04/apim-users-and-groups/</link><pubDate>Tue, 30 Apr 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/04/apim-users-and-groups/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;In API Management, users and groups are a core aspect of the Developer Portal and are used to manage the visibility and access to respective products and their APIs.&lt;/p&gt;
&lt;p&gt;One of the common questions that I often get asked is, &amp;ldquo;how do I appropriately govern the groups effectively so that I can ensure that the correct groups and users have access to the appropriate resources and those who don&amp;rsquo;t&amp;hellip; well don&amp;rsquo;t?&amp;rdquo;.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>In API Management, users and groups are a core aspect of the Developer Portal and are used to manage the visibility and access to respective  products and their APIs.</p>
<p>One of the common questions that I often get asked is, &ldquo;how do I appropriately govern the groups effectively so that I can ensure that the correct groups and users have access to the appropriate resources and those who don&rsquo;t&hellip; well don&rsquo;t?&rdquo;.</p>
<h2 id="background">Background</h2>
<p>API Management has three immutable built in system groups used to govern access:</p>
<p>
  <img src="/images/posts/2024/04/APIMSystemGroups.png" alt="Overview">

</p>
<ul>
<li>
<p><strong>Administrators</strong> | These are not consumers or customers of your APIs but are rather the users who build and manage the API Management service instances, creating the APIs, operations, and products that are used by developers. You can&rsquo;t add users to this group.</p>
<p>The group contains the admin email account provided at the time of service creation. Azure subscription administrators are members of this group.</p>
</li>
<li>
<p><strong>Developers</strong> | These are customers and consumers that build applications using your APIs. Developers are granted access to and are authenticated into the developer portal. Membership to this group is managed by the system and cannot be added to by you.</p>
<blockquote>
<p><code>Developers (Authenticated Users) invited, created, pulled from external groups, or otherwise signed up are all created as a developer and gain automatic membership to this group.</code></p>
</blockquote>
</li>
<li>
<p><strong>Guests</strong> | These are users such as prospective customers and are able to visit the developer portal without having to be authenticated. APIs that guests can view are managed by yourself as well as the type of access such as read-only access (the ability to view APIs but not call them). As with the previous two groups, membership is managed by the system.</p>
</li>
</ul>
<p>Then there is the ability to create custom groups and or use external groups in associated Microsoft Entra tenants. These custom groups can be used to further govern developer visibility and access to API products.</p>
<blockquote>
<p><strong><code>Note:</code></strong></p>
<p><code>User accounts from MS Entra ID or B2C groups are still created as new developer user entities within APIM and associated with the Developers System group as well as the desired Entra ID group.</code></p>
</blockquote>
<p>
  <img src="/images/posts/2024/04/CustomGroupsAndAssociations.png" alt="Custom Groups and Associations">

</p>
<h2 id="governing-access">Governing Access</h2>
<p>The key to understanding Group Access is to remember that all developers (no matter how they have been created) are associated with the Developers System group. In effect this means that any product that has been associated with the Developers system group will be accessible to all users (except Guests given association).</p>
<p>Therefore do not blindly associate the Developer System group to your products unless you expect all users past, present, and future to have access to them.</p>
<p>Rather in answer create custom groups/associate Entra ID groups of which can be used to provide access to specific products. Further to this, granularity is key for security, following the good practice of least privilege (<em><code>A user or entity should only have access to the specific data, resources and applications needed to complete a required task.</code></em>).</p>
<p>
  <img src="/images/posts/2024/04/Governance.png" alt="Group Governance">

</p>
<p>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>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Managed Api Connectors require more than just the connection details to be detailed in the Logic Apps connections.json configuration as shown below:&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;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 (&lt;code&gt;Strings | Integers | Booleans&lt;/code&gt;) so that the templates are usable and maintainable by collaborators apart from myself. This usually equated to where possible avoiding the use of &lt;code&gt;Object and Array&lt;/code&gt; parameters, although in many cases the use of these types was inevitable given the complexity of the infrastructure and resources being deployed.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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 API Management | Logic App (Standard) Backend Using a Swagger Definition</title><link>https://andrewilson.co.uk/post/2024/02/standard-logic-app-apim-backend-swagger/</link><pubDate>Thu, 01 Feb 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/02/standard-logic-app-apim-backend-swagger/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend"&gt;
&lt;img src="https://img.shields.io/badge/Repo-Standard--Logic--App--APIM--Backend-blue?logo=github&amp;amp;style=for-the-badge" alt="GitHub Repository"&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After setting up a &lt;a href="https://andrewilson.co.uk/post/2024/01/standard-logic-app-apim-backend/"&gt;Logic App (Standard) Backend in Azure API Management (APIM)&lt;/a&gt; in my last post, I wanted to try and see if I could create a Swagger definition from a Standard Logic App which could then be used to simplify the API authoring process in APIM. This post shows my methods of doing so.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you haven&amp;rsquo;t already I would recommend reading my previous &lt;a href="https://andrewilson.co.uk/post/2024/01/standard-logic-app-apim-backend/"&gt;&lt;strong&gt;post&lt;/strong&gt;&lt;/a&gt; as this one will be working off of the building blocks of the last.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p><a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend">
  <img src="https://img.shields.io/badge/Repo-Standard--Logic--App--APIM--Backend-blue?logo=github&amp;style=for-the-badge" alt="GitHub Repository">

</a></p>
<p>After setting up a <a href="/post/2024/01/standard-logic-app-apim-backend/">Logic App (Standard) Backend in Azure API Management (APIM)</a> in my last post, I wanted to try and see if I could create a Swagger definition from a Standard Logic App which could then be used to simplify the API authoring process in APIM. This post shows my methods of doing so.</p>
<blockquote>
<p>If you haven&rsquo;t already I would recommend reading my previous <a href="/post/2024/01/standard-logic-app-apim-backend/"><strong>post</strong></a> as this one will be working off of the building blocks of the last.</p>
</blockquote>
<p>As with my previous post, the high-level architecture has not changed and neither has the overall aim as shown below.

  <img src="/images/posts/2024/01/Overview.png" alt="Overview">

</p>
<p>The design aims to abstract the backend from the api operations, i.e. the backend points to the Logic App and the individual operations point to the respective workflows. The design also specifies granular access to the workflow Shared-Access-Signature (sig) held in the applications specific KeyVault (<em>to see further details on this, see <a href="/post/2023/11/rbac-key-vault-specific-secret/">Azure RBAC Key Vault | Role Assignment for Specific Secret</a></em>). Furthermore, the additional required parameters that are necessary to call a workflow have been implemented through APIM policies to remove the need for callers to understand backend implementation.</p>
<p>I have opted for Infrastructure as Code (IaC) as my method of implementation, specifically Bicep. I have broken down the implementation of the diagram above into two parts, Application Deployment, and API Deployment.</p>
<h3 id="problem-space">Problem Space</h3>
<p>Having come across the ARM REST API to generate a Swagger definition for a <a href="https://learn.microsoft.com/en-us/rest/api/logic/workflows/list-swagger?view=rest-logic-2016-06-01&amp;tabs=HTTP">consumption Logic App</a>, I went searching for the same functionality for Standard Logic Apps. Given a short time searching, I found the <a href="https://learn.microsoft.com/en-us/rest/api/appservice/workflows?view=rest-appservice-2022-03-01">REST APIs</a> situated under App Services with the Swagger generation API no where to be seen.</p>
<p>But would this stop me&hellip; No.</p>
<p>Knowing that I can retrieve a detailed definition of a Standard Logic App through the Azure Resource Manager, I started looking at methods of stripping out the detail that I would need to construct a Swagger Definition.</p>
<p>My chosen method to do this is through a PowerShell script using both ARM REST APIs and the az cli. This choice will allow me to run the script as non-interactive in future DevOps CI/CD pipelines.</p>
<h2 id="application-deployment">Application Deployment</h2>
<p>Our first step toward generating the Swagger definition for our Logic App is to deploy an instance of our Logic App to Azure. The process has not changed from the last post apart from the addition of step 4. As shown in the diagram below, step 4 is where we will use the PowerShell script that I have written to extract the core details of our Logic App and workflows to construct a Swagger Definition.</p>
<p>
  <img src="/images/posts/2024/01/Application-Deployment-Swagger.png" alt="Application Deployment">

</p>
<p>The script has been designed to take a ControlFile.json that you update with one or more HTTP triggered workflows and API details to assist in the retrieval and generation of the definition. There is an example control file in my <a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend/blob/main/SwaggerGenerator/ControlFile.json">GitHub repository</a>.</p>
<p>The <a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend/blob/main/SwaggerGenerator/SpecCreator.ps1">SpecCreator.ps1</a> takes the following parameters:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-PowerShell" data-lang="PowerShell"><span style="display:flex;"><span><span style="color:#6272a4">&lt;#
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .</span><span style="color:#f1fa8c">SYNOPSIS</span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">	Creates a Swagger definition for a select Standard Logic App and its specified Workflows.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .</span><span style="color:#f1fa8c">DESCRIPTION</span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">	Uses a series of parameters to derive the Standard Logic App and the Workflows to include in the base Swagger definition template. 
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER SubscriptionId
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    The ID of the Subscription that the Standard Logic App is Hosted in.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER ResourceGroupName
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    The Resource Group Name that the Standard Logic App is Hosted in.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER LogicAppName
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    The Name of the Standard Logic App to use for the Swagger Definition.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER APITitle
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    API Name used to define the set of operations.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER APIDescription
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    Description of the API and its set of operations.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER APIVersion
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    None-Mandatory - Specified version of the API. Default is 1.0.0.0
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER SpecTemplatePath
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    None-Mandatory - Base path to the swagger definition template, doesn&#39;t include the name of the file.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER ControlFile
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    None-Mandatory - Base path to the json control file used to identify the set of Workflows to extract as operations for the swagger definition, doesn&#39;t include the name of the file.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">	.PARAMETER InteractiveMode
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">	If specified will request the user to interactively login into Azure for az tooling.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    #&gt;</span>
</span></span></code></pre></div><p>The script then uses a series of ARM REST API calls to obtain the following detail:</p>
<ul>
<li>Request Schema (if one is provided).</li>
<li>Workflow Definition.</li>
</ul>
<p>Using the Workflow Definition, we can interrogate the Workflow Trigger for details such as:</p>
<ul>
<li>HTTP Method.</li>
<li>Relative Paths.
<ul>
<li><strong>Note:</strong> Path Parameters are expected to be wrapped with curly braces, for example:
<ul>
<li>/users/{id}</li>
<li>/cars/{carId}/drivers/{driverId}</li>
<li>cars/{carId}/drivers/{driverId}</li>
<li>/{carId}/{driverId}</li>
<li>{carId}</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Once all the Workflow details have been obtained, the script uses an imported tokenised Swagger Definition to generate the respective Definition for your Standard Logic App and Workflows.</p>
<h2 id="api-deployment">API Deployment</h2>
<p>For the most part, the API Deployment has not changed. We are using the same Bicep to create the APIM Instance, APIM Backends/Named Values, and Policies. The main change to our Bicep Deployment occurs in step 2.1 as shown in the diagram below.</p>
<p>
  <img src="/images/posts/2024/01/API-Deployment-with-Swagger.png" alt="API Deployment">

</p>
<p>Rather than constructing the API and API Operations individually through Bicep Resources as done in the previous post, we are going to define the APIM API along with the generated Swagger Definition from earlier steps. APIM validates the Swagger Definition and uses it to construct the API and Operations on our behalf.</p>
<p>The three main differences to our API deployment are:</p>
<ol>
<li>We need to make use of the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-files#loadtextcontent">loadTextContent</a> Bicep function to load the generated Swagger Definition into our Bicep Template.
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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">swaggerDefinition</span> = <span style="color:#50fa7b">loadTextContent</span>(<span style="color:#f1fa8c">&#39;../../SwaggerGenerator/GeneratedSpec.json&#39;</span>)
</span></span></code></pre></div></li>
<li>We need to change the <code>Microsoft.ApiManagement/service/apis</code> resource to accept our generated Swagger Definition:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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 Logic App API in APIM - Loading in Swagger Definition&#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">logicAppAPI</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis@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">displayName</span>: <span style="color:#8be9fd;font-style:italic">apimAPIDisplayName</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">path</span>: <span style="color:#8be9fd;font-style:italic">apimAPIPath</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">protocols</span>: [
</span></span><span style="display:flex;"><span>      <span style="color:#f1fa8c">&#39;https&#39;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">format</span>: <span style="color:#f1fa8c">&#39;swagger-json&#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">swaggerDefinition</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
<li>We need to remove the following Bicep Module call to create the API Operations as this will now be done for us.
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Deploy logic App API operation&#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">logicAppAPIOperation</span> <span style="color:#f1fa8c">&#39;Modules/apimOperation.azuredeploy.bicep&#39;</span> = [<span style="color:#8be9fd;font-style:italic">for</span> <span style="color:#8be9fd;font-style:italic">operation</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">apimAPIOperations</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">operation</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-deploy&#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">parentName</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">apimInstance</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">logicAppAPI</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 style="color:#8be9fd;font-style:italic">lgCallBackObject</span>: <span style="color:#50fa7b">listCallbackUrl</span>(<span style="color:#50fa7b">resourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Web/sites/hostruntime/webhooks/api/workflows/triggers&#39;</span>, <span style="color:#8be9fd;font-style:italic">logicAppName</span>, <span style="color:#f1fa8c">&#39;runtime&#39;</span>, <span style="color:#f1fa8c">&#39;workflow&#39;</span>, <span style="color:#f1fa8c">&#39;management&#39;</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowName</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowTrigger</span>), <span style="color:#f1fa8c">&#39;2022-09-01&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">operationDisplayName</span>: <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">displayName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">operationMethod</span>: <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">method</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">operationName</span>: <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">name</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>
</ol>
<h2 id="summary">Summary</h2>
<p>The main benefit of using this method over constructing the API and Operations through Bicep is that we only need to define the backing API once.</p>
<p>If you would like to spin up a demo of this implementation or would like to use it as the basis of something you are working on, the source to all my work is in 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>Have a go and see if this simplifies your Standard Logic App APIM Configurations.</p>
]]></content:encoded></item><item><title>Azure API Management | Logic App (Standard) Backend</title><link>https://andrewilson.co.uk/post/2024/01/standard-logic-app-apim-backend/</link><pubDate>Tue, 02 Jan 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/01/standard-logic-app-apim-backend/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend"&gt;
&lt;img src="https://img.shields.io/badge/Repo-Standard--Logic--App--APIM--Backend-blue?logo=github&amp;amp;style=for-the-badge" alt="GitHub Repository"&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Updated [31/01/2024]: See&lt;/code&gt; &lt;a href="https://andrewilson.co.uk/post/2024/02/standard-logic-app-apim-backend-swagger/"&gt;New Post&lt;/a&gt; &lt;code&gt;showing methods of linking a Logic App Standard as a Backend to APIM through a Swagger Definition.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have recently been reviewing the method in which a Logic App (Standard) workflow would be setup as an API in API Management. My aim is to overcome and simplify the limitation whereby &lt;a href="https://techcommunity.microsoft.com/t5/azure-paas-blog/import-logic-apps-standard-into-azure-api-management/ba-p/3055490#:~:text=As%20an%20alternative%2C%20to%20import,into%20the%20backend%20and%20frontend."&gt;directly importing a Logic App (Standard) workflow is not available, only in consumption.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After some exploration I believe I have identified a configurable and secure method in setting up the front-to-backend routing as can be seen in the diagram below:
&lt;img src="https://andrewilson.co.uk/images/posts/2024/01/Overview.png" alt="Overview"&gt;
&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p><a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend">
  <img src="https://img.shields.io/badge/Repo-Standard--Logic--App--APIM--Backend-blue?logo=github&amp;style=for-the-badge" alt="GitHub Repository">

</a></p>
<blockquote>
<p><code>Updated [31/01/2024]: See</code> <a href="/post/2024/02/standard-logic-app-apim-backend-swagger/">New Post</a> <code>showing methods of linking a Logic App Standard as a Backend to APIM through a Swagger Definition.</code></p>
</blockquote>
<p>I have recently been reviewing the method in which a Logic App (Standard) workflow would be setup as an API in API Management. My aim is to overcome and simplify the limitation whereby <a href="https://techcommunity.microsoft.com/t5/azure-paas-blog/import-logic-apps-standard-into-azure-api-management/ba-p/3055490#:~:text=As%20an%20alternative%2C%20to%20import,into%20the%20backend%20and%20frontend.">directly importing a Logic App (Standard) workflow is not available, only in consumption.</a></p>
<p>After some exploration I believe I have identified a configurable and secure method in setting up the front-to-backend routing as can be seen in the diagram below:

  <img src="/images/posts/2024/01/Overview.png" alt="Overview">

</p>
<p>The overall design aims to abstract the backend from the api operations, i.e. the backend points to the Logic App and the individual operations point to the respective workflows. The design also specifies granular access to the workflow Shared-Access-Signature (sig) held in the applications specific KeyVault (<em>to see further details on this, see <a href="/post/2023/11/rbac-key-vault-specific-secret/">Azure RBAC Key Vault | Role Assignment for Specific Secret</a></em>). Furthermore, the additional required parameters that are necessary to call a workflow have been implemented through APIM policies to remove the need for callers to understand backend implementation.</p>
<p>I have opted for Infrastructure as Code (IaC) as my method of implementation, specifically Bicep. I have broken down the implementation of the diagram above into two parts, Application Deployment, and API Deployment.</p>
<h2 id="application-deployment">Application Deployment</h2>
<p>The following diagram demonstrates how the application backend has been deployed.

  <img src="/images/posts/2024/01/Application-Deployment.png" alt="ApplicationDeployment">

</p>
<p>The deployment is split into three stages:</p>
<ol>
<li>Deploy the Core Application Components.</li>
<li>Deploy the Logic Workflows to the recently deployed Logic App.</li>
<li>Store the Workflow SAS keys in KeyVault for later secure use.</li>
</ol>
<p>In turn the Bicep for <strong>step 1</strong> is 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;-webkit-text-size-adjust:none;"><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">Application</span> <span style="color:#8be9fd;font-style:italic">Core</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 style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;A prefix used to identify the application resources&#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">applicationPrefixName</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 name of the application used for tags&#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">applicationName</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 resources will be deployed to - defaulting to the resource group location&#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;The environment that the resources are being deployed 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">env</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:#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">applicationKeyVaultName</span> = <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationPrefixName</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">env</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">kv&#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">lgApplicationAppServicePlanName</span> = <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationPrefixName</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">env</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">asp&#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">lgStorageAccountName</span> = <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationPrefixName</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">env</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">st&#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">applicationLogicAppName</span> = <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">applicationPrefixName</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">env</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">logic&#39;</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">isProduction</span> = <span style="color:#8be9fd;font-style:italic">env</span> <span style="color:#ff79c6">==</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:#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;Deploy the Application Specific 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">applicationKeyVaultDeploy</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:#8be9fd;font-style:italic">applicationKeyVaultName</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 style="color:#8be9fd;font-style:italic">Application</span>: <span style="color:#8be9fd;font-style:italic">applicationName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 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">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">enableRbacAuthorization</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">enableSoftDelete</span>: <span style="color:#8be9fd;font-style:italic">isProduction</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 App Service Plan used for Logic App Standard&#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">lgAppServicePlanDeploy</span> <span style="color:#f1fa8c">&#39;Microsoft.Web/serverfarms@2022-09-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">lgApplicationAppServicePlanName</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 style="color:#8be9fd;font-style:italic">Application</span>: <span style="color:#8be9fd;font-style:italic">applicationName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 style="color:#8be9fd;font-style:italic">kind</span>: <span style="color:#f1fa8c">&#39;elastic&#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;WS1&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">tier</span>: <span style="color:#f1fa8c">&#39;WorkflowStandard&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">size</span>: <span style="color:#f1fa8c">&#39;WS1&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">family</span>: <span style="color:#f1fa8c">&#39;WS&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">capacity</span>: <span style="color:#8be9fd;font-style:italic">1</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 Storage Account used for Logic App Standard&#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">lgStorageAccountDeploy</span> <span style="color:#f1fa8c">&#39;Microsoft.Storage/storageAccounts@2023-01-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">lgStorageAccountName</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 style="color:#8be9fd;font-style:italic">Application</span>: <span style="color:#8be9fd;font-style:italic">applicationName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 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 style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">supportsHttpsTrafficOnly</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">minimumTlsVersion</span>: <span style="color:#f1fa8c">&#39;TLS1_2&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">defaultToOAuthAuthentication</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><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@2022-09-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">tags</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Application</span>: <span style="color:#8be9fd;font-style:italic">applicationName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 style="color:#8be9fd;font-style:italic">kind</span>: <span style="color:#f1fa8c">&#39;functionapp,workflowapp&#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">serverFarmId</span>: <span style="color:#8be9fd;font-style:italic">lgAppServicePlanDeploy</span>.<span style="color:#8be9fd;font-style:italic">id</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">publicNetworkAccess</span>: <span style="color:#f1fa8c">&#39;Enabled&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">httpsOnly</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">resource</span> <span style="color:#8be9fd;font-style:italic">config</span> <span style="color:#f1fa8c">&#39;config@2022-09-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 style="color:#8be9fd;font-style:italic">FUNCTIONS_EXTENSION_VERSION</span>: <span style="color:#f1fa8c">&#39;~4&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">FUNCTIONS_WORKER_RUNTIME</span>: <span style="color:#f1fa8c">&#39;node&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">WEBSITE_NODE_DEFAULT_VERSION</span>: <span style="color:#f1fa8c">&#39;~18&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">AzureWebJobsStorage</span>: <span style="color:#f1fa8c">&#39;DefaultEndpointsProtocol=https;AccountName=</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">lgStorageAccountDeploy</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">;AccountKey=</span><span style="color:#f1fa8c">${</span><span style="color:#50fa7b">listKeys</span>(<span style="color:#8be9fd;font-style:italic">lgStorageAccountDeploy</span>.<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#f1fa8c">&#39;2019-06-01&#39;</span>).<span style="color:#8be9fd;font-style:italic">keys</span>[<span style="color:#8be9fd;font-style:italic">0</span>].<span style="color:#8be9fd;font-style:italic">value</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">;EndpointSuffix=core.windows.net&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">WEBSITE_CONTENTAZUREFILECONNECTIONSTRING</span>: <span style="color:#f1fa8c">&#39;DefaultEndpointsProtocol=https;AccountName=</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">lgStorageAccountDeploy</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">;AccountKey=</span><span style="color:#f1fa8c">${</span><span style="color:#50fa7b">listKeys</span>(<span style="color:#8be9fd;font-style:italic">lgStorageAccountDeploy</span>.<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#f1fa8c">&#39;2019-06-01&#39;</span>).<span style="color:#8be9fd;font-style:italic">keys</span>[<span style="color:#8be9fd;font-style:italic">0</span>].<span style="color:#8be9fd;font-style:italic">value</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">;EndpointSuffix=core.windows.net&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">WEBSITE_CONTENTSHARE</span>: <span style="color:#8be9fd;font-style:italic">lgStorageAccountDeploy</span>.<span style="color:#8be9fd;font-style:italic">name</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">AzureFunctionsJobHost__extensionBundle__id</span>: <span style="color:#f1fa8c">&#39;Microsoft.Azure.Functions.ExtensionBundle.Workflows&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#8be9fd;font-style:italic">AzureFunctionsJobHost__extensionBundle__version</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#f1fa8c">&#39;[1.*,&#39;</span><span style="color:#f1fa8c">}${</span><span style="color:#f1fa8c">&#39; 2.0.0)&#39;</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">APP_KIND</span>: <span style="color:#f1fa8c">&#39;workflowApp&#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:#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">keyVaultName</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#8be9fd;font-style:italic">applicationKeyVaultName</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">output</span> <span style="color:#8be9fd;font-style:italic">applicationLogicAppName</span> <span style="color:#8be9fd;font-style:italic">string</span>  = <span style="color:#8be9fd;font-style:italic">applicationLogicAppName</span>
</span></span></code></pre></div><p><strong>Step 2</strong> is the deployment of the workflow definition such as this very simple request response:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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;Response&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Response&#34;</span>,
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;kind&#34;</span>: <span style="color:#f1fa8c">&#34;Http&#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;statusCode&#34;</span>: <span style="color:#bd93f9">200</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></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;triggers&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;When_a_HTTP_request_is_received&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Request&#34;</span>,
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;kind&#34;</span>: <span style="color:#f1fa8c">&#34;Http&#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;kind&#34;</span>: <span style="color:#f1fa8c">&#34;Stateless&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>Step 3</strong> demonstrated below takes a number of workflows, retrieves their SAS keys and creates them as secrets in the Application KeyVault.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><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">Application</span> <span style="color:#8be9fd;font-style:italic">Secrets</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 style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Name of the Logic App to place workflow(s) sig into KeyVault&#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">applicationLogicAppName</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;Name of the Key Vault to place secrets into&#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">keyVaultName</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;Array of Workflows to obtain sigs from.&#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">workflows</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">workflowName</span>: <span style="color:#f1fa8c">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">workflowTrigger</span>: <span style="color:#f1fa8c">&#39;&#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></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;Retrieve the existing 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">logicApp</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">applicationLogicAppName</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 existing 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;Vault the Logic App workflow sig as a secret - Deployment principle requires RBAC permissions to do this&#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">vaultLogicAppKey</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults/secrets@2023-07-01&#39;</span> = [<span style="color:#8be9fd;font-style:italic">for</span> <span style="color:#8be9fd;font-style:italic">workflow</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">workflows</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">logicApp</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">workflow</span>.<span style="color:#8be9fd;font-style:italic">workflowName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-sig&#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">tags</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">ResourceType</span>: <span style="color:#f1fa8c">&#39;LogicAppStandard&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">ResourceName</span>: <span style="color:#8be9fd;font-style:italic">logicApp</span>.<span style="color:#8be9fd;font-style:italic">name</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 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">listCallbackUrl</span>(<span style="color:#50fa7b">resourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Web/sites/hostruntime/webhooks/api/workflows/triggers&#39;</span>, <span style="color:#8be9fd;font-style:italic">logicApp</span>.<span style="color:#8be9fd;font-style:italic">name</span>, <span style="color:#f1fa8c">&#39;runtime&#39;</span>, <span style="color:#f1fa8c">&#39;workflow&#39;</span>, <span style="color:#f1fa8c">&#39;management&#39;</span>, <span style="color:#8be9fd;font-style:italic">workflow</span>.<span style="color:#8be9fd;font-style:italic">workflowName</span>, <span style="color:#8be9fd;font-style:italic">workflow</span>.<span style="color:#8be9fd;font-style:italic">workflowTrigger</span>), <span style="color:#f1fa8c">&#39;2022-09-01&#39;</span>).<span style="color:#8be9fd;font-style:italic">queries</span>.<span style="color:#8be9fd;font-style:italic">sig</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><h2 id="api-deployment">API Deployment</h2>
<p>The following diagram demonstrates how API Management and the Logic App backend API have been deployed.</p>
<p>
  <img src="/images/posts/2024/01/API-Deployment.png" alt="ApplicationDeployment">

</p>
<p>The deployment is split into two stages:</p>
<ol>
<li>Deploy an API Management Service Instance.</li>
<li>Deploy respective Backend, API, API Operations, and Policies.</li>
</ol>
<p>Our deployment of the API and its operations pointing at the Standard Logic App requires the following components:</p>
<ol>
<li><strong>Azure Role Assignment</strong> - This is the authorisation system that we will use to assign APIMs <a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview">System Assigned Managed Identity</a> access to the applications Key Vault, specifically the sig secret.</li>
<li><strong>APIM API and API Operations</strong> - Represents a set of available operations with each containing a reference to a backend service that implements the API.</li>
<li><strong>APIM Named Values</strong> - This is a global collection of name/value pairs within the APIM Instance. Using APIM Policies we can use Named Values to further API configuration. Named Values can store constant string values, secrets, or more importantly Key Vault references to secrets.</li>
<li><strong>APIM Backend</strong> - APIM Backend is an HTTP service that implements a front-end API. Normally with consumption Logic Apps when imported into APIM a Backend Service is automatically created for you. This is not currently possible with Standard Logic Apps. However, Standard Logic Apps have the same foundations as Azure Functions which means that we can set this up through custom backends (i.e. is treated as a Function Backend).
<ul>
<li>Setting up the Backend means that we can abstract backend service information, promoting reusability and improved governance.</li>
</ul>
</li>
<li><strong>APIM Policies</strong> - Policies are statements that are run sequentially on a given request or response for an API. These statements further our ability to configure the API and its abilities such as adding further parameters, setting a backend, making use of configured Named Values.</li>
</ol>
<p>The Bicep for <strong>step 1</strong> is 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;-webkit-text-size-adjust:none;"><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">APIM</span> <span style="color:#8be9fd;font-style:italic">Instance</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 style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;A prefix used to identify the api resources&#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">apiPrefixName</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 resources will be deployed to - defaulting to the resource group location&#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;The environment that the resources are being deployed 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">env</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 apim publisher email&#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">apimPublisherEmail</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 apim publisher 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">apimPublisherName</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:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">apimInstanceName</span> = <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">apiPrefixName</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">env</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">apim&#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:#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 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 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 style="color:#8be9fd;font-style:italic">Environment</span>: <span style="color:#8be9fd;font-style:italic">env</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 style="color:#8be9fd;font-style:italic">sku</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">capacity</span>: <span style="color:#8be9fd;font-style:italic">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;Consumption&#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 style="color:#8be9fd;font-style:italic">publisherEmail</span>: <span style="color:#8be9fd;font-style:italic">apimPublisherEmail</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">publisherName</span>: <span style="color:#8be9fd;font-style:italic">apimPublisherName</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><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">apimInstanceName</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#8be9fd;font-style:italic">apimInstanceName</span>
</span></span></code></pre></div><p>The Bicep for <strong>step 2</strong> makes use of module deployments and policies loaded as text into variables.
The Main deployment template is 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;-webkit-text-size-adjust:none;"><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">Logic</span> <span style="color:#8be9fd;font-style:italic">App</span> <span style="color:#8be9fd;font-style:italic">Standard</span> <span style="color:#8be9fd;font-style:italic">APIM</span> <span style="color:#8be9fd;font-style:italic">API</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;Name of the Logic App to add as a backend&#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;Name of the APIM instance&#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">apimInstanceName</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;Name of the Key Vault instance&#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">keyVaultName</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;Name of the API to create in APIM&#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">apiName</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;APIM API path&#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">apimAPIPath</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;APIM API display 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">apimAPIDisplayName</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;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">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></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">displayName</span>: <span style="color:#f1fa8c">&#39;&#39;</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></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">lgWorkflowName</span>: <span style="color:#f1fa8c">&#39;&#39;</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></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">// Logic App Base URL</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">lgBaseUrl</span> = <span style="color:#f1fa8c">&#39;https://</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">logicApp</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></span><span style="display:flex;"><span><span style="color:#6272a4">// Key Vault Read Access</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefinitionId</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 style="color:#6272a4">// All Operations Policy</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">apimAPIPolicyRaw</span> = <span style="color:#50fa7b">loadTextContent</span>(<span style="color:#f1fa8c">&#39;./APIM-Policies/APIMAllOperationsPolicy.xml&#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">apimAPIPolicy</span> = <span style="color:#50fa7b">replace</span>(<span style="color:#8be9fd;font-style:italic">apimAPIPolicyRaw</span>, <span style="color:#f1fa8c">&#39;__apiName__&#39;</span>, <span style="color:#8be9fd;font-style:italic">apiName</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// Operation Policy Template</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">apimOperationPolicyRaw</span> = <span style="color:#50fa7b">loadTextContent</span>(<span style="color:#f1fa8c">&#39;./APIM-Policies/APIMOperationPolicy.xml&#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:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Retrieve the existing APIM Instance, will add APIs and Policies to this resource&#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 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">apimInstanceName</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 Logic App API in APIM&#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">logicAppAPI</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis@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">displayName</span>: <span style="color:#8be9fd;font-style:italic">apimAPIDisplayName</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">path</span>: <span style="color:#8be9fd;font-style:italic">apimAPIPath</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">protocols</span>: [
</span></span><span style="display:flex;"><span>      <span style="color:#f1fa8c">&#39;https&#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:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Retrieve the existing Logic 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">logicApp</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">logicAppName</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 logic App API operation&#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">logicAppAPIOperation</span> <span style="color:#f1fa8c">&#39;Modules/apimOperation.azuredeploy.bicep&#39;</span> = [<span style="color:#8be9fd;font-style:italic">for</span> <span style="color:#8be9fd;font-style:italic">operation</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">apimAPIOperations</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">operation</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-deploy&#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">parentName</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">apimInstance</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">logicAppAPI</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 style="color:#8be9fd;font-style:italic">lgCallBackObject</span>: <span style="color:#50fa7b">listCallbackUrl</span>(<span style="color:#50fa7b">resourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Web/sites/hostruntime/webhooks/api/workflows/triggers&#39;</span>, <span style="color:#8be9fd;font-style:italic">logicAppName</span>, <span style="color:#f1fa8c">&#39;runtime&#39;</span>, <span style="color:#f1fa8c">&#39;workflow&#39;</span>, <span style="color:#f1fa8c">&#39;management&#39;</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowName</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowTrigger</span>), <span style="color:#f1fa8c">&#39;2022-09-01&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">operationDisplayName</span>: <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">displayName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">operationMethod</span>: <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">method</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">operationName</span>: <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">name</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;Retrieve the existing application Key Vault 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">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 existing logicapp workflow sig 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">vaultLogicAppKey</span> <span style="color:#f1fa8c">&#39;Microsoft.KeyVault/vaults/secrets@2023-07-01&#39;</span> <span style="color:#8be9fd;font-style:italic">existing</span> = [<span style="color:#8be9fd;font-style:italic">for</span> <span style="color:#8be9fd;font-style:italic">operation</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">apimAPIOperations</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">logicAppName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-sig&#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></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 logic app 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 style="color:#8be9fd;font-style:italic">for</span> (<span style="color:#8be9fd;font-style:italic">operation</span>, <span style="color:#8be9fd;font-style:italic">index</span>) <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">apimAPIOperations</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">keyVaultSecretsUserRoleDefinitionId</span>, <span style="color:#8be9fd;font-style:italic">keyVault</span>.<span style="color:#8be9fd;font-style:italic">id</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowName</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">scope</span>: <span style="color:#8be9fd;font-style:italic">vaultLogicAppKey</span>[<span style="color:#8be9fd;font-style:italic">index</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:#8be9fd;font-style:italic">keyVaultSecretsUserRoleDefinitionId</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><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 values for the logic app API sigs&#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">logicAppBackendNamedValues</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/namedValues@2022-08-01&#39;</span> = [<span style="color:#8be9fd;font-style:italic">for</span> (<span style="color:#8be9fd;font-style:italic">operation</span>, <span style="color:#8be9fd;font-style:italic">index</span>) <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">apimAPIOperations</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">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-sig&#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">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-sig&#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;sig&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f1fa8c">&#39;logicApp&#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">&#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">operation</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 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">vaultLogicAppKey</span>[<span style="color:#8be9fd;font-style:italic">index</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><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 backend for the Logic App 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">logicAppBackend</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:#8be9fd;font-style:italic">lgBaseUrl</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">resourceId</span>: <span style="color:#50fa7b">uri</span>(<span style="color:#50fa7b">environment</span>().<span style="color:#8be9fd;font-style:italic">resourceManager</span>, <span style="color:#8be9fd;font-style:italic">logicApp</span>.<span style="color:#8be9fd;font-style:italic">id</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></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 a policy for the logic App API and all its operations - linking the logic app 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">logicAppAPIAllOperationsPolicy</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis/policies@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;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">logicAppAPI</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">value</span>: <span style="color:#8be9fd;font-style:italic">apimAPIPolicy</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></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">logicAppBackend</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;Add query strings via policy&#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">operationPolicy</span> <span style="color:#f1fa8c">&#39;./Modules/apimOperationPolicy.azuredeploy.bicep&#39;</span> = [<span style="color:#8be9fd;font-style:italic">for</span> (<span style="color:#8be9fd;font-style:italic">operation</span>, <span style="color:#8be9fd;font-style:italic">index</span>) <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">apimAPIOperations</span>: {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;operationPolicy-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operation</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 style="color:#8be9fd;font-style:italic">params</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">parentStructureForName</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">apimInstance</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">logicAppAPI</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operation</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 style="color:#8be9fd;font-style:italic">rawPolicy</span>: <span style="color:#8be9fd;font-style:italic">apimOperationPolicyRaw</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">apiVersion</span>: <span style="color:#50fa7b">listCallbackUrl</span>(<span style="color:#50fa7b">resourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Web/sites/hostruntime/webhooks/api/workflows/triggers&#39;</span>, <span style="color:#8be9fd;font-style:italic">logicAppName</span>, <span style="color:#f1fa8c">&#39;runtime&#39;</span>, <span style="color:#f1fa8c">&#39;workflow&#39;</span>, <span style="color:#f1fa8c">&#39;management&#39;</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowName</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowTrigger</span>), <span style="color:#f1fa8c">&#39;2022-09-01&#39;</span>).<span style="color:#8be9fd;font-style:italic">queries</span>[<span style="color:#f1fa8c">&#39;api-version&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">sp</span>: <span style="color:#50fa7b">listCallbackUrl</span>(<span style="color:#50fa7b">resourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Web/sites/hostruntime/webhooks/api/workflows/triggers&#39;</span>, <span style="color:#8be9fd;font-style:italic">logicAppName</span>, <span style="color:#f1fa8c">&#39;runtime&#39;</span>, <span style="color:#f1fa8c">&#39;workflow&#39;</span>, <span style="color:#f1fa8c">&#39;management&#39;</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowName</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowTrigger</span>), <span style="color:#f1fa8c">&#39;2022-09-01&#39;</span>).<span style="color:#8be9fd;font-style:italic">queries</span>.<span style="color:#8be9fd;font-style:italic">sp</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">sv</span>: <span style="color:#50fa7b">listCallbackUrl</span>(<span style="color:#50fa7b">resourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Web/sites/hostruntime/webhooks/api/workflows/triggers&#39;</span>, <span style="color:#8be9fd;font-style:italic">logicAppName</span>, <span style="color:#f1fa8c">&#39;runtime&#39;</span>, <span style="color:#f1fa8c">&#39;workflow&#39;</span>, <span style="color:#f1fa8c">&#39;management&#39;</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowName</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowTrigger</span>), <span style="color:#f1fa8c">&#39;2022-09-01&#39;</span>).<span style="color:#8be9fd;font-style:italic">queries</span>.<span style="color:#8be9fd;font-style:italic">sv</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">sig</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">-</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-sig}}&#39;</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">logicAppAPIOperation</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 APIM All Operations Policy template is as follows:</p>
<blockquote>
<p>The Deletion Set Header is used to remove subscription key headers from the forwarded request to the backend. For more information see <a href="/post/2023/11/apim-subscription-key-header/"><em>Azure API Management | Unintentional Pass through of Subscription Key Header</em></a>.</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;-webkit-text-size-adjust:none;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>
</span></span><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:#ff79c6">&lt;set-backend-service</span> <span style="color:#50fa7b">id=</span><span style="color:#f1fa8c">&#34;logicapp-backend-policy&#34;</span> <span style="color:#50fa7b">backend-id=</span><span style="color:#f1fa8c">&#34;__apiName__&#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;Ocp-Apim-Subscription-Key&#34;</span> <span style="color:#50fa7b">exists-action=</span><span style="color:#f1fa8c">&#34;delete&#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><p>The APIM Operation Policy template is as follows:</p>
<blockquote>
<p>By setting the parameters as policy means that those calling the API do not need to be aware of backend configuration. Thus allowing for complete separation of concerns.</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;-webkit-text-size-adjust:none;"><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;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;set-query-parameter</span> <span style="color:#50fa7b">name=</span><span style="color:#f1fa8c">&#34;sp&#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>__sp__<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;set-query-parameter</span> <span style="color:#50fa7b">name=</span><span style="color:#f1fa8c">&#34;sv&#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>__sv__<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;set-query-parameter</span> <span style="color:#50fa7b">name=</span><span style="color:#f1fa8c">&#34;sig&#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>__sig__<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><p>The API Operation Module deployment 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;-webkit-text-size-adjust:none;"><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">API</span> <span style="color:#8be9fd;font-style:italic">Operation</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 style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;API Management Service API Name Path&#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">parentName</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;Name of the API Operation&#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">operationName</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;Display name for the API operation&#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">operationDisplayName</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;API Operation Method e.g. GET&#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">operationMethod</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;Logic App Call Back object containing URL and other details&#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">lgCallBackObject</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:#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">operationUrlBase</span> = <span style="color:#50fa7b">split</span>(<span style="color:#50fa7b">split</span>(<span style="color:#8be9fd;font-style:italic">lgCallBackObject</span>.<span style="color:#8be9fd;font-style:italic">value</span>, <span style="color:#f1fa8c">&#39;?&#39;</span>)[<span style="color:#8be9fd;font-style:italic">0</span>], <span style="color:#f1fa8c">&#39;/api&#39;</span>)[<span style="color:#8be9fd;font-style:italic">1</span>]
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">hasRelativePath</span> = <span style="color:#8be9fd;font-style:italic">lgCallBackObject</span>.?<span style="color:#8be9fd;font-style:italic">relativePath</span> <span style="color:#ff79c6">!=</span> <span style="color:#ff79c6">null</span> ? <span style="color:#ff79c6">true</span> : <span style="color:#ff79c6">false</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">pathParametersList</span> = <span style="color:#8be9fd;font-style:italic">hasRelativePath</span> ? <span style="color:#8be9fd;font-style:italic">lgCallBackObject</span>.<span style="color:#8be9fd;font-style:italic">relativePathParameters</span> : []
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">pathParameters</span> = [<span style="color:#8be9fd;font-style:italic">for</span> <span style="color:#8be9fd;font-style:italic">pathParameter</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">pathParametersList</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">pathParameter</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">type</span>: <span style="color:#f1fa8c">&#39;string&#39;</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">RelativePathHasBeginingSlash</span> = <span style="color:#8be9fd;font-style:italic">hasRelativePath</span> ? <span style="color:#50fa7b">first</span>(<span style="color:#8be9fd;font-style:italic">lgCallBackObject</span>.<span style="color:#8be9fd;font-style:italic">relativePath</span>) <span style="color:#ff79c6">==</span> <span style="color:#f1fa8c">&#39;/&#39;</span> : <span style="color:#ff79c6">false</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">operationUrl</span> = <span style="color:#8be9fd;font-style:italic">hasRelativePath</span> <span style="color:#ff79c6">&amp;&amp;</span> <span style="color:#8be9fd;font-style:italic">RelativePathHasBeginingSlash</span> ? <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operationUrlBase</span><span style="color:#f1fa8c">}${</span><span style="color:#8be9fd;font-style:italic">lgCallBackObject</span>.<span style="color:#8be9fd;font-style:italic">relativePath</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span> : <span style="color:#8be9fd;font-style:italic">hasRelativePath</span> <span style="color:#ff79c6">&amp;&amp;</span> <span style="color:#ff79c6">!</span><span style="color:#8be9fd;font-style:italic">RelativePathHasBeginingSlash</span> ? <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operationUrlBase</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">lgCallBackObject</span>.<span style="color:#8be9fd;font-style:italic">relativePath</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span> : <span style="color:#8be9fd;font-style:italic">operationUrlBase</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;Deploy logic App API operation&#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">logicAppAPIGetOperation</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis/operations@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">parentName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operationName</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">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">displayName</span>: <span style="color:#8be9fd;font-style:italic">operationDisplayName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">method</span>: <span style="color:#8be9fd;font-style:italic">operationMethod</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">urlTemplate</span>: <span style="color:#8be9fd;font-style:italic">operationUrl</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">templateParameters</span>: <span style="color:#8be9fd;font-style:italic">hasRelativePath</span> ? <span style="color:#8be9fd;font-style:italic">pathParameters</span> : <span style="color:#ff79c6">null</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 API Operation Policy Module Deployment 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;-webkit-text-size-adjust:none;"><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">APIM</span> <span style="color:#8be9fd;font-style:italic">LG</span> <span style="color:#8be9fd;font-style:italic">API</span> <span style="color:#8be9fd;font-style:italic">Operation</span> <span style="color:#8be9fd;font-style:italic">Policy</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 Parent naming structure for the Policy&#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">parentStructureForName</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 raw policy document template&#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">rawPolicy</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 Logic App service API version&#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">apiVersion</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 Logic App workflow permissions&#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">sp</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 Logic App workflow version number of the query parameters&#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">sv</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 named value name for the workflow sig&#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">sig</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:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">policyApiVersion</span> = <span style="color:#50fa7b">replace</span>(<span style="color:#8be9fd;font-style:italic">rawPolicy</span>, <span style="color:#f1fa8c">&#39;__api-version__&#39;</span>, <span style="color:#8be9fd;font-style:italic">apiVersion</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">policySP</span> = <span style="color:#50fa7b">replace</span>(<span style="color:#8be9fd;font-style:italic">policyApiVersion</span>, <span style="color:#f1fa8c">&#39;__sp__&#39;</span>, <span style="color:#8be9fd;font-style:italic">sp</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">policySV</span> = <span style="color:#50fa7b">replace</span>(<span style="color:#8be9fd;font-style:italic">policySP</span>, <span style="color:#f1fa8c">&#39;__sv__&#39;</span>, <span style="color:#8be9fd;font-style:italic">sv</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">policySIG</span> = <span style="color:#50fa7b">replace</span>(<span style="color:#8be9fd;font-style:italic">policySV</span>, <span style="color:#f1fa8c">&#39;__sig__&#39;</span>, <span style="color:#8be9fd;font-style:italic">sig</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:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Add query strings via policy&#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">operationPolicy</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis/operations/policies@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">parentStructureForName</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/policy&#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">value</span>: <span style="color:#8be9fd;font-style:italic">policySIG</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></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>Have a go and see if this simplifies your Standard Logic App APIM Configurations.</p>
]]></content:encoded></item><item><title>Windows Terminal | Azure Customisation for PowerShell</title><link>https://andrewilson.co.uk/post/2023/11/windows-terminal-customisation-powershell/</link><pubDate>Thu, 30 Nov 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/11/windows-terminal-customisation-powershell/</guid><description>&lt;p&gt;If you haven&amp;rsquo;t already, placing a customisation on your command prompt for PowerShell is a great way to gain extra context in given activities. For example, if you would like to have a comprehensive overview of your Git status summary information, then &lt;a href="https://github.com/dahlbyk/posh-git"&gt;posh-git&lt;/a&gt; is for you.&lt;/p&gt;
&lt;p&gt;
&lt;img src="https://andrewilson.co.uk/images/posts/2023/11/posh-git.png" alt="posh-git"&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Be Careful:&lt;/strong&gt; Adding customisations will bring latency to start-up&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One customisation that I came across that I really like is the &lt;a href="https://ohmyposh.dev/docs/themes#cloud-native-azure"&gt;cloud-native-azure&lt;/a&gt; theme using &lt;a href="https://ohmyposh.dev/"&gt;Oh My Posh&lt;/a&gt;.
The theme gives context to the following:&lt;/p&gt;</description><content:encoded><![CDATA[<p>If you haven&rsquo;t already, placing a customisation on your command prompt for PowerShell is a great way to gain extra context in given activities. For example, if you would like to have a comprehensive overview of your Git status summary information, then <a href="https://github.com/dahlbyk/posh-git">posh-git</a> is for you.</p>
<p>
  <img src="/images/posts/2023/11/posh-git.png" alt="posh-git">

</p>
<blockquote>
<p><strong>Be Careful:</strong> Adding customisations will bring latency to start-up</p>
</blockquote>
<p>One customisation that I came across that I really like is the <a href="https://ohmyposh.dev/docs/themes#cloud-native-azure">cloud-native-azure</a> theme using <a href="https://ohmyposh.dev/">Oh My Posh</a>.
The theme gives context to the following:</p>
<ul>
<li>Path.</li>
<li>Git status summary with changing colours and glyphs depending on status.</li>
<li>Command execution status.</li>
<li>Terminal Type (pwsh, shell, etc.)</li>
<li>Battery Status.</li>
<li>Date and Time.</li>
<li>Kubernetes Context.</li>
<li>The part I really like | <strong>Azure Subscription Context</strong>.</li>
</ul>
<p>
  <img src="/images/posts/2023/11/cloud-native-azure.png" alt="cloud-native-azure">

</p>
<p>For me, this theme really ticks the box, having both context of git status information and the Azure context in which I may be working is a real productivity assist.</p>
<h2 id="oh-my-posh-theme-install">Oh My Posh Theme Install</h2>
<p>To install and enable the theme for your command prompt for PowerShell, you will need to do the following:</p>
<ol>
<li>Install a font.
<ul>
<li>Often these customisations will make use of glyphs (graphic symbols) to assist in conveying information quickly.</li>
<li>I made use of <a href="https://www.nerdfonts.com/font-downloads">Nerd Font - 0xProto Nerd Font</a></li>
<li>Simply:
<ul>
<li>Install the font.</li>
<li>Unzip the download.</li>
<li>Right Click and Install the .ttf files.</li>
</ul>
</li>
</ul>
</li>
<li>Enable the font in Windows Terminal.
<ul>
<li>Open the Windows Terminal.</li>
<li>Press <code>ctrl+,</code> to get to Windows Terminal Settings.</li>
<li>Under Profiles, select Windows PowerShell.</li>
<li>Under Additional Settings, select Appearance.</li>
<li>Select the drop-down for Font Face and select your downloaded font. For me this is 0xProto Nerd Font.</li>
</ul>
</li>
<li>Install <a href="https://ohmyposh.dev/docs/installation/windows">Oh My Posh</a>.</li>
<li>Apply your theme of choice, or in this case <a href="https://ohmyposh.dev/docs/themes#cloud-native-azure">cloud-native-azure</a>.
<ul>
<li>Simply:
<ul>
<li>Load your PowerShell profile through your editor of choice:
<ul>
<li><code>&gt; code $PROFILE</code> / <code>&gt; notepad $PROFILE</code></li>
<li>If you receive a path error, a profile may not have been setup for you yet. To sort this, use the following PowerShell command:
<ul>
<li><code>&gt; new-item -type file -path $profile -force</code></li>
<li>Then run the previous command to open the profile for editing.</li>
</ul>
</li>
</ul>
</li>
<li>Add the following initiation line for oh-my-posh for your given choice of theme
<ul>
<li><code>oh-my-posh init pwsh --config &quot;$env:POSH_THEMES_PATH\cloud-native-azure.omp.json&quot; | Invoke-Expression</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<p>Now the next time you load up your PowerShell terminal, you can enjoy the awesome customisation and hopefully get some productivity benefits too.</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>&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>Azure API Management | Unintentional Pass through of Subscription Key Header</title><link>https://andrewilson.co.uk/post/2023/11/apim-subscription-key-header/</link><pubDate>Tue, 21 Nov 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/11/apim-subscription-key-header/</guid><description>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;There is a potential unintentional side effect when you add a &lt;a href="https://learn.microsoft.com/en-us/azure/api-management/api-management-subscriptions"&gt;APIM subscription key&lt;/a&gt; as a header to an inbound request. The header is not stripped from the request prior to being sent to the configured backend service. Rather it is retained.&lt;/p&gt;
&lt;p&gt;If you manage the backing service and are not concerned with the disclosure of the subscription key, then no problem. &lt;strong&gt;However&lt;/strong&gt;, being overly permissive of this information may make your API more vulnerable to security threats and disallows a separation of concerns.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>There is a potential unintentional side effect when you add a <a href="https://learn.microsoft.com/en-us/azure/api-management/api-management-subscriptions">APIM subscription key</a> as a header to an inbound request. The header is not stripped from the request prior to being sent to the configured backend service. Rather it is retained.</p>
<p>If you manage the backing service and are not concerned with the disclosure of the subscription key, then no problem. <strong>However</strong>, being overly permissive of this information may make your API more vulnerable to security threats and disallows a separation of concerns.</p>
<p>The more concerning of the options is where you are using a backing service that is outside of your control, and the backing service being potentially vulnerable to security threats that you are not in a position to manage.</p>
<blockquote>
<p><strong>General Rule of Thumb</strong> : Prevent <em><strong>ANY</strong></em> overly permissive configurations that will make your APIs more vulnerable to security threats.</p>
</blockquote>
<h2 id="solution">Solution</h2>
<p>To strip the header out of the outbound request we can make use of <a href="https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-policies">APIM Policies</a>, more specifically the <a href="https://learn.microsoft.com/en-us/azure/api-management/set-header-policy">set-header 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;-webkit-text-size-adjust:none;"><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;set-header</span> <span style="color:#50fa7b">name=</span><span style="color:#f1fa8c">&#34;Ocp-Apim-Subscription-Key&#34;</span> <span style="color:#50fa7b">exists-action=</span><span style="color:#f1fa8c">&#34;delete&#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><p>Make sure that the header name is set to the name you have used for your subscription key header, <a href="https://learn.microsoft.com/en-us/azure/api-management/api-management-subscriptions#use-a-subscription-key">by default</a> this is set to <em>Ocp-Apim-Subscription-Key</em>.</p>
<p>Lastly, this is not a policy that you have to provide per API Operation, but can be placed at the respective <a href="https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-policies#scopes">scope</a> at which you have enabled APIM Subscriptions.</p>
]]></content:encoded></item><item><title>Desired State Configuration | Project DSC</title><link>https://andrewilson.co.uk/post/2023/10/project-dsc/</link><pubDate>Mon, 23 Oct 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/10/project-dsc/</guid><description>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;I have often found myself with a peeked interest into any method that will simplify both the on-boarding and return to a given project.&lt;/p&gt;
&lt;p&gt;Why the interest you may ask&amp;hellip;&lt;/p&gt;
&lt;p&gt;Well, in most cases when working on a given project (&lt;em&gt;Greenfield or Brownfield&lt;/em&gt;), one member of the team will scout ahead to make sure all the engineering tasks are complete. This often allows the development team to work in parallel without stepping on each others toes or be reclined to a halt due to engineering tasks that can only be conducted by a smaller subset of the team. These tasks often include:&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>I have often found myself with a peeked interest into any method that will simplify both the on-boarding and return to a given project.</p>
<p>Why the interest you may ask&hellip;</p>
<p>Well, in most cases when working on a given project (<em>Greenfield or Brownfield</em>), one member of the team will scout ahead to make sure all the engineering tasks are complete. This often allows the development team to work in parallel without stepping on each others toes or be reclined to a halt due to engineering tasks that can only be conducted by a smaller subset of the team. These tasks often include:</p>
<ul>
<li>Setting up a DevOps project.</li>
<li>Setting up a repository.</li>
<li>Setting up one or more Azure Subscriptions.</li>
<li>Setting up the base frameworks, tooling, and repository structure.</li>
<li>Setting up any Infrastructure as Code for the project.</li>
<li>Setting up CI/CD pipelines.</li>
<li>etc.</li>
</ul>
<p>For most of these tasks, they will be setup once and be applicable for everyone on the team, no further setup required. However, what about individual local project setup (development environments)?</p>
<ul>
<li>Each contributing member needs to have conducted project setup in order to contribute, but how they setup is un-managed.</li>
<li>Setup needs to be documented, but is reliant on members reading and following this documentation.</li>
<li>Project setup is a duration of time multiplied by the number of members on the team.</li>
<li>In the event of an existing project, if not documented correctly, setting up the local project takes investigation and furthered time spent.</li>
</ul>
<p>In comes my latest find&hellip;</p>
<h2 id="winget-configuration">WinGet Configuration</h2>
<p>With a package manager such as WinGet, we would typically need to install any pre-requisites in an imperative sequencing of steps such as:</p>
<ul>
<li>Git tooling.</li>
<li>NodeJS version x.xx</li>
<li>etc..</li>
</ul>
<p>However, with <a href="https://learn.microsoft.com/en-us/windows/package-manager/configuration/">WinGet Configuration</a> we now have the ability to define declarative configuration as code that documents and setups the list of pre-requisites for the environment though a single command <code>winget configure</code>.</p>
<p>This method becomes an answer to many of the problems stipulated above:</p>
<ol>
<li>Each contributing member needs to have conducted project setup in order to contribute, but how they setup is un-managed.
<ul>
<li><em>Through tracked configuration as code there is a repeatable, reliable, and a managed method of setup.</em></li>
</ul>
</li>
<li>Setup needs to be documented, but is reliant on members reading and following this documentation.
<ul>
<li><em>Configuration as Code can be used as a documenting method for any pre-requisites their versions, dependencies and conditions. Setup is not reliant on interpretation or human error.</em></li>
</ul>
</li>
<li>Project setup is a duration of time multiplied by the number of members on the team.
<ul>
<li><em>Automation of manual setup will reduce time spent by each contributing member.</em></li>
</ul>
</li>
<li>In the event of an existing project, if not documented correctly, setting up the local project takes investigation and furthered time spent.
<ul>
<li><em>Projects can be easily returned to with the ability to setup your environment exactly how it was when it was last developed.</em></li>
</ul>
</li>
</ol>
<h3 id="getting-started">Getting Started</h3>
<p>To get started, have a review of the WinGet Configuration documentation:</p>
<ul>
<li><a href="https://learn.microsoft.com/en-us/windows/package-manager/configuration/#use-a-winget-configuration-file-to-configure-your-machine">WinGet Configuration</a>.</li>
<li><a href="https://learn.microsoft.com/en-us/windows/package-manager/configuration/create">How to Author a WinGet Configuration file</a>.</li>
<li><a href="https://learn.microsoft.com/en-us/windows/package-manager/winget/configure">WinGet Configure Command</a>.</li>
<li><a href="https://learn.microsoft.com/en-us/windows/package-manager/configuration/check">Checking the trustworthiness of a WinGet Configuration file</a>.</li>
</ul>
<h3 id="example">Example</h3>
<p>I wanted to take this approach to my blog site so that I have a documented and repeatable setup. This resulted in me creating the following Configuration as 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;-webkit-text-size-adjust:none;"><code class="language-Yaml" data-lang="Yaml"><span style="display:flex;"><span><span style="color:#6272a4"># yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># Hugo Site DSC</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># Installs:</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">#   - Git</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">#   - Visual Studio Code</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">#   - NodeJS</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">#   - Hugo</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">properties</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">configurationVersion</span>: <span style="color:#bd93f9">0.2.0</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">resources</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">resource</span>: Microsoft.WinGet.DSC/WinGetPackage
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">directives</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">description</span>: Install Git
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">allowPrerelease</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">settings</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">id</span>: Git.Git
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">source</span>: winget
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">resource</span>: Microsoft.WinGet.DSC/WinGetPackage
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">directives</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">description</span>: Install Visual Studio Code
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">allowPrerelease</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">settings</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">id</span>: Microsoft.VisualStudioCode
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">source</span>: winget
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">resource</span>: Microsoft.WinGet.DSC/WinGetPackage
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">directives</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">description</span>: Install NodeJS
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">allowPrerelease</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">settings</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">id</span>: OpenJS.NodeJS
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">source</span>: winget
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">version</span>: <span style="color:#f1fa8c">&#39;19.8.1&#39;</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">resource</span>: Microsoft.WinGet.DSC/WinGetPackage
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">directives</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">description</span>: Install Hugo
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">allowPrerelease</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">settings</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">id</span>: Hugo.Hugo.Extended
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">source</span>: winget
</span></span></code></pre></div><p>Have a go and see what you think.</p>
]]></content:encoded></item><item><title>BizTalk | Replacing Strong Name Keys</title><link>https://andrewilson.co.uk/post/2023/06/biztalk-replacing-strong-name-keys/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/06/biztalk-replacing-strong-name-keys/</guid><description>&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;Strong Names provide .NET Framework assemblies with unique identities. When the .NET Framework loads a strong-named assembly for a referring assembly, it verifies the strong name signature. If the strong name signature of the assembly cannot be verified, the .NET Framework will not load the assembly.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;!Do not rely on strong names for security. They provide a unique identity only!&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Alternatively in our case, BizTalk assemblies are loaded into the Global Assembly Cache (GAC). In this case, instead of the .NET Framework verifying the assembly identity each time it requires loading, the assembly identity is verified once when installed into the GAC.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>Strong Names provide .NET Framework assemblies with unique identities. When the .NET Framework loads a strong-named assembly for a referring assembly, it verifies the strong name signature. If the strong name signature of the assembly cannot be verified, the .NET Framework will not load the assembly.</p>
<blockquote>
<p><code>!Do not rely on strong names for security. They provide a unique identity only!</code></p>
</blockquote>
<p>Alternatively in our case, BizTalk assemblies are loaded into the Global Assembly Cache (GAC). In this case, instead of the .NET Framework verifying the assembly identity each time it requires loading, the assembly identity is verified once when installed into the GAC.</p>
<p>Why do we need to strong-name our BizTalk assemblies:</p>
<ol>
<li>Strong-naming is required if we want to store our assembly in the GAC.</li>
<li>The assembly can be loaded side by side with other versions of the assembly.</li>
<li>The assembly can be referenced and used by other strong-named assemblies.</li>
<li>Strong-naming can prevent assembly conflicts.</li>
<li>Strong-naming prevents spoofing of your code (<em>malicious user can modify your code but cannot re-sign it as you</em>)
<blockquote>
<p><code>! Only as long as you keep the private key secure !</code></p>
</blockquote>
</li>
</ol>
<h2 id="problem-space">Problem Space</h2>
<p>One of the big <code>NO NO's</code> when it comes to strong-naming assemblies is, do not add, remove, or change the strong naming key that is used to sign the assembly. Reasoning behind is that by modifying the assembly&rsquo;s strong-name key you have effectively changed the assembly&rsquo;s identity. This means any application that uses the assembly will be requiring the assembly with the previous signed public key, thereby .NET Framework will fail verification and or the assembly with that identity wont be found in the GAC.</p>
<p>However, what if you find yourself in the position where:</p>
<ol>
<li>You have lost the Signing Key that was used to strong-name your assembly.</li>
<li>You have previously password protected your Signing Key but have now forgotten/lost the password.</li>
<li>Your Signing Key has been compromised and you believe a malicious user might wish to modify and re-sign.</li>
</ol>
<p>In this situation, we need to find a way to regenerate a signing key, and update our BizTalk Applications referencing points so that it knows to look for our assembly with the new identity.</p>
<h2 id="solution">Solution</h2>
<p>Every strong-named assembly has a public key that it is identified by, in this case referenced by your BizTalk Application. Each BizTalk Application will reference its dependant assemblies(<em>schemas, maps, orchestrations etc.</em>) through its Bindings of which will also contain the public key for the relevant assemblies. It is this public key that will need to change to the new public key in order to retain the correct assembly identity reference. As part of this verification process, you will also need to make sure the you are referencing the correct version of the assembly.</p>
<blockquote>
<p>For Example: Orchestration</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;-webkit-text-size-adjust:none;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#ff79c6">&lt;ModuleRef</span> <span style="color:#50fa7b">Name=</span><span style="color:#f1fa8c">&#34;Application.Orchestrations&#34;</span> <span style="color:#50fa7b">Version=</span><span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span> <span style="color:#50fa7b">Culture=</span><span style="color:#f1fa8c">&#34;neutral&#34;</span> <span style="color:#50fa7b">PublicKeyToken=</span><span style="color:#f1fa8c">&#34;PublicTokenToBeReplaced&#34;</span> <span style="color:#50fa7b">FullName=</span><span style="color:#f1fa8c">&#34;Application.Orchestrations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=PublicTokenToBeReplaced&#34;</span><span style="color:#ff79c6">&gt;</span>
</span></span></code></pre></div><blockquote>
<p>For Example: Pipeline</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;-webkit-text-size-adjust:none;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#ff79c6">&lt;ReceivePipeline</span> <span style="color:#50fa7b">Name=</span><span style="color:#f1fa8c">&#34;Application.Pipelines.FlatFileReceivePipeline&#34;</span> <span style="color:#50fa7b">FullyQualifiedName=</span><span style="color:#f1fa8c">&#34;Application.Pipelines.FlatFileReceivePipeline, Application.Pipelines, Version=1.0.0.0, Culture=neutral, PublicKeyToken=PublicTokenToBeReplaced&#34;</span> <span style="color:#50fa7b">Type=</span><span style="color:#f1fa8c">&#34;1&#34;</span> <span style="color:#50fa7b">TrackingOption=</span><span style="color:#f1fa8c">&#34;ServiceStartEnd MessageSendReceive PipelineEvents&#34;</span> <span style="color:#50fa7b">Description=</span><span style="color:#f1fa8c">&#34;&#34;</span> <span style="color:#ff79c6">/&gt;</span>
</span></span></code></pre></div><p>If you are like me, all my BizTalk Applications are deployed using the BizTalk Deployment Framework allowing me to conduct repeatable deployments without manual configuration and setup between environments. In this case you will need to update your PortBindingsMaster.xml file to replace the public key tokens as shown above.</p>
<p>If deployed manually from scratch, by setting up your ports, orchestrations etc. you will be setting up the new public key link by default. If deploying over the solution, then you will see in your Applications resources two of the same assembly but different PublicKeyToken. You will need to restart host instances (reloads assemblies) and then either update the references to point to the new version of the assembly or you may need to extract the bindings for the application, replace the Public Token, and then import the bindings. Be aware that any secrets held in your bindings will not be extracted and therefore you may need to re-enter these after you import. Once the references to the new assembly are complete, you can remove the old reference.</p>
<h3 id="retrieving-new-and-old-public-key-from-strong-name-key">Retrieving New and Old Public Key from Strong Name Key</h3>
<h4 id="biztalk-server-admin-console">BizTalk Server Admin Console</h4>
<p>The simplest method of retrieving the current (old) public key is to have a look in the BizTalk Server Admin Console.</p>
<ol>
<li>Navigate to your Application in BizTalk.</li>
<li>Navigate to Resources.</li>
<li>Find your assembly in the list of resources you are updating.</li>
<li>The Name of the assembly contains the PublicKeyToken.</li>
</ol>
<p>This method is useful if you have lost the current Strong Name Key, or have forgotten the password.</p>
<h4 id="strong-name-tool-snexe">Strong Name Tool Sn.exe</h4>
<p>This method relies on the fact that you still have the current (old) strong name key (if password protected, the password) and have already created the new key.</p>
<ol>
<li>
<p>Load Visual Studio Developer Command Line.</p>
</li>
<li>
<p>CD to the path that holds your Strong Name Keys</p>
</li>
<li>
<p>Enter the following command</p>
<p><code>sn -p NameOfKey.snk(can also be .pfx) token.snk</code></p>
<blockquote>
<p>This command extracts the public key from the key pair in the provided strong name and stores it in the new file token.snk</p>
</blockquote>
</li>
<li>
<p>(<strong>Password Protected</strong>) If you provided a .pfx file, you will be prompted with a password, enter the password that protects the key.</p>
</li>
<li>
<p>Enter the following command</p>
<p><code>sn -t token.snk</code></p>
<blockquote>
<p>This command displays the public key stored in the new file token.snk</p>
</blockquote>
</li>
</ol>
<p>These steps can be followed for both the old key to be replaced and the new key.</p>
<hr>
<blockquote>
<p><strong>Sources</strong></p>
<ol>
<li>
<p>Using Strong Name Signatures | <a href="https://learn.microsoft.com/en-us/archive/msdn-magazine/2006/july/clr-inside-out-using-strong-name-signatures">https://learn.microsoft.com/en-us/archive/msdn-magazine/2006/july/clr-inside-out-using-strong-name-signatures</a></p>
</li>
<li>
<p>Strong-named assemblies | <a href="https://learn.microsoft.com/en-us/dotnet/standard/assembly/strong-named">https://learn.microsoft.com/en-us/dotnet/standard/assembly/strong-named</a></p>
</li>
<li>
<p>Global Assembly Cache | <a href="https://learn.microsoft.com/en-us/dotnet/framework/app-domains/gac">https://learn.microsoft.com/en-us/dotnet/framework/app-domains/gac</a></p>
</li>
<li>
<p>Creating a Strong-Named BizTalk Assembly | <a href="https://learn.microsoft.com/en-us/biztalk/adapters-and-accelerators/accelerator-swift/lesson-2-creating-a-strong-named-biztalk-assembly-for-the-swiftschemas-project">https://learn.microsoft.com/en-us/biztalk/adapters-and-accelerators/accelerator-swift/lesson-2-creating-a-strong-named-biztalk-assembly-for-the-swiftschemas-project</a></p>
</li>
<li>
<p>Sn.exe (Strong Name Tool) | <a href="https://learn.microsoft.com/en-us/dotnet/framework/tools/sn-exe-strong-name-tool">https://learn.microsoft.com/en-us/dotnet/framework/tools/sn-exe-strong-name-tool</a></p>
</li>
<li>
<p>Deployment Framework for BizTalk Server V5.5 | <a href="http://www.tfabraham.com/BTDFDocs/V5_5/">http://www.tfabraham.com/BTDFDocs/V5_5/</a></p>
</li>
<li>
<p>Strong Naming | <a href="https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/strong-naming">https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/strong-naming</a></p>
</li>
</ol>
</blockquote>
]]></content:encoded></item><item><title>BizTalk | Deploying and Reading Secure SSO Settings</title><link>https://andrewilson.co.uk/post/2023/04/biztalk-secure-sso-settings/</link><pubDate>Wed, 26 Apr 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/04/biztalk-secure-sso-settings/</guid><description>&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;One of the fundamental parts to BizTalk for both configuration and integration applications is &lt;a href="https://learn.microsoft.com/en-us/biztalk/core/enterprise-single-sign-on-sso"&gt;&lt;code&gt;Enterprise Single Sign On&lt;/code&gt;&lt;/a&gt;, or as we will continue to reference as &lt;code&gt;SSO&lt;/code&gt; from now on.&lt;/p&gt;
&lt;p&gt;SSO outside the bounds of BizTalk is an available service that is used to store and transmit encrypted user credentials. However, due to the nature of the service it is fairly generic, this has meant that many middleware applications and custom adapters have been designed to leverage SSO to securely store and transmit secure &lt;code&gt;settings&lt;/code&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>One of the fundamental parts to BizTalk for both configuration and integration applications is  <a href="https://learn.microsoft.com/en-us/biztalk/core/enterprise-single-sign-on-sso"><code>Enterprise Single Sign On</code></a>, or as we will continue to reference as <code>SSO</code> from now on.</p>
<p>SSO outside the bounds of BizTalk is an available service that is used to store and transmit encrypted user credentials. However, due to the nature of the service it is fairly generic, this has meant that many middleware applications and custom adapters have been designed to  leverage SSO to securely store and transmit secure <code>settings</code>.</p>
<p>The SSO System holds any secure values in logical entities called <code>Affiliate Applications</code>. Affiliate Applications represent a system or sub-system, back-end system, or line of business application.</p>
<p>Looking back at BizTalk, each deployed application (<code>Line of business application</code>) is setup in SSO as an Affiliate Application. BizTalk uses the Affiliate Application to store internal configurations such as secure adapter data and configuration.</p>
<blockquote>
<p>This post is written in mind for a BizTalk project that makes use of the <a href="https://github.com/BTDF/DeploymentFramework"><code>BizTalk Deployment Framework (BTDF)</code></a>. I would highly recommend making use of BTDF as it will assist you in conducting repeatable deployments without manual configuration and setup between environments.</p>
</blockquote>
<p>BTDF when added to your BizTalk project will include a <code>SettingsFileGenerator.xml</code>. This file will enable you to configure environment settings that are used to tokenise your PortBindings configuration and more. Another key aspect to this file, is that you can include settings that are utilised by your integrations at run-time such as database connection strings. When you deploy your application(s) to BizTalk, the selected environment settings are deployed to the Affiliate Application in SSO. These settings are encrypted at rest, and are available from anywhere within the BizTalk group.</p>
<h2 id="problem-space">Problem Space</h2>
<p>SSO is brilliant for securely storing my settings, but how would I <code>securely</code> get those settings there?</p>
<p>One of the big problems with the BizTalk Deployment Framework <code>SettingsFileGenerator.xml</code> is that it stores your integration environment setting values in plain text, to add to the issue, this file is usually checked into source control.</p>
<p>Now you might think that you would be able to tokenise the settings values and then replace them at a CI/CD level. Agreed this is one step further into secure storage and removal of these values in source control. However, this file will still then contain your secure setting values in plain text as part of a CI/CD artefact, and this file will exist on the BizTalk App box that the deployment has occurred on. So you are still no further secured.</p>
<h2 id="solution--secure-deployment">Solution | Secure Deployment</h2>
<p>When your BizTalk Application msi is extracted as part of a deployment (extracted onto the App Box <em>Programs x86</em>), the msi will have included some framework deployment tools. One of these tools is the <code>SSOSettingsEditor.exe</code>. This editor will allow you to specify the Affiliate Application name, of which it will then display all the currently deployed settings and values in SSO for that application. This editor will also allow you to edit such values.</p>
<p>Handily, this editor is backed by a DLL that has also been made available in the same folder <code>SSOSettingsFileReader.dll</code>. This means we can use this DLL as part of our CI/CD pipeline as a post deployment step to update values in SSO. So how do we do this?</p>
<p>Firstly, we will require our handy <code>SettingsFileGenerator.xml</code>, we still want our setting being loaded into SSO for us to update later, but don&rsquo;t require the value to be the real one. It can be a dummy value, and so we don&rsquo;t mind it being in plain text.</p>
<p>Secondly, we can use PowerShell and the <code>SSOSettingsFileReader.dll</code> as a Post Deployment Step. The DLL contains a set of static classes and methods of which we can make effective use of. For this use case, we will require <code>[SSOSettingsFileManager.SSOSettingsManager]::WriteSetting</code>. The WriteSetting method takes three parameters:</p>
<ol>
<li><strong>The Affiliate Application Name</strong> | this will be the name of your project as seen in your <code>.btdfproj</code></li>
<li><strong>The Setting Name</strong> | this is the name of the setting that you wish to replace it&rsquo;s value.</li>
<li><strong>The New Setting Value</strong> | this is the value you wish to replace the default with.</li>
</ol>
<p>After you have run this command, you can verify the changes have been made with the <code>SSOSettingsEditor.exe</code> as mentioned above.</p>
<h2 id="solution--reading-from-sso-in-your-integration">Solution | Reading from SSO in your Integration</h2>
<p>Let&rsquo;s assume that you have an integration that will require a setting value from SSO such as a database connection string. To access this setting we can make use of the same DLL as mentioned above. There is another static class and method that will read a value from SSO:</p>
<ul>
<li><code>[SSOSettingsFileManager.SSOSettingsFileReader]::ReadString</code></li>
</ul>
<p>This method takes two parameters:</p>
<ol>
<li><strong>The Affiliate Application Name</strong> | this will be the name of your project as seen in your <code>.btdfproj</code></li>
<li><strong>The Setting Name</strong> | this is the name of the setting that you wish to retrieve.</li>
</ol>
<p>To make such as call, you will need to:</p>
<ol>
<li>Add the DLL as a reference to you BizTalk Project.</li>
<li>Include the DLL as a component, this is done in your <code>.btdfproj</code></li>
<li>Add the call to your integration as either:
<ol>
<li>A variable in your orchestration.</li>
<li>A Scripting Functoid calling the external assembly in a Map.</li>
</ol>
</li>
</ol>
<h2 id="conclusion">Conclusion</h2>
<p>Provided both solutions, you can now specify your own secure integration settings values where these values are only stored as secure variables in your CI/CD and passed into SSO, and then read these values into your integrations straight from SSO. These secret values no longer need to live in plain text.</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>&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;An Azure Tenant is hierarchically structured with the following make up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tenant&lt;/li&gt;
&lt;li&gt;One or more Management Groups&lt;/li&gt;
&lt;li&gt;One or more Subscriptions&lt;/li&gt;
&lt;li&gt;One or more Resource groups&lt;/li&gt;
&lt;li&gt;One or more Resources&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;img src="https://andrewilson.co.uk/images/posts/2023/04/az-scopes.png" alt="AzureScopes"&gt;
&lt;/p&gt;
&lt;p&gt;Deployment Scopes &lt;code&gt;{Tenant, Management Group, Subscription, Resource Group}&lt;/code&gt; allow us to deploy respective types of resources at each level.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Scope&lt;/code&gt; 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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;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 (&lt;em&gt;using a storage account resource as an example&lt;/em&gt;):&lt;/p&gt;
&lt;h3 id="conditional-deployment"&gt;Conditional Deployment&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/conditional-resource-deployment"&gt;Conditional Deployment&lt;/a&gt; is used where you may or may not wish to deploy a given resource depending on the outcome of a given condition (if statement).&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;I have recently been playing around with some of the &lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions"&gt;Bicep functions&lt;/a&gt; when I came across a pattern by Microsoft called the &lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/patterns-shared-variable-file"&gt;&lt;strong&gt;Shared Variable File Pattern&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;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).&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="azure-logic-apps"&gt;Azure Logic Apps&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;If you are new to developing Azure Logic Apps, there is great Microsoft Learning material to get you started:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What are Azure Logic Apps | &lt;a href="https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-overview"&gt;https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Introduction to Azure Logic Apps | &lt;a href="https://learn.microsoft.com/en-us/training/modules/intro-to-logic-apps/"&gt;https://learn.microsoft.com/en-us/training/modules/intro-to-logic-apps/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Quickstart: Create an integration workflow | &lt;a href="https://learn.microsoft.com/en-us/azure/logic-apps/quickstart-create-first-logic-app-workflow"&gt;https://learn.microsoft.com/en-us/azure/logic-apps/quickstart-create-first-logic-app-workflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;Once you have created your workflow in the Logic App designer, your next question may be:&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>Fluent UI | React | Controlled multi-select Dropdown - Selected Keys</title><link>https://andrewilson.co.uk/post/2023/02/fluent-ui-react-controlled-multi-select-dropdown-selected-keys/</link><pubDate>Mon, 13 Feb 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/02/fluent-ui-react-controlled-multi-select-dropdown-selected-keys/</guid><description>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;I ran into a problem the other day working with the Fluent UI React component &lt;code&gt;Controlled Multi-select Dropdown&lt;/code&gt;. As context, here is my redacted code and explanation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;import&lt;/span&gt; { Dropdown, IDropdownOption } &lt;span style="color:#ff79c6"&gt;from&lt;/span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#39;office-ui-fabric-react&amp;#39;&lt;/span&gt;; &lt;span style="color:#6272a4"&gt;// office-ui-fabric-react@7.204.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;export&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;interface&lt;/span&gt; State {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itemList: &lt;span style="color:#8be9fd"&gt;IDropdownOption&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; selectedItems: &lt;span style="color:#8be9fd"&gt;string&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;export&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;default&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;class&lt;/span&gt; DropDownExample &lt;span style="color:#ff79c6"&gt;extends&lt;/span&gt; React.Component&amp;lt;&lt;span style="color:#ff79c6"&gt;any&lt;/span&gt;, &lt;span style="color:#50fa7b"&gt;State&lt;/span&gt;&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;constructor&lt;/span&gt;(context) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;super&lt;/span&gt;(context);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;this&lt;/span&gt;.state &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; itemList&lt;span style="color:#ff79c6"&gt;:&lt;/span&gt; [{ key&lt;span style="color:#ff79c6"&gt;:&lt;/span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#39;None&amp;#39;&lt;/span&gt;, text&lt;span style="color:#ff79c6"&gt;:&lt;/span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#39;None&amp;#39;&lt;/span&gt; }],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; selectedItems&lt;span style="color:#ff79c6"&gt;:&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;this&lt;/span&gt;.GetItems();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;this&lt;/span&gt;.LoadPreselection();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;private&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;async&lt;/span&gt; GetItems(){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;// ... load items into itemList
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;private&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;async&lt;/span&gt; LoadPreselection(){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;// ... load pre-selected items into selectedItems
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;public&lt;/span&gt; onItemChange &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; (event: &lt;span style="color:#8be9fd"&gt;React.FormEvent&lt;/span&gt;&amp;lt;&lt;span style="color:#ff79c6"&gt;HTMLDivElement&lt;/span&gt;&amp;gt;, item: &lt;span style="color:#8be9fd"&gt;IDropdownOption&lt;/span&gt;)&lt;span style="color:#ff79c6"&gt;:&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;void&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;if&lt;/span&gt; (item) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;let&lt;/span&gt; tempItemsArray &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; selectedKeys;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;const&lt;/span&gt; index &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; tempItemsArray.indexOf(item.key, &lt;span style="color:#bd93f9"&gt;0&lt;/span&gt;); &lt;span style="color:#6272a4"&gt;// Obtain item index if exists - returns -1 if it does not
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;if&lt;/span&gt; (index &lt;span style="color:#ff79c6"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;-&lt;/span&gt;&lt;span style="color:#bd93f9"&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tempItemsArray.splice(index, &lt;span style="color:#bd93f9"&gt;1&lt;/span&gt;); &lt;span style="color:#6272a4"&gt;// If the item exists, remove the item from the existing array through the splice function
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#ff79c6"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tempItemsArray.push(item.key); &lt;span style="color:#6272a4"&gt;// If the item does not exist, add the item to the existing array
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; setSelectedKeys(tempItemsArray); &lt;span style="color:#6272a4"&gt;// Update State
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; render() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#ff79c6"&gt;Dropdown&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#50fa7b"&gt;placeholder&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;[Select Item]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#50fa7b"&gt;disabled&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;=&lt;/span&gt;{&lt;span style="color:#ff79c6"&gt;this&lt;/span&gt;.state.itemList.length &lt;span style="color:#ff79c6"&gt;===&lt;/span&gt; &lt;span style="color:#bd93f9"&gt;0&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#50fa7b"&gt;label&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#39;Items&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#50fa7b"&gt;id&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#39;items&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#50fa7b"&gt;responsiveMode&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;=&lt;/span&gt;{&lt;span style="color:#bd93f9"&gt;1&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#50fa7b"&gt;multiSelect&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#50fa7b"&gt;options&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;=&lt;/span&gt;{&lt;span style="color:#ff79c6"&gt;this&lt;/span&gt;.state.itemList}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#50fa7b"&gt;selectedKeys&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;=&lt;/span&gt;{&lt;span style="color:#ff79c6"&gt;this&lt;/span&gt;.state.selectedItems}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#50fa7b"&gt;onChange&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;=&lt;/span&gt;{&lt;span style="color:#ff79c6"&gt;this&lt;/span&gt;.onItemChange.bind(&lt;span style="color:#ff79c6"&gt;this&lt;/span&gt;)}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I had a list of items that I wanted to load into the Multi-select control from an API call. After obtaining the list of items, I wanted to preload any previously selected items.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>I ran into a problem the other day working with the Fluent UI React component <code>Controlled Multi-select Dropdown</code>. As context, here is my redacted code and explanation:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">import</span> { Dropdown, IDropdownOption } <span style="color:#ff79c6">from</span> <span style="color:#f1fa8c">&#39;office-ui-fabric-react&#39;</span>; <span style="color:#6272a4">// office-ui-fabric-react@7.204.0
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">export</span> <span style="color:#ff79c6">interface</span> State {
</span></span><span style="display:flex;"><span>	itemList: <span style="color:#8be9fd">IDropdownOption</span>[];
</span></span><span style="display:flex;"><span>	selectedItems: <span style="color:#8be9fd">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:#ff79c6">export</span> <span style="color:#ff79c6">default</span> <span style="color:#ff79c6">class</span> DropDownExample <span style="color:#ff79c6">extends</span> React.Component&lt;<span style="color:#ff79c6">any</span>, <span style="color:#50fa7b">State</span>&gt; {
</span></span><span style="display:flex;"><span>	<span style="color:#ff79c6">constructor</span>(context) {
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">super</span>(context);
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">this</span>.state <span style="color:#ff79c6">=</span> {
</span></span><span style="display:flex;"><span>			itemList<span style="color:#ff79c6">:</span> [{ key<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#39;None&#39;</span>, text<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#39;None&#39;</span> }],
</span></span><span style="display:flex;"><span>			selectedItems<span style="color:#ff79c6">:</span> []
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">this</span>.GetItems();
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">this</span>.LoadPreselection();
</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">private</span> <span style="color:#ff79c6">async</span> GetItems(){
</span></span><span style="display:flex;"><span>		<span style="color:#6272a4">// ... load items into itemList
</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">private</span> <span style="color:#ff79c6">async</span> LoadPreselection(){
</span></span><span style="display:flex;"><span>		<span style="color:#6272a4">// ... load pre-selected items into selectedItems
</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">public</span> onItemChange <span style="color:#ff79c6">=</span> (event: <span style="color:#8be9fd">React.FormEvent</span>&lt;<span style="color:#ff79c6">HTMLDivElement</span>&gt;, item: <span style="color:#8be9fd">IDropdownOption</span>)<span style="color:#ff79c6">:</span> <span style="color:#ff79c6">void</span> <span style="color:#ff79c6">=&gt;</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">if</span> (item) {
</span></span><span style="display:flex;"><span>      		<span style="color:#8be9fd;font-style:italic">let</span> tempItemsArray <span style="color:#ff79c6">=</span> selectedKeys;
</span></span><span style="display:flex;"><span>      		<span style="color:#ff79c6">const</span> index <span style="color:#ff79c6">=</span> tempItemsArray.indexOf(item.key, <span style="color:#bd93f9">0</span>); <span style="color:#6272a4">// Obtain item index if exists - returns -1 if it does not
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      		<span style="color:#ff79c6">if</span> (index <span style="color:#ff79c6">&gt;</span> <span style="color:#ff79c6">-</span><span style="color:#bd93f9">1</span>) {
</span></span><span style="display:flex;"><span>      		   tempItemsArray.splice(index, <span style="color:#bd93f9">1</span>); <span style="color:#6272a4">// If the item exists, remove the item from the existing array through the splice function
</span></span></span><span style="display:flex;"><span>			} <span style="color:#ff79c6">else</span> {
</span></span><span style="display:flex;"><span>      		  tempItemsArray.push(item.key); <span style="color:#6272a4">// If the item does not exist, add the item to the existing array
</span></span></span><span style="display:flex;"><span>      		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      		setSelectedKeys(tempItemsArray); <span style="color:#6272a4">// Update State
</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>	render() {
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">return</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>			&lt;<span style="color:#ff79c6">Dropdown</span>
</span></span><span style="display:flex;"><span>				<span style="color:#50fa7b">placeholder</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#34;[Select Item]&#34;</span>
</span></span><span style="display:flex;"><span>				<span style="color:#50fa7b">disabled</span><span style="color:#ff79c6">=</span>{<span style="color:#ff79c6">this</span>.state.itemList.length <span style="color:#ff79c6">===</span> <span style="color:#bd93f9">0</span>}
</span></span><span style="display:flex;"><span>				<span style="color:#50fa7b">label</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;Items&#39;</span>
</span></span><span style="display:flex;"><span>				<span style="color:#50fa7b">id</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;items&#39;</span>
</span></span><span style="display:flex;"><span>				<span style="color:#50fa7b">responsiveMode</span><span style="color:#ff79c6">=</span>{<span style="color:#bd93f9">1</span>}
</span></span><span style="display:flex;"><span>				<span style="color:#50fa7b">multiSelect</span>
</span></span><span style="display:flex;"><span>				<span style="color:#50fa7b">options</span><span style="color:#ff79c6">=</span>{<span style="color:#ff79c6">this</span>.state.itemList}
</span></span><span style="display:flex;"><span>				<span style="color:#50fa7b">selectedKeys</span><span style="color:#ff79c6">=</span>{<span style="color:#ff79c6">this</span>.state.selectedItems}
</span></span><span style="display:flex;"><span>				<span style="color:#50fa7b">onChange</span><span style="color:#ff79c6">=</span>{<span style="color:#ff79c6">this</span>.onItemChange.bind(<span style="color:#ff79c6">this</span>)}
</span></span><span style="display:flex;"><span>			/&gt;
</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>I had a list of items that I wanted to load into the Multi-select control from an API call. After obtaining the list of items, I wanted to preload any previously selected items.</p>
<p>At this point there were no problems&hellip;. until I had written my <code>onchange</code> method. For some reason I was able to manage the list of selected keys absolutely fine, but there were no visible changes to the UI selection when clicking.</p>
<p>For example:</p>
<ol>
<li>Open drop down.</li>
<li>Select an option.
<ul>
<li>onChange event triggered (state managed).</li>
<li>Selection in dropdown hasn&rsquo;t changed (Item UI does not confirm selection OR un-selection)</li>
</ul>
</li>
</ol>
<p>As you can imagine, this caused some confusion. The SelectedKeys Array has the correct values but the control is not confirming what I am seeing behind the scenes.</p>
<p>This is until the penny dropped when reviewing example solutions.</p>
<h2 id="solution">Solution</h2>
<p>The Dropdown control is treating the selectedKeys as an IMutable <em><code>&quot;(Once set, it cannot be changed)&quot;</code></em>. This completely changes the onChanged method solution.</p>
<p>Rather than <code>Push</code> to the <code>existing</code> array, we need to use the <code>Spread</code> syntax. The spread syntax will add the new item along with the already existing items into a <code>new array</code> that can be used by the control. 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;-webkit-text-size-adjust:none;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>...<span style="color:#ff79c6">this</span>.state.selectedItems, item.key <span style="color:#ff79c6">as</span> <span style="color:#8be9fd">string</span>
</span></span></code></pre></div><p>Rather than using <code>Splice</code> to remove an element from an <code>existing</code> array, we need to use the <code>Filter</code> method. The filter method creates a <code>new</code> array with all the elements that pass the logic test supplied, in this case bring everything over that doesn&rsquo;t match the selected item. 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;-webkit-text-size-adjust:none;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">this</span>.state.selectedItems.filter(key <span style="color:#ff79c6">=&gt;</span> key <span style="color:#ff79c6">!==</span> item.key)
</span></span></code></pre></div><p>Combined, my <code>onChange</code> method now takes on the following form and operates as expected:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">public</span> onItemChange <span style="color:#ff79c6">=</span> (event: <span style="color:#8be9fd">React.FormEvent</span>&lt;<span style="color:#ff79c6">HTMLDivElement</span>&gt;, item: <span style="color:#8be9fd">IDropdownOption</span>)<span style="color:#ff79c6">:</span> <span style="color:#ff79c6">void</span> <span style="color:#ff79c6">=&gt;</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#ff79c6">if</span> (item) {
</span></span><span style="display:flex;"><span>			<span style="color:#ff79c6">this</span>.setState({
</span></span><span style="display:flex;"><span>				selectedItems:
</span></span><span style="display:flex;"><span>					<span style="color:#8be9fd">item.selected</span>
</span></span><span style="display:flex;"><span>						<span style="color:#ff79c6">?</span> [...<span style="color:#ff79c6">this</span>.state.selectedItems, item.key <span style="color:#ff79c6">as</span> <span style="color:#8be9fd">string</span>]
</span></span><span style="display:flex;"><span>						<span style="color:#ff79c6">:</span> <span style="color:#ff79c6">this</span>.state.selectedItems.filter(key <span style="color:#ff79c6">=&gt;</span> key <span style="color:#ff79c6">!==</span> item.key),
</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>Azure DevOps Pipeline | Git Shallow Fetch</title><link>https://andrewilson.co.uk/post/2023/01/azure-devops-pipeline-shallow-fetch/</link><pubDate>Mon, 09 Jan 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/01/azure-devops-pipeline-shallow-fetch/</guid><description>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;I have been recently working on building a new Yaml pipeline in Azure DevOps and wished to use the &lt;a href="https://marketplace.visualstudio.com/items?itemName=gittools.gittools"&gt;GitVersion Task&lt;/a&gt;, however, upon running the pipeline the task failed with the following error:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ERROR [../../.. ..:..:..:..] An unexpected error occurred:
System.NullReferenceException: Object reference not set to an instance of an object.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After some digging and a conversation with one of my colleagues, it turns out there has been a change on Azure DevOps pipelines. By default now when a pipeline is created, the &amp;lsquo;&lt;em&gt;Get Sources&lt;/em&gt;&amp;rsquo; &lt;strong&gt;Shallow fetch&lt;/strong&gt; setting is enabled and set to a depth of 1.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>I have been recently working on building a new Yaml pipeline in Azure DevOps and wished to use the <a href="https://marketplace.visualstudio.com/items?itemName=gittools.gittools">GitVersion Task</a>, however, upon running the pipeline the task failed with the following error:</p>
<pre tabindex="0"><code>ERROR [../../.. ..:..:..:..] An unexpected error occurred:
System.NullReferenceException: Object reference not set to an instance of an object.
</code></pre><p>After some digging and a conversation with one of my colleagues, it turns out there has been a change on Azure DevOps pipelines. By default now when a pipeline is created, the &lsquo;<em>Get Sources</em>&rsquo; <strong>Shallow fetch</strong> setting is enabled and set to a depth of 1.</p>
<p>The <a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/repos/pipeline-options-for-git?view=azure-devops&amp;tabs=yaml#shallow-fetch">Shallow fetch</a> setting if enabled will limit how far back in source history to download. Effectively using the following git command <code>git fetch --depth=n</code>.</p>
<p>This in turn causes the error seen above as the GitVersion task requires the whole repo branching model to calculate the SEMVER version.</p>
<h2 id="switching-off-shallow-fetch">Switching off Shallow Fetch</h2>
<p>To switch off <strong>Shallow Fetch</strong> if using a Yaml pipeline:</p>
<ol>
<li>Edit your Pipeline.</li>
<li>Selecting the ellipsis button, select Triggers.</li>
<li>Navigate to the Yaml tab, and select Get Sources.</li>
<li>There is a setting near the very bottom called <em>Shallow fetch</em>, switch the setting off and save.</li>
</ol>
]]></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>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;I recently started working on a Git Repository (&lt;em&gt;not of my own creation&lt;/em&gt;) that had a repository name containing spaces.&lt;/p&gt;
&lt;p&gt;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:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;git clone https://.../.../my%20repo myrepo
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;My best practice answer would be, &lt;strong&gt;don&amp;rsquo;t create repositories with names containing spaces&lt;/strong&gt;, but this one already did.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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 Storage Account | Deletion and Reuse</title><link>https://andrewilson.co.uk/post/2023/01/azure-storage-account-deletion/</link><pubDate>Thu, 05 Jan 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/01/azure-storage-account-deletion/</guid><description>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;When it comes to creating Azure Storage Accounts, the &lt;a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-account-overview#storage-account-name"&gt;name&lt;/a&gt; has some very important rules that need to be kept in mind. These rules will not only be important in the creation of the resource, but will be critical in deletion and reuse.&lt;/p&gt;
&lt;p&gt;The rules are as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Storage account names &lt;strong&gt;must&lt;/strong&gt; be &lt;strong&gt;between 3 and 24 characters in length&lt;/strong&gt; and may &lt;strong&gt;contain numbers and lowercase letters only&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your storage account &lt;strong&gt;name must be unique within Azure&lt;/strong&gt;. &lt;strong&gt;No two storage accounts can have the same name&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>When it comes to creating Azure Storage Accounts, the <a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-account-overview#storage-account-name">name</a> has some very important rules that need to be kept in mind. These rules will not only be important in the creation of the resource, but will be critical in deletion and reuse.</p>
<p>The rules are as follows:</p>
<ol>
<li>
<p>Storage account names <strong>must</strong> be <strong>between 3 and 24 characters in length</strong> and may <strong>contain numbers and lowercase letters only</strong>.</p>
</li>
<li>
<p>Your storage account <strong>name must be unique within Azure</strong>. <strong>No two storage accounts can have the same name</strong>.</p>
<p><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup><em>&lsquo;A storage account provides a unique namespace in Azure for your data. Every object that you store in Azure Storage has a URL address that includes your unique account name. The combination of the account name and the service endpoint forms the endpoints for your storage account.&rsquo;</em></p>
</li>
</ol>
<p>So point being, once you create your storage account with the given name, no other tenant can have another storage account with the same name.</p>
<p>So what does this look like for development?</p>
<ul>
<li>What happens when I delete a storage account in my tenant and recreate it?</li>
<li>What happens if I am doing cross tenant development, and I delete the account in one tenant and want to create it in another?</li>
</ul>
<h3 id="storage-account-deletion">Storage Account Deletion</h3>
<p>When a Storage Account is deleted, the account is <strong>soft deleted for 14 days</strong> with the possibility of recovery. See <a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-account-recover">recovery rules</a>.</p>
<p>The methods of deletion are as follows:</p>
<ul>
<li>Azure Portal</li>
<li><a href="https://learn.microsoft.com/en-us/cli/azure/storage/account?view=azure-cli-latest#az-storage-account-delete">Azure CLI</a></li>
<li><a href="https://learn.microsoft.com/en-us/powershell/module/az.storage/remove-azstorageaccount?view=azps-9.2.0">Azure PowerShell</a></li>
<li><a href="https://learn.microsoft.com/en-us/rest/api/storagerp/storage-accounts/delete?tabs=HTTP">Azure Rest API</a></li>
</ul>
<h4 id="same-tenant-development">Same Tenant Development</h4>
<p>If say you are developing an Azure solution that uses an Azure Storage Account, and you have a development release cycle that deploys the solution and then deprovisions it. OR you are simply deleting the Storage Account and then recreating it with the same name.</p>
<p><strong>This will be fine.</strong> However, you will lose any possibilities of recovery of the previously deleted Storage Account.</p>
<h4 id="cross-tenant-development">Cross Tenant Development</h4>
<p>In the instance that you are developing with a Storage Account in one tenant and then decide to deploy this account into another tenant having deleted it in the previous. <strong>This will not be possible</strong> until the 14 day recovery period has subsided.</p>
<h3 id="summary">Summary</h3>
<p>Make sure you are aware of the names you provide your Storage Accounts, this is lesser of a problem with single tennant development, however can cause issues when moving into cross tenant development.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Storage account endpoints | <a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-account-overview#storage-account-endpoints">https://learn.microsoft.com/en-us/azure/storage/common/storage-account-overview#storage-account-endpoints</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 | Product Required Subscription Behaviours</title><link>https://andrewilson.co.uk/post/2022/12/apim-product-required-subscription-behaviours/</link><pubDate>Tue, 20 Dec 2022 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2022/12/apim-product-required-subscription-behaviours/</guid><description>&lt;h2 id="background--functional-workings-of-apim-subscriptions"&gt;Background | Functional Workings of APIM Subscriptions&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Azure API Management Subscriptions operate at &lt;strong&gt;three&lt;/strong&gt; scope levels:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;All APIs&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Applies to any API that requires a Subscription.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;As this will allow access to &lt;strong&gt;ANY&lt;/strong&gt; api, use this with caution.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Specific APIs&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;These subscriptions are linked to specific APIs, and their keys can only be used by those APIs.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;An API with a Specific subscription, as mentioned above, can be called with a generic All APIs Subscription Key.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Products&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Products are independent of APIs and therefore have their own &amp;lsquo;&lt;em&gt;Subscription Required&lt;/em&gt;&amp;rsquo; setting and behaviours. &lt;em&gt;See Problem Space below.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;As mentioned above, because Products are independent of APIs, their subscriptions operate slightly differently and &lt;strong&gt;can cause some ill effects if unknown&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="background--functional-workings-of-apim-subscriptions">Background | Functional Workings of APIM Subscriptions</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>Azure API Management Subscriptions operate at <strong>three</strong> scope levels:</p>
<ol>
<li><strong>All APIs</strong>
<ul>
<li>Applies to any API that requires a Subscription.</li>
<li><em>As this will allow access to <strong>ANY</strong> api, use this with caution.</em></li>
</ul>
</li>
<li><strong>Specific APIs</strong>
<ul>
<li>These subscriptions are linked to specific APIs, and their keys can only be used by those APIs.</li>
<li><em>An API with a Specific subscription, as mentioned above, can be called with a generic All APIs Subscription Key.</em></li>
</ul>
</li>
<li><strong>Products</strong>
<ul>
<li>Products are independent of APIs and therefore have their own &lsquo;<em>Subscription Required</em>&rsquo; setting and behaviours. <em>See Problem Space below.</em></li>
</ul>
</li>
</ol>
<h2 id="problem-space">Problem Space</h2>
<p>As mentioned above, because Products are independent of APIs, their subscriptions operate slightly differently and <strong>can cause some ill effects if unknown</strong>.</p>
<p>The following behaviours can be observed:</p>
<hr>
<ol>
<li>
<blockquote>
<ul>
<li>API Subscription <strong>Required</strong> | Product Subscription <strong>NOT</strong> Required:
<ul>
<li>The API can now be accessed <strong>without</strong> a Subscription.</li>
<li>If a Subscription is provided it can only be from:
<ul>
<li>All APIs Scoped Subscription Keys</li>
<li>The Products Subscription Keys</li>
</ul>
</li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ol>
<hr>
<ol start="2">
<li>
<blockquote>
<ul>
<li>API <strong>Specific</strong> Subscription <strong>Required</strong> | Product Subscription <strong>NOT</strong> Required:
<ul>
<li>The API can now be accessed <strong>without</strong> a Subscription.</li>
<li>If a Subscription is provided it can be from:
<ul>
<li><strong>All three</strong> scoped Subscription Keys.</li>
</ul>
</li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ol>
<hr>
<ol start="3">
<li>
<blockquote>
<ul>
<li>API Subscription <strong>Required</strong> | Product Subscription <strong>Required</strong>:
<ul>
<li>The API <strong>Requires</strong> a Subscription key from either:
<ul>
<li>All APIs Scoped Subscription Keys</li>
<li>The Products Subscription Keys</li>
</ul>
</li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ol>
<hr>
<ol start="4">
<li>
<blockquote>
<ul>
<li>API <strong>Specific</strong> Subscription <strong>Required</strong> | Product Subscription <strong>Required</strong>:
<ul>
<li>The API <strong>Requires</strong> a Subscription key from <strong>any of the three</strong> Subscription scopes.</li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ol>
<hr>
<ol start="5">
<li>
<blockquote>
<ul>
<li>API Subscription <strong>NOT</strong> Required | Product Subscription <strong>Required</strong>:
<ul>
<li>The API can now be accessed <strong>without</strong> a Subscription.</li>
</ul>
</li>
</ul>
</blockquote>
</li>
</ol>
<h2 id="summary">Summary</h2>
<p>Be very careful with the way you configure your subscription requirements. The only setup with a Product Subscription that will demand your API to require a subscription key is (<strong>setup 3 and 4</strong>).</p>
<p>All other setup options (1, 2, and 5) will leave your API open to be called without a subscription key.</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>&lt;h2 id="problem-space"&gt;Problem Space&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;By default:&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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>&lt;h2 id="problem-space"&gt;Problem Space:&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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>&lt;h3 id="problem-space"&gt;Problem Space:&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;</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;-webkit-text-size-adjust:none;"><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;-webkit-text-size-adjust:none;"><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 API Management | Purge Soft-Deleted Instance</title><link>https://andrewilson.co.uk/post/2022/09/apim-purge-soft-deleted-instance/</link><pubDate>Wed, 07 Sep 2022 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2022/09/apim-purge-soft-deleted-instance/</guid><description>&lt;h3 id="problem-space"&gt;Problem Space:&lt;/h3&gt;
&lt;p&gt;Around June 2020 a change was made to Azure API Management whereby any deletion of the instance via the Azure portal, Azure PowerShell, Azure CLI, and REST API version &lt;code&gt;2020-06-01-preview&lt;/code&gt; or later will result in the instance being &lt;em&gt;&lt;strong&gt;soft-deleted&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This is to allow for recoverability of a recently deleted API Management instance, and therefore protecting against accidental deletion of the instance.&lt;/p&gt;
&lt;p&gt;The problem with this is that not all the Azure Resource Management tooling currently supports the management of soft deleted API Management Instances. Currently the only management tooling that supports this feature are the REST API and Azure CLI. You cannot at this point in time list, show, or purge a soft deleted instance in the Management Portal or Azure PowerShell.&lt;/p&gt;</description><content:encoded><![CDATA[<h3 id="problem-space">Problem Space:</h3>
<p>Around June 2020 a change was made to Azure API Management whereby any deletion of the instance via the Azure portal, Azure PowerShell, Azure CLI, and REST API version <code>2020-06-01-preview</code> or later will result in the instance being <em><strong>soft-deleted</strong></em>.</p>
<p>This is to allow for recoverability of a recently deleted API Management instance, and therefore protecting against accidental deletion of the instance.</p>
<p>The problem with this is that not all the Azure Resource Management tooling currently supports the management of soft deleted API Management Instances. Currently the only management tooling that supports this feature are the REST API and Azure CLI. You cannot at this point in time list, show, or purge a soft deleted instance in the Management Portal or Azure PowerShell.</p>
<h3 id="solutions">Solutions</h3>
<h4 id="azure-cli">Azure CLI</h4>
<p>As of the 5th of July 2022, you can use the Azure CLI to manage soft-deleted APIM instances. <a href="https://github.com/MicrosoftDocs/azure-docs-cli/blob/main/docs-ref-conceptual/release-notes-azure-cli.md#july-05-2022">Specifically CLI version 2.38.0 and above</a>.</p>
<p>Documentation on the relative management actions that can be applied are shown <a href="https://learn.microsoft.com/en-us/cli/azure/apim/deletedservice?view=azure-cli-latest">here</a>.</p>
<p>To purge a deleted instance, you can use the following commands:</p>
<pre tabindex="0"><code class="language-Azure" data-lang="Azure">az login

az account set --subscription &#34;{SubscriptionId}&#34;

// Using the list command and query argument you can obtain the name and location of your soft deleted APIM Instances

az apim deletedservice list --query &#34;[].[name, location]&#34;

// Since you now know the name and location of all the potential instances you wish to purge, you can now issue the purge command for each instance.

az apim deletedservice purge --service-name &#34;{APIMInstanceName}&#34; --location &#34;{RegionDeployedTo}&#34;
</code></pre><h4 id="rest-api">Rest API</h4>
<p>In-order to purge a soft-deleted APIM instance, you will need to execute the <em>Delete</em> REST API for API Management as per <a href="https://docs.microsoft.com/en-us/rest/api/apimanagement/current-ga/deleted-services/purge?tabs=HTTP">Microsoft Documentation</a>.</p>
<p>To make this simpler, here is some PowerShell using the <code>2021-08-01</code> REST API version and az tooling:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-PowerShell" data-lang="PowerShell"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$SubscriptionId</span> = <span style="color:#f1fa8c">&#39;{SubscriptionId}&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$Region</span> = <span style="color:#f1fa8c">&#39;{Region}&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$APIMInstanceName</span> = <span style="color:#f1fa8c">&#39;{APIMInstanceName}&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Connect-AzAccount</span> -Subscription <span style="color:#8be9fd;font-style:italic">$SubscriptionId</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$accessToken</span> = <span style="color:#8be9fd;font-style:italic">Get-AzAccessToken</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$request</span> = <span style="color:#8be9fd;font-style:italic">@</span>{
</span></span><span style="display:flex;"><span>    Method = <span style="color:#f1fa8c">&#39;DELETE&#39;</span>
</span></span><span style="display:flex;"><span>    Uri = <span style="color:#f1fa8c">&#34;https://management.azure.com/subscriptions/</span>$(<span style="color:#8be9fd;font-style:italic">$SubscriptionId</span>)<span style="color:#f1fa8c">/providers/Microsoft.ApiManagement/locations/</span>$(<span style="color:#8be9fd;font-style:italic">$Region</span>)<span style="color:#f1fa8c">/deletedservices/</span>$(<span style="color:#8be9fd;font-style:italic">$APIMInstanceName</span>)<span style="color:#f1fa8c">?api-version=2021-08-01&#34;</span>
</span></span><span style="display:flex;"><span>    Headers = <span style="color:#8be9fd;font-style:italic">@</span>{
</span></span><span style="display:flex;"><span>        Authorization = <span style="color:#f1fa8c">&#34;Bearer </span>$(<span style="color:#8be9fd;font-style:italic">$accessToken</span>.Token)<span style="color:#f1fa8c">&#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:#8be9fd;font-style:italic">Invoke-RestMethod</span> <span style="color:#8be9fd;font-style:italic">@request</span>
</span></span></code></pre></div>]]></content:encoded></item><item><title>Azure Logic App | Parallel Terminates &amp; Action State Checking</title><link>https://andrewilson.co.uk/post/2022/09/azure-logic-app-parallel-terminate/</link><pubDate>Sun, 04 Sep 2022 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2022/09/azure-logic-app-parallel-terminate/</guid><description>&lt;h2 id="problem-space"&gt;Problem Space:&lt;/h2&gt;
&lt;p&gt;I have recently encountered an interesting problem space with Azure logic app action &lt;code&gt;run after&lt;/code&gt; logic. I ran into the problem whilst creating a logic app that will perform actions in parallel. The bulk of the actions are performed in scopes. If an action fails within the first set of scopes, the scope status is &lt;code&gt;Failed&lt;/code&gt;. If the first scope fails, then the second scope full of actions are executed (&lt;em&gt;this based on run after logic&lt;/em&gt;). The problem with this is that I still need the logic app to terminate as Failed so that an alert will be fired off.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space:</h2>
<p>I have recently encountered an interesting problem space with Azure logic app action <code>run after</code> logic. I ran into the problem whilst creating a logic app that will perform actions in parallel. The bulk of the actions are performed in scopes. If an action fails within the first set of scopes, the scope status is <code>Failed</code>. If the first scope fails, then the second scope full of actions are executed (<em>this based on run after logic</em>). The problem with this is that I still need the logic app to terminate as Failed so that an alert will be fired off.</p>
<h3 id="assumed-fix-one">Assumed fix one</h3>
<p>To fix this, I assumed that I could place a terminate action after the parallel actions conclude with run after logic <em>Succeeded</em>. Thus being, if the second scope on either branch has succeeded, then the terminate failed action runs.</p>
<p>This logic can be seen in the diagram shown below:</p>
<p>
  <img src="/images/posts/2022/09/LogicAppState%28Small%29.png" alt="Initial Design">

</p>
<p>How this actually turns out is that both parallel first scopes will have to have failed and the second scopes have run for the terminate condition to run.
Thus, if using run after logic to assess if an action after a parallel stream should run will only work in an <strong>AND</strong> logic case.</p>
<blockquote>
<p>i.e</p>
<p>Both second scopes have succeeded, or both second scopes have failed etc.</p>
<p>Not if scope 2.2 succeeded or if scope 3.2 succeeded.</p>
</blockquote>
<h3 id="assumed-fix-two">Assumed fix two</h3>
<p>After validating the run after logic of the first assumed fix, I then experimented with placing a terminate failed action at the end of each parallel stream with the same run after condition. As you would expect, the major problem with this is that if the one parallel stream fails first, the other stream will be skipped mid execution. Clearly not fixing the problem. This can be seen in the diagram below:</p>
<p>
  <img src="/images/posts/2022/09/LogicAppState2%28Small%29.png" alt="Assumed Fix Two">

</p>
<h3 id="actual-fix">Actual Fix</h3>
<p>The actual fix for this is not one you would naturally come to. Based on <a href="https://docs.microsoft.com/en-us/azure/logic-apps/logic-apps-control-flow-run-steps-group-scopes">Microsoft Documentation</a> we need to evaluate the scopes result status within a conditional action, then conduct the actions based on that result. Within the condition action, we can use either and/or logic as required. The expression to retrieve the scopes run status is as follows:</p>
<p><code>result('Scope')[0]['status']</code></p>
<p>This solution allows us to retrieve individual scope run status and evaluate at the end of the parallel stream. Based on either 2.1 or 3.1 scopes failing, we can then run our failed termination, else allow logic app to succeed.</p>
<p>Bear in mind that for this to work, the conditional after the parallel stream needs to have all run after types set so that the conditional runs regardless. This can be seen in the diagram below:</p>
<p>
  <img src="/images/posts/2022/09/LogicAppState3%28Small%29.png" alt="Actual Fix">

</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>&lt;h2 id="problem-space"&gt;Problem Space:&lt;/h2&gt;
&lt;p&gt;I recently came into some issues with assigning Azure roles through a Bicep template and pipeline deployment. I was looking to assign &amp;lsquo;&lt;em&gt;Storage Blob Data Reader&lt;/em&gt;&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:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What are &lt;a href="#role-assignment-conditions"&gt;Role Assignment Conditions&lt;/a&gt; and how can I use them in my template?&lt;/li&gt;
&lt;li&gt;I am trying to assign a built in role, what is the &lt;a href="#identifying-the-role-definition-id"&gt;roleDefinitionId&lt;/a&gt; that I should be using?&lt;/li&gt;
&lt;li&gt;I am trying to assign the role to a &lt;a href="#referencing-a-service-principal-id"&gt;service principal user&lt;/a&gt;, what id should I be referencing in the template?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For reference, the Bicep template for role assignment&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; that I am using is shown below:&lt;/p&gt;</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>