<?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>Azure on Andrew Wilson's Blog</title><link>https://andrewilson.co.uk/tags/azure/</link><description>Recent content in Azure on Andrew Wilson's Blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Tue, 03 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://andrewilson.co.uk/tags/azure/index.xml" rel="self" type="application/rss+xml"/><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>Introduction 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.
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.</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;"><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;"><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;"><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;"><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>The Problem 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 OkObjectResult started returning an empty JSON object {} instead of the expected data.
[Function(&amp;#34;MyFunction&amp;#34;)] public IActionResult Run([HttpTrigger(AuthorizationLevel.Function, &amp;#34;post&amp;#34;)] HttpRequest req) { var data = new MyResponse { Name = &amp;#34;Test&amp;#34;, Value = 123 }; return new OkObjectResult(data); // Returns {} instead of expected JSON } Why This Happens There are two key changes that caused this issue:</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;"><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;"><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;"><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;"><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>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>Background 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 web.config file is the nerve centre, handling everything from parameters to connection strings.
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:</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;"><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;"><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;"><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;"><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>Overview 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.
Azure App Service Slots unlock powerful advantages:
Site Review. Preview and test your site before it goes live, ensuring everything works as expected.</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;"><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;"><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;"><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;"><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>Microsoft Announcement Microsoft has just introduced something quite revolutionary into the world of integration: Agent Loop, 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!
Let’s break it down.
What Is Agent Loop? Agent Loop is Microsoft’s way of embedding advanced AI decision-making into Logic Apps workflows.</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>Bicep | Existing Resource Dependencies</title><link>https://andrewilson.co.uk/post/2025/03/bicep-existing-resource-dependencies/</link><pubDate>Wed, 26 Mar 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/03/bicep-existing-resource-dependencies/</guid><description>Background The Bicep existing keyword is a powerful capability that allows us to reference a resource that wasn&amp;rsquo;t deployed as part of the current Bicep file.
One of the typical use cases that I often see is where a resource is deployed as part of a module called by the parent template, the resource that was deployed as part of the module is then required later in the parent template and therefore an existing resource definition is used.</description><content:encoded><![CDATA[<h2 id="background">Background</h2>
<p>The Bicep <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/existing-resource">existing</a> keyword is a powerful capability that allows us to reference a resource that wasn&rsquo;t deployed as part of the current Bicep file.</p>
<p>One of the typical use cases that I often see is where a resource is deployed as part of a module called by the parent template, the resource that was deployed as part of the module is then required later in the parent template and therefore an existing resource definition is used. As part of the template configuration there would be an explicit dependency between the existing resource definition and the module reference.</p>
<p>An example diagram of this is shown below:</p>
<p>
  <img src="/images/posts/2025/03/BicepExistingDependencyDiagramBackground.png" alt="Background">

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

</li>
<li>Copy the workflow definition from the Portal into your Logic App ARM Template:</li>
</ol>
<p><code>Make sure to parametrise fields within your workflow using the ARM Parameter syntax</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Json" data-lang="Json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;parameters&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;logicAppName&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;String&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;location&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;String&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&#34;interval&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;int&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;variables&#34;</span>: {},
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&#34;resources&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Microsoft.Logic/workflows&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;apiVersion&#34;</span>: <span style="color:#f1fa8c">&#34;2019-05-01&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;name&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;logicAppName&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;location&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;location&#39;)]&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">&#34;properties&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;definition&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;$schema&#34;</span>: <span style="color:#f1fa8c">&#34;https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#&#34;</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;actions&#34;</span>: {
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;Condition_-_Morning_or_Afternoon&#34;</span>: {
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;actions&#34;</span>: {
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;Terminate_-_Afternoon&#34;</span>: {
</span></span><span style="display:flex;"><span>                                    <span style="color:#ff79c6">&#34;inputs&#34;</span>: {
</span></span><span style="display:flex;"><span>                                        <span style="color:#ff79c6">&#34;runStatus&#34;</span>: <span style="color:#f1fa8c">&#34;Succeeded&#34;</span>
</span></span><span style="display:flex;"><span>                                    },
</span></span><span style="display:flex;"><span>                                    <span style="color:#ff79c6">&#34;runAfter&#34;</span>: {},
</span></span><span style="display:flex;"><span>                                    <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Terminate&#34;</span>
</span></span><span style="display:flex;"><span>                                }
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;else&#34;</span>: {
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;actions&#34;</span>: {
</span></span><span style="display:flex;"><span>                                    <span style="color:#ff79c6">&#34;Terminate_-_Morning&#34;</span>: {
</span></span><span style="display:flex;"><span>                                        <span style="color:#ff79c6">&#34;inputs&#34;</span>: {
</span></span><span style="display:flex;"><span>                                            <span style="color:#ff79c6">&#34;runStatus&#34;</span>: <span style="color:#f1fa8c">&#34;Succeeded&#34;</span>
</span></span><span style="display:flex;"><span>                                        },
</span></span><span style="display:flex;"><span>                                        <span style="color:#ff79c6">&#34;runAfter&#34;</span>: {},
</span></span><span style="display:flex;"><span>                                        <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Terminate&#34;</span>
</span></span><span style="display:flex;"><span>                                    }
</span></span><span style="display:flex;"><span>                                }
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;expression&#34;</span>: {
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;and&#34;</span>: [
</span></span><span style="display:flex;"><span>                                    {
</span></span><span style="display:flex;"><span>                                        <span style="color:#ff79c6">&#34;greaterOrEquals&#34;</span>: [
</span></span><span style="display:flex;"><span>                                            <span style="color:#f1fa8c">&#34;@utcNow(&#39;H:mm:ss&#39;)&#34;</span>,
</span></span><span style="display:flex;"><span>                                            <span style="color:#f1fa8c">&#34;12:00:00&#34;</span>
</span></span><span style="display:flex;"><span>                                        ]
</span></span><span style="display:flex;"><span>                                    }
</span></span><span style="display:flex;"><span>                                ]
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;runAfter&#34;</span>: {},
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;If&#34;</span>
</span></span><span style="display:flex;"><span>                        }
</span></span><span style="display:flex;"><span>                    },
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;contentVersion&#34;</span>: <span style="color:#f1fa8c">&#34;1.0.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;outputs&#34;</span>: {},
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;parameters&#34;</span>: {},
</span></span><span style="display:flex;"><span>                    <span style="color:#ff79c6">&#34;triggers&#34;</span>: {
</span></span><span style="display:flex;"><span>                        <span style="color:#ff79c6">&#34;Recurrence_-_Start&#34;</span>: {
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;evaluatedRecurrence&#34;</span>: {
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;frequency&#34;</span>: <span style="color:#f1fa8c">&#34;Day&#34;</span>,
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;interval&#34;</span>: <span style="color:#bd93f9">1</span>,
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;schedule&#34;</span>: {
</span></span><span style="display:flex;"><span>                                    <span style="color:#ff79c6">&#34;hours&#34;</span>: [
</span></span><span style="display:flex;"><span>                                        <span style="color:#f1fa8c">&#34;11&#34;</span>
</span></span><span style="display:flex;"><span>                                    ]
</span></span><span style="display:flex;"><span>                                }
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;recurrence&#34;</span>: {
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;frequency&#34;</span>: <span style="color:#f1fa8c">&#34;Hour&#34;</span>,
</span></span><span style="display:flex;"><span>                                <span style="color:#ff79c6">&#34;interval&#34;</span>: <span style="color:#f1fa8c">&#34;[parameters(&#39;interval&#39;)]&#34;</span>
</span></span><span style="display:flex;"><span>                            },
</span></span><span style="display:flex;"><span>                            <span style="color:#ff79c6">&#34;type&#34;</span>: <span style="color:#f1fa8c">&#34;Recurrence&#34;</span>
</span></span><span style="display:flex;"><span>                        }
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                },
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">&#34;parameters&#34;</span>: {}
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><ol start="5">
<li>Any changes to the workflow in the Azure Portal Designer are to be reflected in your ARM Template that is in Source Control. You can use source control change tooling to see the diff between current and latest, this can help you retain parameterized values that you have previously included.</li>
</ol>
<h3 id="bicep-template-deployment">Bicep Template Deployment</h3>
<p>Generating Logic App ARM Templates from Bicep is possible but has a different approach. Bicep is not written in Json as ARM is, and therefore you cannot simply copy the Json workflow definition into the Logic App deployment resource.</p>
<p>Following the example given by Microsoft<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, their method is to write out the definition using the Bicep syntax and replace property values with Bicep properties as you would with ARM. This approach however does not adapt well to change, nor is it an easy task to bring the workflow definition across from the portal. Having to convert the workflow to Bicep syntax every time is simply a pain.</p>
<p>That said, hope is not lost.</p>
<p>Bicep has some built in functions that will allow us to retain a similar development process, such that:</p>
<p>We will still run through <code>steps 1, 2, and 3</code> as we have done for ARM, but this time with Bicep.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#ff79c6">/************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Logic</span> <span style="color:#8be9fd;font-style:italic">App</span> <span style="color:#8be9fd;font-style:italic">Template</span> <span style="color:#8be9fd;font-style:italic">Bicep</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">**************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Logic App Name&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">logicAppName</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The location that the resource will be deployed to&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">logicAppDeployment</span> <span style="color:#f1fa8c">&#39;Microsoft.Logic/workflows@2022-10-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">logicAppName</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">definition</span>: {}
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">parameters</span>: {}
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">state</span>: <span style="color:#f1fa8c">&#39;Enabled&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">output</span> <span style="color:#8be9fd;font-style:italic">LogicAppName</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#8be9fd;font-style:italic">logicAppName</span>
</span></span></code></pre></div><ol start="4">
<li>
<p>Rather than copying the workflow definition into the Bicep template, copy the definition into a <code>new Json file</code> in your source control repository.</p>
</li>
<li>
<p>As you do not have access to your Bicep Parameters, any values that need parametrising will need to be replaced with tokens that will be replaced by the Bicep Template. For instance, **interval**.</p>
</li>
<li>
<p>Next we will load our workflow definition into the template, conduct token replacement with Parameter values, and then assign the definition and parameters to the Logic App Resource.</p>
<p>The Bicep Functions that we will be using to conduct these activities are:</p>
<ul>
<li><strong>loadTextContent</strong> | Loads the content of the specified file as a string. | <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-files#loadtextcontent">https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-files#loadtextcontent</a></li>
<li><strong>replace</strong> | Returns a new string with all instances of one string replaced by another string. | <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-string#replace">https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-string#replace</a></li>
<li><strong>json</strong> | Converts a valid JSON string into a JSON data type. | <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-object#json">https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-object#json</a></li>
</ul>
<p>See the Example Below:</p>
</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#ff79c6">/************************</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Logic</span> <span style="color:#8be9fd;font-style:italic">App</span> <span style="color:#8be9fd;font-style:italic">Template</span> <span style="color:#8be9fd;font-style:italic">Bicep</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">**************************/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">targetScope</span> = <span style="color:#f1fa8c">&#39;resourceGroup&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Parameters **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ****************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Logic App Name&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">logicAppName</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;The location that the resource will be deployed to&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">location</span> <span style="color:#8be9fd;font-style:italic">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Trigger Interval&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">param</span> <span style="color:#8be9fd;font-style:italic">interval</span> <span style="color:#8be9fd;font-style:italic">int</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Variables **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">logicAppDefinition</span> = <span style="color:#50fa7b">loadTextContent</span>(<span style="color:#f1fa8c">&#39;./Definition.json&#39;</span>) <span style="color:#6272a4">// Load our definition into a string variable</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">logicAppReplacementParameter</span> = <span style="color:#50fa7b">replace</span>(<span style="color:#8be9fd;font-style:italic">logicAppDefinition</span>, <span style="color:#f1fa8c">&#39;**interval**&#39;</span>, <span style="color:#8be9fd;font-style:italic">interval</span>) <span style="color:#6272a4">// Replace tokens with our parameters</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">logicAppDefinitionJson</span> = <span style="color:#50fa7b">json</span>(<span style="color:#8be9fd;font-style:italic">logicAppReplacementParameter</span>) <span style="color:#6272a4">// Retrieve the Json object from the Json String so we can access specific data when assigning</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Resources **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ***************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">logicAppDeployment</span> <span style="color:#f1fa8c">&#39;Microsoft.Logic/workflows@2022-10-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">logicAppName</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">location</span>: <span style="color:#8be9fd;font-style:italic">location</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">definition</span>: <span style="color:#8be9fd;font-style:italic">logicAppDefinitionJson</span>.<span style="color:#8be9fd;font-style:italic">definition</span> <span style="color:#6272a4">// Set the definition</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">parameters</span>: <span style="color:#8be9fd;font-style:italic">logicAppDefinitionJson</span>.<span style="color:#8be9fd;font-style:italic">parameters</span> <span style="color:#6272a4">// Set any Properties that may be set</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// ** Outputs **</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// *************</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">output</span> <span style="color:#8be9fd;font-style:italic">LogicAppName</span> <span style="color:#8be9fd;font-style:italic">string</span> = <span style="color:#8be9fd;font-style:italic">logicAppName</span>
</span></span></code></pre></div><ol start="7">
<li>As with the ARM templates, any changes to the workflow in the Azure Portal Designer are to be reflected in your Json Workflow file that is in Source Control. You can use source control change tooling to see the diff between current and latest, this can help you retain tokenised values that you have previously included.</li>
</ol>
<h2 id="take-note-when">Take Note When</h2>
<ul>
<li>Special point of care should be taken if your logic app is using xpath expressions, proper concatenation/interpolation of the quotes can be a nightmare.</li>
<li>Bicep loadTextContent has size limits: The maximum allowed size of the file is 96 Kb.
<ul>
<li>If you are meeting these size limits for your workflows, this may be an indication that you need to split your workflow up.</li>
</ul>
</li>
</ul>
<h2 id="summary">Summary</h2>
<p>Whether you choose to build your resource deployment templates with ARM or Bicep, both options as shown will provide you a development process where you can keep your low code consumption Logic App not only in source control, but have a reliable deployment template that will allow you to create your Logic App when and where ever you wish.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>What is Bicep | <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep">https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Automate deployment for Azure Logic Apps by using <strong>ARM Templates</strong> | <a href="https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-azure-resource-manager-templates-overview">https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-azure-resource-manager-templates-overview</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Automate deployment for Azure Logic Apps by using <strong>Bicep Templates</strong> | <a href="https://learn.microsoft.com/en-us/azure/logic-apps/quickstart-create-deploy-bicep?tabs=CLI">https://learn.microsoft.com/en-us/azure/logic-apps/quickstart-create-deploy-bicep?tabs=CLI</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>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>Problem Space When it comes to creating Azure Storage Accounts, the name 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.
The rules are as follows:
Storage account names must be between 3 and 24 characters in length and may contain numbers and lowercase letters only.</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>Background | Functional Workings of APIM Subscriptions 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.
Azure API Management Subscriptions operate at three scope levels:
All APIs Applies to any API that requires a Subscription. As this will allow access to ANY api, use this with caution.</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>Problem Space Subscriptions are a nice and easy method of securing your APIs in APIM, however as I bumped into a small detail around their use the other day, I thought it wise to note it down.
When a Subscription Key is required on an API, as an invoker I will need to provide either a Header or a Parameter to my request which will contain the Subscription Key.
By default:</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>Subscriptions are a nice and easy method of securing your APIs in APIM, however as I bumped into a small detail around their use the other day, I thought it wise to note it down.</p>
<p>When a Subscription Key is required on an API, as an invoker I will need to provide either a Header or a Parameter to my request which will contain the Subscription Key.</p>
<p>By default:</p>
<ul>
<li>The <strong>Header</strong> is as follows:
<ul>
<li><strong>Ocp-Apim-Subscription-Key</strong>: SubscriptionKey</li>
</ul>
</li>
<li>The Query Parameter is as follows:
<ul>
<li>?<strong>subscription-key</strong>=SubscriptionKey</li>
</ul>
</li>
</ul>
<p>This being said, Azure does also allow you to override these Header and Query Parameter names.</p>
<h3 id="azure-portal">Azure Portal</h3>
<p>To view or change these Subscription Contract Names in the <a href="https://portal.azure.com/">Azure Portal</a>, <em>navigate through to your APIM instance - APIs - Select the API you wish to view/change - Select Settings</em>.</p>
<p>As can be seen by the image below, the header and query parameter name is shown and editable under the Subscription Header in the Portal.</p>
<p>
  <img src="/images/posts/2022/12/SubscriptionContractNamesPortal.png" alt="SubscriptionContractNamesPortal">

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