<?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 APIM on Journey through Cloud &amp; Code</title><link>https://gurupasupathy.com/tags/azure-apim/</link><description>Recent content in Azure APIM on Journey through Cloud &amp; Code</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 05 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://gurupasupathy.com/tags/azure-apim/index.xml" rel="self" type="application/rss+xml"/><item><title>API Specification and Policy Updates in Azure APIM Are Zero Downtime</title><link>https://gurupasupathy.com/post/2026-06-05_apim_policy_update_0downtime/</link><pubDate>Fri, 05 Jun 2026 00:00:00 +0000</pubDate><guid>https://gurupasupathy.com/post/2026-06-05_apim_policy_update_0downtime/</guid><description>&lt;p&gt;Does APIM support zero downtime deployment? — To answer this question, multiple factors need to be ascertained, like, What is the SKU? Have you opted for Availability zones? etc. In fact, the question needs to be qualified further. What do you mean by zero downtime deployment?&lt;/p&gt;
&lt;p&gt;In the case of APIM, there are infrastructure changes and then there are gateway configuration changes like API specifications and policies. So, the answer depends on — SKU, AZ, “what” kind of changes&lt;/p&gt;</description><content:encoded><![CDATA[<p>Does APIM support zero downtime deployment? — To answer this question, multiple factors need to be ascertained, like, What is the SKU? Have you opted for Availability zones? etc. In fact, the question needs to be qualified further. What do you mean by zero downtime deployment?</p>
<p>In the case of APIM, there are infrastructure changes and then there are gateway configuration changes like API specifications and policies. So, the answer depends on — SKU, AZ, “what” kind of changes</p>
<p>From the official documentation:</p>
<p>“When you change availability zone configuration, the changes can take 15 to 45 minutes or more to apply. The API Management gateway can continue to handle API requests during this time.”</p>
<p>Gateway configuration, such as APIs and policy definitions, regularly synchronizes between the availability zones that you select for the instance. Propagation of updates between the availability zones normally takes less than 10 seconds.</p>
<p>Active requests: When an availability zone is unavailable, any requests in progress that are connected to an API Management unit in the faulty availability zone are terminated and need to be retried.</p>
<p>Automatic: You can expect instances that use automatic availability zone support to have no downtime during an availability zone outage. Units in the unaffected zone or zones continue to work.</p>
<p>“You can also expect instances that use automatic availability zone support, but have a single unit, to have no downtime.” In this case, API Management distributes the unit’s underlying compute resources to two zones. The resource in the unaffected zone continues to work.</p>
<p>Zone-redundant: You can expect zone-redundant instances to have no downtime during an availability zone outage.</p>
<p>My personal view based on this is —API specifications and Policy updates won’t cause any non-recoverable failures to the consumers; provided retry strategy is in place.</p>
<p>Is it zero downtime? Zero downtime need not mean every request succeeds on the first attempt. If the system remains available and failures are recoverable, it meets the zero-downtime requirement. So — Yes.</p>
<p>Confirmation from Microsoft Question and Answer Forum
To validate my understanding, I reached out to the MS Q&amp;A forum and got a response consistent to the above understanding.</p>
<p>Here is the link to the question in the forum that has the official response.</p>
<p>Bottom line — API specification and Policy updates are zero downtime.</p>
]]></content:encoded></item><item><title>Managing Azure APIM Operation Policies in Terraform by Importing OpenAPI Specification</title><link>https://gurupasupathy.com/post/2026-02-20_managing-apim-op-policies-in-terraform-by-importing-openapi-spec/</link><pubDate>Fri, 20 Feb 2026 00:00:00 +0000</pubDate><guid>https://gurupasupathy.com/post/2026-02-20_managing-apim-op-policies-in-terraform-by-importing-openapi-spec/</guid><description>&lt;p&gt;&lt;img loading="lazy" src="https://gurupasupathy.com/img/1__mk3hcBMP7jVKBsxWOa5JDA.png"&gt;&lt;/p&gt;
&lt;p&gt;When using Terraform to import an OpenAPI/Swagger definition into Azure API Management (APIM), the API and its operations are created successfully. However, one subtle behavior can cause confusion when trying to manage operation-level policies declaratively.&lt;/p&gt;
&lt;p&gt;This post explains the issue and a simple workaround.&lt;/p&gt;
&lt;h3 id="the-scenario"&gt;The Scenario&lt;/h3&gt;
&lt;p&gt;I was importing my API using Terraform:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Swagger/OpenAPI definition imported into APIM&lt;br&gt;
API created successfully&lt;br&gt;
All operations appeared correctly in Azure&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Later, I wanted to attach operation-level policies using Terraform using &lt;em&gt;azurerm_api_management_api_operation_policy&lt;/em&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p><img loading="lazy" src="/img/1__mk3hcBMP7jVKBsxWOa5JDA.png"></p>
<p>When using Terraform to import an OpenAPI/Swagger definition into Azure API Management (APIM), the API and its operations are created successfully. However, one subtle behavior can cause confusion when trying to manage operation-level policies declaratively.</p>
<p>This post explains the issue and a simple workaround.</p>
<h3 id="the-scenario">The Scenario</h3>
<p>I was importing my API using Terraform:</p>
<blockquote>
<p>Swagger/OpenAPI definition imported into APIM<br>
API created successfully<br>
All operations appeared correctly in Azure</p>
</blockquote>
<p>Later, I wanted to attach operation-level policies using Terraform using <em>azurerm_api_management_api_operation_policy</em></p>
<p>At this point I ran into a problem: <strong>Terraform had no record of the operations in its state file.</strong></p>
<h3 id="why-thishappens">Why This Happens</h3>
<p>This behavior is expected once you understand how Terraform works. Terraform only tracks resources explicitly declared in configuration, or<br>
resources manually imported into state</p>
<p>When Swagger is imported via <em>azurerm_api_management_api</em> the operations are created inside Azure, but they are not separate Terraform-managed resources unless you explicitly declare using <em>azurerm_api_management_api_operation</em></p>
<p>Effectively — API is created in Azure and tracked in Terraform while<br>
API Operations (via Swagger import) are created in Azure but NOT tracked in Terraform</p>
<p>This makes it unclear how to attach policies to those operations without creating the operations explicitly — a nightmare if you have hundreds of operations</p>
<h3 id="the-simple-workaround">The Simple Workaround</h3>
<p>You do not need a Terraform resource reference to the operation for you to create an operation policy and attach it. Instead, you can attach the policy directly using <em>azurerm_api_management_api_operation_policy</em> resource and referencing the Swagger operationId.</p>
<p>Example:</p>
<p>resource &ldquo;azurerm_api_management_api_operation_policy&rdquo; &ldquo;my_op_policy&rdquo; {<br>
provider = &laquo;provider&raquo;<br>
api_name = &ldquo;<your api name>&rdquo;<br>
api_management_name = data.azurerm_api_management.apim.name<br>
resource_group_name = data.azurerm_api_management.apim.resource_group_name<br>
operation_id = &ldquo;<operationId from swagger>&rdquo;<br>
xml_content = templatefile(&quot;<policy path>&quot;, {<br>
backend_name = &ldquo;<backend name>&rdquo;<br>
method = &ldquo;<operation method>&rdquo;<br>
})<br>
}</p>
<p>As long as the API exists in APIM and the operation exists and operation_id exactly matches the Swagger operationId — Terraform can apply and update the policy successfully. No explicit Terraform operation resource is required.</p>
<h3 id="notes">Notes</h3>
<p>1. Use the Swagger operationId, not the display name. Terraform identifies the operation strictly by operationId.</p>
<p>2. Treat operationId as a stable contract. If you later rename the operationId or remove an endpoint or restructure the Swagger Terraform may fail because the referenced operation no longer exists.</p>
<p>3. Importing operations individually is possible but rarely worth it. You can define <em>azurerm_api_management_api_operation</em> and import each operation manually into Terraform state. However, it requires one resource per operation. Also, manual imports are tedious and scales poorly for large APIs thus defeating the benefit of Swagger-driven API definition</p>
<p>For most setups, referencing operationId directly in the policy resource is simpler.</p>
<h3 id="takeaway">Takeaway</h3>
<p>When importing Swagger into APIM using Terraform:</p>
<blockquote>
<p>Operations are created in Azure<br>
Terraform does not automatically track them<br>
Operation policies can still be managed declaratively by simply referencing the Swagger/OpenAPI Spec operationId</p>
</blockquote>
<p>Understanding this distinction can save significant time when automating API Management deployments.</p>
]]></content:encoded></item><item><title>Extracting Swagger definition for Azure Logic App and importing to Azure APIM</title><link>https://gurupasupathy.com/post/2024-06-10_extracting-logic-app-swagger-def-and-import-to-apim/</link><pubDate>Mon, 10 Jun 2024 00:00:00 +0000</pubDate><guid>https://gurupasupathy.com/post/2024-06-10_extracting-logic-app-swagger-def-and-import-to-apim/</guid><description>&lt;p&gt;&lt;img loading="lazy" src="https://gurupasupathy.com/img/1__Yqhxr__0j4lVw8U9QtA6mKQ.jpeg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use case&lt;/strong&gt; — I want to import a Logic App as an API within my APIM instance.&lt;/p&gt;
&lt;p&gt;There is no direct way to get the swagger file of a logic app using CLI (at least, I could not figure out). So, detailing the steps to extract the swagger definition of a logic app. I use the generated swagger file to import a Logic App as an API within APIM using Azure CLI&lt;/p&gt;</description><content:encoded><![CDATA[<p><img loading="lazy" src="/img/1__Yqhxr__0j4lVw8U9QtA6mKQ.jpeg"></p>
<p><strong>Use case</strong> — I want to import a Logic App as an API within my APIM instance.</p>
<p>There is no direct way to get the swagger file of a logic app using CLI (at least, I could not figure out). So, detailing the steps to extract the swagger definition of a logic app. I use the generated swagger file to import a Logic App as an API within APIM using Azure CLI</p>
<ol>
<li><strong>Provide the service principal contributor role to the logic app</strong></li>
</ol>
<ul>
<li><em>Get the resource id of the logic app —</em></li>
</ul>
<p>$logicAppResourceId = (az logic workflow show &ndash;resource-group &ldquo;{resourcegroup-name}&rdquo; &ndash;name &ldquo;{logicAppName}&rdquo; &ndash;query id &ndash;output tsv)</p>
<ul>
<li><em>Provide contributor role for the service principal —</em></li>
</ul>
<p>az role assignment create --assignee {sp-id} - role Contributor &ndash;scope $logicAppResourceId</p>
<p><strong>2. Get the swagger file from the Logic App</strong></p>
<ul>
<li><em>generate a JW token</em> from <a href="https://login.microsoftonline.com/%7BtenantId%7D/oauth2/token"><em>https://login.microsoftonline.com/{tenantId}/oauth2/token</em></a> for the service principle with resource as “<em><a href="https://management.core.windows.net/">https://management.core.windows.net/</a></em>”</li>
</ul>
<p>$tenantId = &ldquo;11111111-1111-1111-1111-111111111111&rdquo;<br>
$clientId = &ldquo;00000000-0000-0000-0000-000000000000&rdquo;<br>
$clientSecret = &ldquo;your-client-secret&rdquo;<br>
$resource = &ldquo;<a href="https://management.core.azure.com/%22">https://management.core.azure.com/&quot;</a></p>
<p>$body = @{<br>
grant_type    = &ldquo;client_credentials&rdquo;<br>
client_id     = $clientId<br>
client_secret = $clientSecret<br>
resource      = $resource<br>
}</p>
<p>$response = Invoke-RestMethod -Method Post -Uri &ldquo;<a href="https://login.microsoftonline.com/$tenantId/oauth2/token%22">https://login.microsoftonline.com/$tenantId/oauth2/token&quot;</a> -ContentType &ldquo;application/x-www-form-urlencoded&rdquo; -Body $body</p>
<p>$accessToken = $response.access_token<br>
$accessToken</p>
<ul>
<li><em>construct the swagger URL for the logic app —</em></li>
</ul>
<p>$swaggerUrl = &ldquo;<a href="https://management.azure.com">https://management.azure.com</a>&rdquo; + (az logic workflow show &ndash;resource-group &ldquo;{resourcegroup-name}&rdquo; &ndash;name &ldquo;{logicapp-name}&rdquo; &ndash;query id &ndash;output tsv) + &ldquo;/listSwagger?api-version=2016–06–01&rdquo;</p>
<ul>
<li><em>Issue a POST request to $swaggerUrl to get the swagger definition of the LogicApp using Postman (or any other option you prefer)</em></li>
</ul>
<p><strong>3. Import into APIM</strong></p>
<ul>
<li>Run the below command to import the above swagger file to APIM</li>
</ul>
<p>az apim api import &ndash;resource-group &ldquo;{resourcegroup-name}&rdquo; &ndash;service-name &ldquo;{apim-instance-name}&rdquo; &ndash;path &ldquo;/v1&rdquo; &ndash;api-id myapi &ndash;specification-path &ldquo;.\logicapp.backend.swagger.json&rdquo; &ndash;specification-format Swagger</p>
<p><strong>4. Remove the contributor role for the service principal</strong></p>
<p>az role assignment delete &ndash;assignee 00000000–0000–0000–0000–000000000000 &ndash;role &ldquo;Contributor&rdquo; &ndash;scope $logicAppResourceId</p>
]]></content:encoded></item><item><title>Calling a Logic App from APIM</title><link>https://gurupasupathy.com/post/2024-05-22_calling-a-logic-app-from-apim/</link><pubDate>Wed, 22 May 2024 00:00:00 +0000</pubDate><guid>https://gurupasupathy.com/post/2024-05-22_calling-a-logic-app-from-apim/</guid><description>&lt;p&gt;&lt;img loading="lazy" src="https://gurupasupathy.com/img/1__Gepa1jETj8F7cwF8xpVXJg.jpeg"&gt;&lt;/p&gt;
&lt;p&gt;There are couple of ways to integrate an APIM with Logic App. The most common use case as far as I know is exposing the Logic App as an API on the APIM. The other scenario is calling a Logic App from APIM.&lt;/p&gt;
&lt;p&gt;I will provide the APIM policy snippet to call a Logic App. If you are using Managed Identity to authenticate to Logic App (will cover in a separate article), you can skip sending the bearer token.&lt;/p&gt;</description><content:encoded><![CDATA[<p><img loading="lazy" src="/img/1__Gepa1jETj8F7cwF8xpVXJg.jpeg"></p>
<p>There are couple of ways to integrate an APIM with Logic App. The most common use case as far as I know is exposing the Logic App as an API on the APIM. The other scenario is calling a Logic App from APIM.</p>
<p>I will provide the APIM policy snippet to call a Logic App. If you are using Managed Identity to authenticate to Logic App (will cover in a separate article), you can skip sending the bearer token.</p>
<p><strong>Few steps to be done in the Logic App</strong></p>
<ol>
<li>
<p>enable Authentication at the Logic App end</p>
</li>
<li>
<p>the Logic App URL should not contain the SAS token</p>
</li>
<li>
<p>make sure that the Logic App has the below in trigger section. Basically, this is the ensure that the Logic App expects the Bearer token and “IncludeAuthorizationHeadersInOutputs” ensures that the Auth token is available for further processing within the Logic App</p>
<p>&ldquo;triggers&rdquo;: {<br>
&ldquo;manual&rdquo;: {<br>
&ldquo;conditions&rdquo;: [<br>
{<br>
&ldquo;expression&rdquo;: &ldquo;@startsWith(triggerOutputs()?[&lsquo;headers&rsquo;]?[&lsquo;Authorization&rsquo;], &lsquo;Bearer&rsquo;)&rdquo;<br>
}<br>
],<br>
&ldquo;inputs&rdquo;: {<br>
&ldquo;schema&rdquo;: {}<br>
},<br>
&ldquo;kind&rdquo;: &ldquo;Http&rdquo;,<br>
&ldquo;operationOptions&rdquo;: &ldquo;IncludeAuthorizationHeadersInOutputs&rdquo;,<br>
&ldquo;type&rdquo;: &ldquo;Request&rdquo;<br>
}<br>
}</p>
</li>
</ol>
<p><strong>APIM Policy to call the Logic App</strong></p>
<p>We issue a call to the Logic App from with the <send-request>. The response from the Logic App is captured in <em>response-variable-name=”responsela”.</em></p>
<p>&lt;policies&gt;<br>
&lt;inbound&gt;<br>
<base /></p>
<pre><code>    &lt;send-request mode\=&quot;new&quot; response-variable-name\=&quot;responsela&quot; timeout\=&quot;20&quot; ignore-error\=&quot;false&quot;\&gt;  
        &lt;set-url\&gt;https://xxxxxxx.com:443/workflows/xxxxxxxxxxxx/triggers/manual/paths/invoke?api-version=2016-10-01&lt;/set-url\&gt;  
        &lt;set-method\&gt;POST&lt;/set-method\&gt;  
        &lt;set-header name\=&quot;Content-Type&quot; exists-action\=&quot;override&quot;\&gt;  
            &lt;value\&gt;application/json&lt;/value\&gt;  
        &lt;/set-header\&gt;  
        &lt;set-header name\=&quot;Authorization&quot; exists-action\=&quot;override&quot;\&gt;  
            &lt;value\&gt;Bearer \*\*\*\*&lt;/value\&gt;  
        &lt;/set-header\&gt;  
    &lt;/send-request\&gt;  

    &lt;return-response\&gt;  
        &lt;set-status code\=&quot;200&quot; reason\=&quot;OK&quot; /&gt;  
        &lt;set-body\&gt;@(((IResponse)context.Variables\[&quot;responsela&quot;\]).Body.As&lt;JObject\&gt;(preserveContent: true).ToString())&lt;/set-body\&gt;  
    &lt;/return-response\&gt;  

&lt;/inbound\&gt;  
&lt;outbound\&gt;  
    &lt;base /&gt;  
&lt;/outbound\&gt;  
&lt;on-error\&gt;  
    &lt;base /&gt;  
&lt;/on-error\&gt;  
&lt;backend\&gt;  
    &lt;base /&gt;  
&lt;/backend\&gt;  
</code></pre>
<p>&lt;/policies&gt;</p>
<p>All the tags are quite self-explanatory and there a loads of documentation available about them. <return-response> is very useful policy, it suspends further policy pipeline execution and returns to the caller.</p>
<p>Hope this helps.</p>
<p>Cheers!</p>
]]></content:encoded></item></channel></rss>