<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shpend Kelmendi</title>
    <description>The latest articles on DEV Community by Shpend Kelmendi (@shpendkel).</description>
    <link>https://dev.to/shpendkel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3625504%2Ffc0eaef0-f924-497a-9c65-fcb3e7893a36.jpeg</url>
      <title>DEV Community: Shpend Kelmendi</title>
      <link>https://dev.to/shpendkel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shpendkel"/>
    <language>en</language>
    <item>
      <title>How to deploy across multiple Entra Id Tenants with Multitenant App Registrations</title>
      <dc:creator>Shpend Kelmendi</dc:creator>
      <pubDate>Mon, 08 Dec 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/shpendkel/how-to-deploy-across-multiple-azure-tenants-with-multitenant-app-registrations-134p</link>
      <guid>https://dev.to/shpendkel/how-to-deploy-across-multiple-azure-tenants-with-multitenant-app-registrations-134p</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokdo75t2f44eonwhl8k2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokdo75t2f44eonwhl8k2.png" alt="Deploy to multiple Entra ID tenants with Azure DevOps" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You have to deploy your workload.&lt;br&gt;&lt;br&gt;
You follow best practice and provision your workload per IaC and CI/CD pipelines. Now that sounds easy. But how do you do this when you need to deploy your workload to multiple Entra ID tenants?&lt;/p&gt;

&lt;p&gt;In this blog post you will learn how this works and how you can do it by yourself step by step.&lt;/p&gt;
&lt;h2&gt;
  
  
  Problem&lt;a href="https://shpend-kelmendi.ch/2025/12/08/deploy-multiple-tenants#problem" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Many organizations work with multiple Azure tenants - perhaps you have separate tenants for different customers, business units or environments. When you need to deploy the same workload across these tenants using Azure DevOps pipelines, you face a challenge:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;service connections are tied to a single tenant&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For my open source project (&lt;a href="https://github.com/ShpendKe/secret-rotation-local-deploy" rel="noopener noreferrer"&gt;Secret Rotation&lt;/a&gt;) I was challenged to solve this problem.&lt;br&gt;&lt;br&gt;
So there are one home tenant (Entra ID) and two Entra External IDs as CIAMs. One for non productive environment and one for productive environment.&lt;br&gt;&lt;br&gt;
PS: I can really recommend to keep them apart.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution (I used)&lt;a href="https://shpend-kelmendi.ch/2025/12/08/deploy-multiple-tenants#solution-i-used" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnl28bzqahek9n0bh8rh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnl28bzqahek9n0bh8rh.png" alt="Deploy to multiple Entra ID tenants with Azure DevOps" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The solution is to use a &lt;strong&gt;multitenant app registration&lt;/strong&gt; that can authenticate across multiple Entra ID tenants. Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a multitenant app registration in your primary tenant (home tenant)&lt;/li&gt;
&lt;li&gt;Create service principals in each target tenant (Non-Prod, Prod)&lt;/li&gt;
&lt;li&gt;Assign appropriate permissions to each service principal&lt;/li&gt;
&lt;li&gt;Use the same app registration credentials to deploy to all tenants&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach centralizes authentication while distributing authorization, giving you the flexibility to deploy anywhere while maintaining security.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prerequisites&lt;a href="https://shpend-kelmendi.ch/2025/12/08/deploy-multiple-tenants#prerequisites" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Access and permissions to

&lt;ul&gt;
&lt;li&gt;Entra ID (e.g. Contributer) and&lt;/li&gt;
&lt;li&gt;Entra External IDs (Application Developer role or higher)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Azure CLI installed&lt;/li&gt;
&lt;li&gt;Access to Azure DevOps&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  1. Create and configure multitenant app registration&lt;a href="https://shpend-kelmendi.ch/2025/12/08/deploy-multiple-tenants#1-create-and-configure-multitenant-app-registration" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;First, create a service connection in Azure DevOps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In Azure DevOps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to: Project → Project Settings → Service Connections&lt;/li&gt;
&lt;li&gt;Create a new Azure Resource Manager service connection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recommendation:&lt;/strong&gt; Use Workload Identity Federation for better security&lt;/li&gt;
&lt;li&gt;Click on "Manage App Registration" to open the Azure Portal&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Configure multitenant support:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A - Using Azure Portal UI:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the app registration, go to: Manage → Authentication (Preview)&lt;/li&gt;
&lt;li&gt;Under Settings → Supported account types&lt;/li&gt;
&lt;li&gt;Select: "Accounts in any organizational directory (Any Microsoft Entra ID tenant - Multitenant)"&lt;/li&gt;
&lt;li&gt;Click "Save"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Option B - Using Azure CLI:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Login and select your subscription linked to your home tenant&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Get your app registration details from the Azure Portal Overview page&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$clientIdFromHomeTenant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{YOUR_APP_CLIENT_ID_IN_HOME_TENANT}"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Update the app registration to multi-tenant&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$clientIdFromHomeTenant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--sign-in-audience&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AzureADMultipleOrgs&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create service principal in target tenant(s)&lt;a href="https://shpend-kelmendi.ch/2025/12/08/deploy-multiple-tenants#2-create-service-principal-in-target-tenants" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;For each tenant where you want to deploy, you need to create a service principal that represents your multitenant app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Define your target tenant and the app client ID from Tenant X&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$tenantXId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{YOUR_APP_REG_IN_TENANT_X}"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Login to the target tenant (--allow-no-subscripts is needed if no subscription is linked, e.g. Entra External Id)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$tenantXId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--allow-no-subscriptions&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Create the service principal (enterprise application)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$clientIdFromHomeTenant&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; This creates an enterprise application in Tenant B, not a full app registration. The app registration always lives in Tenant A. The enterprise application in Tenant B is essentially a "pointer" to the app in Tenant A.&lt;/p&gt;

&lt;p&gt;The result will look similar to the following extract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals/$entity",
  ...
  "appDisplayName": "{YOUR_APP_REG_IN_HOME_TENANT}",
  "appId": "{APP_ID_FROM_HOME_TENANT}",
  "appOwnerOrganizationId": "{HOME_TENANT_ID}",
  ...
  "displayName": "{YOUR_APP_REG_IN_HOME_TENANT}",
  ...
  "servicePrincipalNames": [
    "{APP_ID_FROM_HOME_TENANT}"
  ],
  "servicePrincipalType": "Application",
  "signInAudience": "AzureADMultipleOrgs",
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that the information are taken from the App Registration in home tenant (Entra ID).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Assign Entra ID role to service principal&lt;a href="https://shpend-kelmendi.ch/2025/12/08/deploy-multiple-tenants#3-assign-entra-id-role-to-service-principal" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;If your deployment needs to create or manage Entra ID resources (like app registrations), you need to assign appropriate directory roles:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Some directory roles need to be activated before they can be assigned. Here's how to check and activate if needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Check if the role is already activated&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$activatedRole&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;--url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://graph.microsoft.com/v1.0/directoryRoles?&lt;/span&gt;&lt;span class="se"&gt;`$&lt;/span&gt;&lt;span class="s2"&gt;filter=roleTemplateId eq '&lt;/span&gt;&lt;span class="nv"&gt;$roleId&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$activatedRole&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Activating role..."&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nv"&gt;$activateBody&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sh"&gt;@"
    { "roleTemplateId": "&lt;/span&gt;&lt;span class="nv"&gt;$roleId&lt;/span&gt;&lt;span class="sh"&gt;" }
"@&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="c"&gt;# Activate role because not active yet&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nv"&gt;$activatedRole&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;--headers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Content-Type=application/json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;--url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://graph.microsoft.com/v1.0/directoryRoles"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;--body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$activateBody&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can assign the entra role to your service principal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Login to the target tenant&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$tenantXId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{YOUR_APP_REG_IN_TENANT_X}'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$tenantXId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--allow-no-subscriptions&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Get the service principal object ID&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$appName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{YOUR_APP_NAME}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$spObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--display-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$appName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[0].id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tsv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Get the role definition ID (example: Application Administrator)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$roleId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'https://graph.microsoft.com/v1.0/directoryRoleTemplates'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value[?displayName=='Application Administrator'].id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tsv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Prepare the role assignment request with tenant scope&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$bodyContent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"@odata.type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#microsoft.graph.unifiedRoleAssignment"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"roleDefinitionId"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$roleId&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"principalId"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$spObjectId&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"directoryScopeId"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertTo-Json&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Direct passing the body content failed. If you know why, let me know :). &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Current workaround with json file&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Set-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;body.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$bodyContent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Encoding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;UTF8&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Assign the role&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;--headers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Content-Type=application/json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;--url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;--body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'@body.json'&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Cleanup&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Remove-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;body.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Assign Azure RBAC roles (optional)&lt;a href="https://shpend-kelmendi.ch/2025/12/08/deploy-multiple-tenants#4-assign-azure-rbac-roles-optional" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;If you need to deploy Azure resources, assign appropriate Azure roles at the subscription or resource group level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Login and set the subscription context&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$subscriptionId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{YOUR_SUBSCRIPTION_ID}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--subscription&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$subscriptionId&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Assign Contributor role at subscription level&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;assignment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;--assignee&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$clientIdFromHomeTenant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Contributor"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;--scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/subscriptions/&lt;/span&gt;&lt;span class="nv"&gt;$subscriptionId&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Use in Azure DevOps pipelines&lt;a href="https://shpend-kelmendi.ch/2025/12/08/deploy-multiple-tenants#5-use-in-azure-devops-pipelines" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Now you can use the same service connection to deploy to multiple tenants. In my secret rotation project I used &lt;code&gt;DefaultAzureCredential&lt;/code&gt;. It uses the tenant id passed in bicepparam file.&lt;br&gt;&lt;br&gt;
Secret rotation is implemented based on &lt;a href="https://github.com/Azure/bicep/blob/main/docs/experimental/local-deploy.md" rel="noopener noreferrer"&gt;local-deploy (Experimental)&lt;/a&gt;. This allows me to run the secret-rotator on a monthly basis and create or rotate the secrets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;schedules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;20&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt; 
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Monthly Secret Rotation&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RotateSecrets&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rotate Azure Secrets&lt;/span&gt;
    &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AzureCLI@2&lt;/span&gt;
        &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rotate&lt;/span&gt;
        &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;azureSubscription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{YOUR_SERVICE_CONNECTION}"&lt;/span&gt;
          &lt;span class="na"&gt;scriptType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pscore"&lt;/span&gt;
          &lt;span class="na"&gt;scriptLocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inlineScript&lt;/span&gt;
          &lt;span class="na"&gt;inlineScript&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;$env:BICEP_TRACING_ENABLED = "true"&lt;/span&gt;
            &lt;span class="s"&gt;bicep local-deploy ./bicep/secret-rotation/main.bicepparam&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion&lt;a href="https://shpend-kelmendi.ch/2025/12/08/deploy-multiple-tenants#conclusion" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Deploying to multiple Entra ID tenants doesn't have to be complicated. By leveraging multitenant app registrations, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintain a single app registration in your primary tenant&lt;/li&gt;
&lt;li&gt;Deploy to as many target tenants as needed&lt;/li&gt;
&lt;li&gt;Simplify your CI/CD pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key is understanding that the app registration lives in one place (home tenant), while service principals in each target tenant provide the necessary permissions to deploy resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Convert your app registration to multitenant&lt;/li&gt;
&lt;li&gt;Create service principals in each target tenant&lt;/li&gt;
&lt;li&gt;Assign appropriate Entra ID and Azure roles to your service principal per tenant&lt;/li&gt;
&lt;li&gt;Use the same credentials with different tenant IDs in your pipelines&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach scales well and makes managing deployments across multiple tenants much more maintainable.&lt;/p&gt;

&lt;p&gt;Do you have a better approach? Any suggestion how to improve? Let me know :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Further readings&lt;a href="https://shpend-kelmendi.ch/2025/12/08/deploy-multiple-tenants#further-readings" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles" rel="noopener noreferrer"&gt;Built-in Azure Roles with Role Definition ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference" rel="noopener noreferrer"&gt;Built-in Entra ID Roles with Role Definition ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-cli" rel="noopener noreferrer"&gt;How to assign role per CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/directoryroletemplate-get?view=graph-rest-1.0&amp;amp;tabs=http" rel="noopener noreferrer"&gt;Directory Role Template API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/directoryrole-post-directoryroles?view=graph-rest-1.0&amp;amp;tabs=http" rel="noopener noreferrer"&gt;Activate Directory Role&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/rbacapplication-post-roleassignments?view=graph-rest-1.0&amp;amp;tabs=http#example-1-create-a-role-assignment-with-tenant-scope" rel="noopener noreferrer"&gt;Assign Entra ID Role to Service Principal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>bicep</category>
      <category>azurecli</category>
      <category>entraid</category>
      <category>iac</category>
    </item>
    <item>
      <title>Quick Azure Cost Management: Budget and Notifications with Bicep and alternatives</title>
      <dc:creator>Shpend Kelmendi</dc:creator>
      <pubDate>Fri, 21 Nov 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/shpendkel/quick-azure-cost-management-budget-and-notifications-with-bicep-and-alternatives-12l</link>
      <guid>https://dev.to/shpendkel/quick-azure-cost-management-budget-and-notifications-with-bicep-and-alternatives-12l</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62ei7r5digfs4gqi7w2v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62ei7r5digfs4gqi7w2v.png" alt="Quick Azure Cost Management: Budget and Notifications with Bicep and alternatives" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Managing costs in the cloud is crucial to avoid unexpected expenses.&lt;br&gt;&lt;br&gt;
How many times have I read "Azure just charged me 1k dollars because of a misconfiguration".&lt;br&gt;&lt;br&gt;
Did it happen to you as well? Me too. And I know how stupid you feel after that. Sometimes it is not even our fault. (cough* strange defaults from Azure *cough)&lt;/p&gt;

&lt;p&gt;So to avoid that, we can setup budgets and get notified when we reach certain thresholds.&lt;br&gt;&lt;br&gt;
It's important to know which scope you want to set the budget on. It can be at management group level, subscription level or resource group level. Choose the scope you are responsible for and want to monitor costs for.&lt;/p&gt;

&lt;p&gt;What are the options? Let's explore them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using Bicep&lt;a href="https://shpend-kelmendi.ch/2025/11/21/budget-alert#using-bicep" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;You can create a simple Bicep template to create a budget at subscription level with email notification. Here is a sample Bicep code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;targetScope = 'subscription'

@description('Name of the budget')
param budgetName string = 'MonthlyBudget'

@description('Monthly budget amount')
param amount int

@description('Email recipients for alerts')
param emailRecipients array

@description('Start date for the budget (YYYY-MM-DD)')
param startDate string = '${utcNow('yyyy-MM')}-01'

resource subscriptionBudget 'Microsoft.Consumption/budgets@2023-05-01' = {
  name: budgetName
  properties: {
    amount: amount
    category: 'Cost'
    timeGrain: 'Monthly'
    timePeriod: {
      startDate: startDate
    }
    notifications: {
      Forecasted_GreaterThan_90_Percent: {
        enabled: true
        operator: 'GreaterThan'
        threshold: 80
        contactEmails: emailRecipients
        thresholdType: 'Forecasted'
      }
      Actual_GreaterThan_80_Percent: {
        enabled: true
        operator: 'GreaterThan'
        threshold: 80
        contactEmails: emailRecipients
        thresholdType: 'Actual'
      }
      Actual_GreaterThan_100_Percent: {
        enabled: true
        operator: 'GreaterThan'
        threshold: 100
        contactEmails: emailRecipients
        thresholdType: 'Actual'
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This bicep template creates a monthly budget with specified amount and email notifications when the actual or forecasted costs exceed 80% and 100% of the budget. You will be notified via email to the specified recipients until end of date (because no end date is specified, it will run for 10 years after last deployment).&lt;/p&gt;

&lt;p&gt;You can deploy this Bicep template using the Azure CLI with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az deployment sub create &lt;span class="nt"&gt;-l&lt;/span&gt; switzerlandnorth &lt;span class="nt"&gt;-f&lt;/span&gt; budget.bicep &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100 &lt;span class="nv"&gt;emailRecipients&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"['YOUR-EMAIL@ADDRESS.COM']"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After successful deployment, you should see the budget in the Azure portal like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fif9nej7atwjo10bl601g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fif9nej7atwjo10bl601g.png" alt="Budget set after bicep successful execution" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using AZ CLI&lt;a href="https://shpend-kelmendi.ch/2025/11/21/budget-alert#using-az-cli" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Alternatively, you can create a budget using the Azure CLI. Here is an example command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Sign into Azure CLI with your account&lt;/span&gt;
az login

&lt;span class="c"&gt;# Select a subscription to monitor with a budget&lt;/span&gt;
az account &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--subscription&lt;/span&gt; &lt;span class="s2"&gt;"Your Subscription"&lt;/span&gt;

&lt;span class="c"&gt;# Create an action group email receiver and corresponding action group&lt;/span&gt;
&lt;span class="nv"&gt;email1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az monitor action-group receiver email create &lt;span class="nt"&gt;--email-address&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;@test.com &lt;span class="nt"&gt;--name&lt;/span&gt; EmailReceiver1 &lt;span class="nt"&gt;--resource-group&lt;/span&gt; YourResourceGroup &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;ActionGroupId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;az monitor action-group create &lt;span class="nt"&gt;--resource-group&lt;/span&gt; YourResourceGroup &lt;span class="nt"&gt;--name&lt;/span&gt; TestAG &lt;span class="nt"&gt;--short-name&lt;/span&gt; TestAG &lt;span class="nt"&gt;--receiver&lt;/span&gt; &lt;span class="nv"&gt;$email1&lt;/span&gt; &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; tsv&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Create a monthly budget that sends an email and triggers an Action Group to send a second email.&lt;/span&gt;
&lt;span class="c"&gt;# Make sure the StartDate for your monthly budget is set to the first day of the current month.&lt;/span&gt;
&lt;span class="c"&gt;# Note that Action Groups can also be used to trigger automation such as Azure Functions or Webhooks.&lt;/span&gt;

az consumption budget create-with-rg &lt;span class="nt"&gt;--amount&lt;/span&gt; 100 &lt;span class="nt"&gt;--budget-name&lt;/span&gt; TestCLIBudget &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="nv"&gt;$rg&lt;/span&gt; &lt;span class="nt"&gt;--category&lt;/span&gt; Cost &lt;span class="nt"&gt;--time-grain&lt;/span&gt; Monthly &lt;span class="nt"&gt;--time-period&lt;/span&gt; &lt;span class="s1"&gt;'{"start-date":"2024-06-01","end-date":"2025-12-31"}'&lt;/span&gt; &lt;span class="nt"&gt;--notifications&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Key1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;operator&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;GreaterThanOrEqualTo&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;contact-emails&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:[],  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;threshold&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:80.0, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;contact-groups&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:[&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$ActionGroupId&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/cost-management-billing/costs/tutorial-acm-create-budgets?tabs=clibudget#create-and-edit-budgets" rel="noopener noreferrer"&gt;Source of code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But be careful: it's in preview and might change in the future. Another challenge with this approach is readability and maintainability of the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Azure Portal&lt;a href="https://shpend-kelmendi.ch/2025/11/21/budget-alert#using-azure-portal" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;You can also create a budget directly from the Azure Portal. Follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to Subscription or Resource group depending on your responsibility.&lt;/li&gt;
&lt;li&gt;Click on "Budgets" under the "Cost Management" section.&lt;/li&gt;
&lt;li&gt;Click on "+ Add" to create a new budget.&lt;/li&gt;
&lt;li&gt;Enter the budget details such as name, amount, time period.&lt;/li&gt;
&lt;li&gt;Set alerts by setting first condition to 80% and second to 100% with email notifications.&lt;/li&gt;
&lt;li&gt;Add all email addresses that should receive the notifications.&lt;/li&gt;
&lt;li&gt;Hit "Create" to finalize the budget setup.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion&lt;a href="https://shpend-kelmendi.ch/2025/11/21/budget-alert#conclusion" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I highly recommend setting up budgets for your Azure resources to avoid unexpected costs.&lt;br&gt;&lt;br&gt;
This should be done as early as possible in your cloud journey.&lt;br&gt;&lt;br&gt;
It's one of the best practices in the Azure Well-Architected Framework under Cost Management pillar. By applying budgets, you can monitor and control your cloud spending effectively and achieve the fullfil the following design principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Continuously right-size investment as your workload evolves with the ecosystem."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using Bicep makes it easy to automate the setup and ensures consistency across your environments.&lt;br&gt;&lt;br&gt;
Another benefit for using Bicep is readability and maintainability of your infrastructure as code.&lt;br&gt;&lt;br&gt;
Other benefits of IaC include version control, collaboration, and repeatability. But that is a topic for another blog post.&lt;/p&gt;

&lt;p&gt;You can customize the budget amount and notification thresholds as per your requirements.&lt;br&gt;&lt;br&gt;
Go always for the automated way if possible!&lt;/p&gt;

&lt;p&gt;PS: If you still get surprised by unexpected costs, consider contacting Azure Support for a refund. They are usually quite helpful in such cases.&lt;/p&gt;

&lt;p&gt;Happy budgeting!&lt;/p&gt;

&lt;h2&gt;
  
  
  Further readings&lt;a href="https://shpend-kelmendi.ch/2025/11/21/budget-alert#further-readings" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/cost-optimization/principles#monitor-and-optimize-over-time" rel="noopener noreferrer"&gt;Monitor and optimize over time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/cost-management-billing/costs/tutorial-acm-create-budgets?tabs=psbudget" rel="noopener noreferrer"&gt;Tutorial: Create and manage budgets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>wellarchitectedframework</category>
      <category>costmanagement</category>
      <category>azurebudget</category>
    </item>
    <item>
      <title>Practical guide to organizing Azure resources</title>
      <dc:creator>Shpend Kelmendi</dc:creator>
      <pubDate>Mon, 17 Nov 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/shpendkel/practical-guide-to-organizing-azure-resources-aje</link>
      <guid>https://dev.to/shpendkel/practical-guide-to-organizing-azure-resources-aje</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ckb6y5czox9ztdtw7tc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ckb6y5czox9ztdtw7tc.png" alt="How to organize Azure resources"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Organizing Azure resources is one of those rare tasks that sounds straightforward until you actually start doing it.&lt;br&gt;&lt;br&gt;
Suddenly you’re trying to balance cost allocation, security boundaries, governance rules, domain ownership, deployment pipelines, and team autonomy. All while the number of services grows faster than your architecture diagram can keep up.&lt;/p&gt;

&lt;p&gt;If designing cloud boundaries feels like organizing a city while it’s still being built, that’s because it is.&lt;br&gt;&lt;br&gt;
And like any good city, Azure needs structure: districts, buildings, addresses, zoning rules, and a way to understand who owns what. Why? Because moving buildings (or Azure resources) is not always easy.&lt;/p&gt;

&lt;p&gt;This guide takes a practical look at how to design resource boundaries in Azure—using concepts from Domain-Driven Design (DDD), Team Topologies, CAF, Well-Architected Framework.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why this is hard: it's about people and the domain, not just resources&lt;a href="https://shpend-kelmendi.ch/2025/11/17/resource-organization#why-this-is-hard-its-about-people-and-the-domain-not-just-resources" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Maybe you have seen the one video:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/rZ3ETK7-ZM8"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Well it's funny, but it is also how some people think about organizing Azure resources. Most architecture decisions aren’t purely technical.&lt;br&gt;&lt;br&gt;
They reflect how teams collaborate, how business domains interact, and what outcomes the organization values.&lt;/p&gt;

&lt;p&gt;When teams ask how they should group Azure resources, they are often really asking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do we ensure ownership is clear? Who is responsible for this workload?&lt;/li&gt;
&lt;li&gt;How do we track costs reliably?&lt;/li&gt;
&lt;li&gt;How do we deploy our workload? What do we deploy together?&lt;/li&gt;
&lt;li&gt;How do we enforce consistent governance without slowing down?&lt;/li&gt;
&lt;li&gt;How do we react to changes in requirements and evolution of the architecture?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding these drivers makes resource organization far more meaningful than simply creating a subscription or a resource group "because that’s what the documentation says."&lt;/p&gt;

&lt;h2&gt;
  
  
  Approaches for our problem&lt;a href="https://shpend-kelmendi.ch/2025/11/17/resource-organization#approaches-for-our-problem" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I see some approaches to solve this problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bounded context from Domain Driven Design (DDD)&lt;/li&gt;
&lt;li&gt;Team Topology&lt;/li&gt;
&lt;li&gt;Azure Resource Grouping Mechanism

&lt;ul&gt;
&lt;li&gt;Mgmt Group&lt;/li&gt;
&lt;li&gt;Subscription&lt;/li&gt;
&lt;li&gt;Resource Group&lt;/li&gt;
&lt;li&gt;Tags&lt;/li&gt;
&lt;li&gt;Service Groups&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;So let's go through those and how those can help us.&lt;/p&gt;

&lt;h2&gt;
  
  
  DDD and team topologies: let your domain shape your cloud solution&lt;a href="https://shpend-kelmendi.ch/2025/11/17/resource-organization#ddd-and-team-topologies-let-your-domain-shape-your-cloud-solution" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;One of the biggest challenges in cloud architecture is aligning resource boundaries with &lt;strong&gt;business domains and teams&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Domain-driven design (DDD)&lt;a href="https://shpend-kelmendi.ch/2025/11/17/resource-organization#domain-driven-design-ddd" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;DDD gives you with bounded context a powerful tool to define clear boundaries around different parts of your system. Its part of the strategic design patterns in DDD. With bounded contexts, you can encapsulate specific business logic, data models, and workflows within well-defined limits. Owned and managed by dedicated teams, bounded contexts reduce complexity and improve maintainability. It helps to answer the question:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What belongs together?"&lt;/li&gt;
&lt;li&gt;"What can evolve independently?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A bounded context doesn't mean you have to deploy it separately. It can be a module within a larger application. But when it comes to cloud resources, aligning bounded contexts with Azure boundaries can bring significant benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each bounded context can map to &lt;strong&gt;its own resource group&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;For bigger organizations, a &lt;strong&gt;dedicated subscription&lt;/strong&gt; may be required. IMO this is not always necessary.&lt;/li&gt;
&lt;li&gt;Independent life-cycles, data ownership, and pipelines align naturally with Azure boundaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Team topologies&lt;a href="https://shpend-kelmendi.ch/2025/11/17/resource-organization#team-topologies" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Team topologies is another useful framework to consider when organizing Azure resources. It was introduced by Matthew Skelton and Manuel Pais in their book "Team Topologies". It emphasizes the importance of team structure and interactions in shaping software architecture. The four fundamental team types are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stream-aligned teams:&lt;/strong&gt; Own their domain end-to-end -&amp;gt; subscription or resource group.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform teams:&lt;/strong&gt; Provide shared services (networking, identity, monitoring, ...) and reduces cognitive load for teams using it. -&amp;gt; subscription or/and management group.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enabling teams:&lt;/strong&gt; Support other teams -&amp;gt; no dedicated boundaries needed but governance needs to be considered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complicated subsystem teams:&lt;/strong&gt; Specialized workloads that require deep expertise -&amp;gt; isolated subscription.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maybe you are in a small organization where one team wears multiple hats. That's fine. You should think about how your solution will scale as the organization grows. Aligning Azure structure with team responsibilities reduces friction and clarifies ownership.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure grouping mechanism&lt;a href="https://shpend-kelmendi.ch/2025/11/17/resource-organization#azure-grouping-mechanism" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now that we have an understanding of DDD and Team Topologies, let's look at Azure grouping mechanisms and their purpose. Azure provides multiple mechanisms to organize resources, each optimized for different scenarios.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overview of Azure grouping mechanisms&lt;a href="https://shpend-kelmendi.ch/2025/11/17/resource-organization#overview-of-azure-grouping-mechanisms" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Let's have a look at what is available:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxwnm8qfrt9vxb9c5yvf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxwnm8qfrt9vxb9c5yvf.png" alt="Azure grouping mechanism overview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://learn.microsoft.com/en-us/azure/governance/service-groups/overview" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/azure/governance/service-groups/overview&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Mechanism&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Management groups&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Logical containers that organize management groups and subscriptions into a hierarchy for unified policy and access management. Policies and RBAC permissions applied at this level inherit down to all subscriptions and resources below.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subscriptions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Logical container linked to a credit card. Each subscription has its own limits, quotas, and provides isolation between different parts of your organization.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource groups&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Logical containers that hold related Azure resources. Goal is to group resources the same lifecycle: deploy, update, and delete together.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Azure Service Groups&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Logical container kept in tenant. Allows flexible grouping of resources across subscriptions, resource groups and resources. Currently in public preview.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tags&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Key-value pairs of metadata that can be applied to resources, resource groups and subscriptions. Tags do not inherit automatically. You have to enforce this by policy or tagged individually at each level.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Comparison of Azure grouping mechanisms&lt;a href="https://shpend-kelmendi.ch/2025/11/17/resource-organization#comparison-of-azure-grouping-mechanisms" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Resource Group&lt;/th&gt;
&lt;th&gt;Subscription&lt;/th&gt;
&lt;th&gt;Management Group&lt;/th&gt;
&lt;th&gt;Service Group&lt;/th&gt;
&lt;th&gt;Tags&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Can it contain and organize resources in a hierarchy?&lt;/td&gt;
&lt;td&gt;Yes (contains resources)&lt;/td&gt;
&lt;td&gt;Yes (contains resource groups)&lt;/td&gt;
&lt;td&gt;Yes (contains subscriptions, management groups)&lt;/td&gt;
&lt;td&gt;Yes (contains resources, resource groups, or subscriptions but only within Azure Service Group)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can resources from different subscriptions be grouped together?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes**&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can the same resource belong to multiple groups of this type?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can policies be enforced on this grouping?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes***&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Are policies inherited downward in the hierarchy?&lt;/td&gt;
&lt;td&gt;Yes (to resources)&lt;/td&gt;
&lt;td&gt;Yes (to resource groups and below)&lt;/td&gt;
&lt;td&gt;Yes (to subscriptions and below)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can role-based access control (RBAC) be applied?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (Service Group only)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Are RBAC permissions inherited downward in the hierarchy?&lt;/td&gt;
&lt;td&gt;Yes (to resources)&lt;/td&gt;
&lt;td&gt;Yes (to resource groups and resources)&lt;/td&gt;
&lt;td&gt;Yes (to subscriptions and below)&lt;/td&gt;
&lt;td&gt;Partial (only to child Service Groups, not to member resources)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can it be used for cost management and billing?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes ****&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can it be used for different environments (prod, test, dev)?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can it be used for ownership tracking?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes ****&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;* Applying a policy will enforce it to all members within the scope. For example, on a Resource Group it only applies to the resources under it.&lt;br&gt;&lt;br&gt;
** Tags can be applied across scopes and are added to resources individually. Azure Policy has built-in policies that can help manage tags.&lt;br&gt;&lt;br&gt;
*** Azure tags can be used as criteria within Azure Policy to apply policies to certain resources. Azure tags are subject to limitations.&lt;br&gt;&lt;br&gt;
**** To avoid inconsistencies, it's recommended to apply tags from the beginning and enforce them via policies. If you are already in this situation, you have different options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Notify subscription/resource group owners to apply tags.&lt;/li&gt;
&lt;li&gt;Notify users with access to subscriptions/resource groups to apply tags.&lt;/li&gt;
&lt;li&gt;Stop/disable resources without required tags (not recommended for production workloads and only after clarified with management).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion&lt;a href="https://shpend-kelmendi.ch/2025/11/17/resource-organization#conclusion" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Resource organization in Azure is not a one-size-fits-all task. It requires a thoughtful approach that considers business domains, team structures, governance needs, and operational practices. By leveraging principles from DDD and Team Topologies, and utilizing Azure's grouping mechanisms effectively, you can create a resource organization strategy that aligns with your organization's goals and workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further readings&lt;a href="https://shpend-kelmendi.ch/2025/11/17/resource-organization#further-readings" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-setup-guide/organize-resources" rel="noopener noreferrer"&gt;Organize your Azure resources effectively&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-setup-guide/initial-subscriptions" rel="noopener noreferrer"&gt;Quick setup subscription&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/landing-zone/design-area/management-application-environments#environments-subscriptions-and-management-groups" rel="noopener noreferrer"&gt;Manage application development environments in Azure landing zones&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://martinfowler.com/bliki/TeamTopologies.html" rel="noopener noreferrer"&gt;Team Topologies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://martinfowler.com/bliki/BoundedContext.html" rel="noopener noreferrer"&gt;Bounded Context pattern in DDD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/governance/service-groups/overview" rel="noopener noreferrer"&gt;Service Groups in Azure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?source=recommendations" rel="noopener noreferrer"&gt;Use tags to organize your Azure resources&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>resourceorganization</category>
      <category>domaindrivendesign</category>
      <category>teamtopologies</category>
    </item>
    <item>
      <title>Fix expired secrets in Azure DevOps Service Connections</title>
      <dc:creator>Shpend Kelmendi</dc:creator>
      <pubDate>Wed, 12 Nov 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/shpendkel/fix-expired-secrets-in-azure-devops-service-connections-1j5a</link>
      <guid>https://dev.to/shpendkel/fix-expired-secrets-in-azure-devops-service-connections-1j5a</guid>
      <description>&lt;p&gt;Workload Identity Federation is the recommended approach for Service Connection. So why would I use the old approach with an app registration where I need to rotate the secret by myself? Well, there are some pipeline tasks which doesn't support Workload Identity Federation.&lt;br&gt;&lt;br&gt;
And in this case, you have to use the old approach.&lt;br&gt;&lt;br&gt;
And today was the day of rotation.&lt;br&gt;&lt;br&gt;
And a team member tried to renew the secret but didn't know how to do it, because the problem is still not fixed correctly in the UI.&lt;/p&gt;

&lt;p&gt;Let's check the steps to fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Problem&lt;a href="https://shpend-kelmendi.ch/2025/11/12/ado-service-connection#1-problem" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xfoa2wvkty5gge2xku0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xfoa2wvkty5gge2xku0.png" alt="Service Connection with App Registration and Secret" width="556" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You have created a service connection with app registration (automatic).&lt;br&gt;&lt;br&gt;
Your secret expired.&lt;br&gt;&lt;br&gt;
You don't know how to fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Solutions&lt;a href="https://shpend-kelmendi.ch/2025/11/12/ado-service-connection#2-solutions" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Renew secret&lt;a href="https://shpend-kelmendi.ch/2025/11/12/ado-service-connection#21-renew-secret" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to ADO -&amp;gt; Project Settings&lt;/li&gt;
&lt;li&gt;Click on Service Connection, which makes trouble&lt;/li&gt;
&lt;li&gt;Click on "Manage App registration"&lt;/li&gt;
&lt;li&gt;In Azure Portal -&amp;gt; Manage -&amp;gt; Certificate &amp;amp; secrets&lt;/li&gt;
&lt;li&gt;Delete your expired secret&lt;/li&gt;
&lt;li&gt;Create a new secret&lt;/li&gt;
&lt;li&gt;Go back to ADO&lt;/li&gt;
&lt;li&gt;Click on the Edit button&lt;/li&gt;
&lt;li&gt;Change description&lt;/li&gt;
&lt;li&gt;Click the Save button&lt;/li&gt;
&lt;li&gt;Click on Edit again&lt;/li&gt;
&lt;li&gt;Click on Verify&lt;/li&gt;
&lt;li&gt;It should show Verification succeeded&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You may need to repeat steps 8-13 if verification does not succeed the first time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpts5zqjcprjo1qye74w1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpts5zqjcprjo1qye74w1.gif" alt="Steps to renew secrets for service connection" width="760" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Use Workload Identity Federation&lt;a href="https://shpend-kelmendi.ch/2025/11/12/ado-service-connection#22-use-workload-identity-federation" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Verify if there is a newer version of the task that supports Workload Identity Federation.&lt;/p&gt;

&lt;p&gt;In our example, it was the task &lt;code&gt;AzureFileCopy&lt;/code&gt; which supports with from version 6 workload identity federation.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 Implement yourself&lt;a href="https://shpend-kelmendi.ch/2025/11/12/ado-service-connection#23-implement-yourself" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A lot of the tasks can be done by yourself with pure scripts. Like the task mentioned above &lt;code&gt;AzureFileCopy&lt;/code&gt;:&lt;code&gt;az storage blob upload -f /path/to/file -c mycontainer -n MyBlob&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The benefit would be that you can use the Ubuntu agent instead of the Windows agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Final thoughts&lt;a href="https://shpend-kelmendi.ch/2025/11/12/ado-service-connection#3-final-thoughts" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Recommendations are to use Workload Identity Federation where possible, because it's better not to handle secrets in any place and to forget the rotation. If this is not possible, try to implement it yourself if you feel confident.&lt;/p&gt;

&lt;p&gt;If not, you can still go for the hacky way.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>azuredevops</category>
      <category>serviceconnection</category>
      <category>workloadidentityfede</category>
    </item>
    <item>
      <title>When the cloud has a cold: Lessons in resilience</title>
      <dc:creator>Shpend Kelmendi</dc:creator>
      <pubDate>Thu, 30 Oct 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/shpendkel/when-the-cloud-has-a-cold-lessons-in-resilience-58ed</link>
      <guid>https://dev.to/shpendkel/when-the-cloud-has-a-cold-lessons-in-resilience-58ed</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7q9gk53if768b5ni5hpz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7q9gk53if768b5ni5hpz.png" alt="Cloud is down: Building Resilient Systems" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;October 9th&lt;/strong&gt; - Azure Front Door takes a nap. Traffic reroutes, dashboards go red, and half the internet suddenly learns what "global edge dependency" really means.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;October 20th&lt;/strong&gt; - an AWS region (US-EAST-1) goes down. Not the apocalypse, just enough to make dashboards bleed red, engineers reach for coffee, and LinkedIn light up with "That's why we use multi-cloud" posts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;October 29th&lt;/strong&gt; - Azure Front Door stumbles again. Same story, different day. And somewhere in between, a few other providers quietly joined the chaos with their own "we're experiencing elevated error rates" moments.&lt;/p&gt;

&lt;p&gt;It's been one of those months when you realize: even the clouds catch colds. The uptime gods don't play favorites - not AWS, not Azure, not anyone.&lt;/p&gt;

&lt;p&gt;And that's when Barry O'Reilly's Residuality Theory came to mind again - the idea that what really matters isn't the outage itself, but what's left behind.&lt;br&gt;
The tangled complexity.&lt;br&gt;
The assumptions that didn't hold.&lt;br&gt;
The edge cases you dismissed with a confident, "That'll never happen."&lt;/p&gt;

&lt;p&gt;Spoiler: &lt;strong&gt;it just did and will again&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The shared responsibility model and why it matters&lt;a href="https://shpend-kelmendi.ch/2025/10/30/azure-front-door-incident#1-the-shared-responsibility-model-and-why-it-matters" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;You're probably familiar with the Fallacies of Distributed Computing. These were observations made by L. Peter Deutsch and colleagues at Sun Microsystems about common misconceptions in the development of distributed systems.&lt;/p&gt;

&lt;p&gt;Here's the list again as a refresher:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The network is reliable;&lt;/li&gt;
&lt;li&gt;Latency is zero;&lt;/li&gt;
&lt;li&gt;Bandwidth is infinite;&lt;/li&gt;
&lt;li&gt;The network is secure;&lt;/li&gt;
&lt;li&gt;Topology doesn't change;&lt;/li&gt;
&lt;li&gt;There is one administrator;&lt;/li&gt;
&lt;li&gt;Transport cost is zero;&lt;/li&gt;
&lt;li&gt;The network is homogeneous;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But there's one more that should be added:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The cloud provider is responsible for everything".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's get one thing straight:&lt;br&gt;
Just because your app runs in the cloud doesn't mean it's automatically secure, scalable, performant or resilient.&lt;/p&gt;

&lt;p&gt;Cloud providers like Azure and AWS take care of the infrastructure, but &lt;a href="https://learn.microsoft.com/en-us/azure/reliability/concept-shared-responsibility" rel="noopener noreferrer"&gt;&lt;strong&gt;resilience is a shared responsibility&lt;/strong&gt;&lt;/a&gt;.&lt;br&gt;
They'll keep the lights on - but how your app reacts when the lights flicker? That's on you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbqsibkktj00p0t65aqzq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbqsibkktj00p0t65aqzq.png" alt="Azure Front Door Disruption: Building Resilient Systems" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see in the image above, Microsoft takes responsibility for the &lt;strong&gt;core platform&lt;/strong&gt; and additionally provides &lt;strong&gt;resilience capabilities&lt;/strong&gt; when the right components are selected (e.g. Zone-Redundancy, Automatic Backup, ...).&lt;/p&gt;

&lt;p&gt;By &lt;em&gt;components&lt;/em&gt;, I mean the &lt;strong&gt;Azure services&lt;/strong&gt;. Depending on the category (SaaS, FaaS, PaaS, IaaS), Microsoft takes on more or fewer responsibilities and provides varying levels of support to help you achieve &lt;strong&gt;reliability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For each component, there are also &lt;a href="https://learn.microsoft.com/en-us/azure/reliability/overview-reliability-guidance" rel="noopener noreferrer"&gt;Reliability Guides&lt;/a&gt; available to help you with implementation.&lt;/p&gt;

&lt;p&gt;At the end of the day, you're the captain of your workloads. You decide what "reliable" means, and that decision drives how you actually build and wire everything together. Microsoft can hand you the tools, but you still have to steer the ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What can we do about it?&lt;a href="https://shpend-kelmendi.ch/2025/10/30/azure-front-door-incident#2-what-can-we-do-about-it" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;You can't stop outages or incidents. It will happen. But you can &lt;strong&gt;design for failure&lt;/strong&gt;.&lt;br&gt;
Resilience isn't luck, it's the result of intentional, repeated practice.&lt;br&gt;
Here are some options how I would build into a system:&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Risk Storming&lt;a href="https://shpend-kelmendi.ch/2025/10/30/azure-front-door-incident#21-risk-storming" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://riskstorming.com/" rel="noopener noreferrer"&gt;Risk Storming&lt;/a&gt; is a collaborative workshop technique designed to help teams identify, visualize, and discuss software risks before they become real problems and promotes shared ownership of quality. It's used mostly as pre-mortem to identify risks before they happen.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to apply it&lt;a href="https://shpend-kelmendi.ch/2025/10/30/azure-front-door-incident#how-to-apply-it" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Prepare&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gather your team (architect, ops, and devs, ...) in a (virtual) room.
Virtual room: Use a shared whiteboard tool like &lt;strong&gt;Miro&lt;/strong&gt; or &lt;strong&gt;Mural&lt;/strong&gt;.
In person: Use a whiteboard and post-its&lt;/li&gt;
&lt;li&gt;Prepare post-its:

&lt;ul&gt;
&lt;li&gt;🔴 high impact/high likelihood → address immediately
&lt;/li&gt;
&lt;li&gt;🟡 high impact/low likelihood → create mitigation
&lt;/li&gt;
&lt;li&gt;🟢 low impact → document and monitor
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Define a scale you want to use (e.g. low/medium/high or numeric value 1-5)&lt;/li&gt;
&lt;li&gt;Prepare examples for better understanding&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Draw your architecture (at different levels)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Virtual room: use your drawings from your architecture documentation&lt;/li&gt;
&lt;li&gt;In person: use post-its (different color)&lt;/li&gt;
&lt;li&gt;This way you can put the identified risks close to the component.&lt;/li&gt;
&lt;li&gt;It can be current architecture or architecture which you plan to build or change&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Identify risks (individually)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Timebox (e.g. 10 min)&lt;/li&gt;
&lt;li&gt;everybody asks them self three questions in silence (no collaboration):

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;What can go wrong? (Description)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;How likely is it? (Likelihood)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;What is the negative impact if the risk does occur? (Impact)&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Benefit of this approach is that each person can bring risks from their perspective&lt;/li&gt;

&lt;li&gt;Keep in mind that to build a solution you need people, process and technology. So consider those in your risks. For example:

&lt;ul&gt;
&lt;li&gt;People: We don't have the knowledge.
&lt;/li&gt;
&lt;li&gt;Process: Manual works slows down delivery.&lt;/li&gt;
&lt;li&gt;Technology: Selected technology is not fulfilling the quality requirements.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Converge the risks&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Everybody put the identified risks on the diagram close to the area where the risk has been identified&lt;/li&gt;
&lt;li&gt;Big benefit of this approach is the visual approach. By doing this it gives you at a glance the hotspots of risks where you-&amp;gt; Focus on hotspot&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Review risks and take actions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gather and summarize risks&lt;/li&gt;
&lt;li&gt;Don't ignore risks identified by single person or risks with a lot of disagreement&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.2 Residuality Theory&lt;a href="https://shpend-kelmendi.ch/2025/10/30/azure-front-door-incident#22-residuality-theory" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Most risk management frameworks deal with the knowns, predictable failures you can plan for. Barry O'Reilly's Residuality Theory takes a different route: it focuses on how to survive the unknowns.&lt;/p&gt;

&lt;p&gt;You start by identifying stressors, unexpected forces that might hit your system. Not just technical ones like "database outage" but also business and human ones: "marketing goes viral", "supplier vanishes", or "new law kills a feature overnight". No idea is too crazy, that's the whole point.&lt;/p&gt;

&lt;p&gt;Then, you apply those stressors and watch what happens. Which parts of your system survive - the residues - and which collapse? You evolve the architecture by adding or adapting components that can handle that stress next time. And then you run the experiment again.&lt;/p&gt;

&lt;p&gt;Residuality Theory doesn't just build resilient systems; it builds organizations that can absorb chaos and come out smarter.&lt;/p&gt;

&lt;p&gt;Resilience isn't about predicting the storm - it's about sailing through it.&lt;/p&gt;

&lt;p&gt;If you want to dive deeper into this topic, here are a few gems worth your time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.architecture-weekly.com/p/residuality-theory-a-rebellious-take" rel="noopener noreferrer"&gt;Oskar Dudycz breaks it down with a great real-world example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=_MPUoiG6w_U" rel="noopener noreferrer"&gt;Barry O’Reilly’s NDC talk with theory and a great real-world example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=dkAmXBLCmHI" rel="noopener noreferrer"&gt;Barry O’Reilly in conversation with Eberhard Wolff - great discussion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.3 Designing for failure&lt;a href="https://shpend-kelmendi.ch/2025/10/30/azure-front-door-incident#23-designing-for-failure" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Let's talk frameworks.&lt;br&gt;
Azure's &lt;strong&gt;Well-Architected Framework&lt;/strong&gt; is basically the cloud version of life advice your grandparents would give you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep it simple,&lt;/li&gt;
&lt;li&gt;plan for the unexpected,&lt;/li&gt;
&lt;li&gt;and don't spend all your money in one place.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;Reliability pillar&lt;/strong&gt; boils down to this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Assume things will fail, and make sure your system doesn't fail with them."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means designing with principles like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/reliability/identify-flows" rel="noopener noreferrer"&gt;Prioritize your critical workloads - not every app needs five-nines availability.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/reliability/simplify" rel="noopener noreferrer"&gt;Keep your architecture simple enough to understand under pressure.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/reliability/self-preservation" rel="noopener noreferrer"&gt;Use automation and self-healing mechanisms.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/reliability/testing-strategy" rel="noopener noreferrer"&gt;Test your recovery, not just your uptime.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Document trade-offs - because every design choice costs something.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that last one - trade-offs - is the sneaky one.&lt;br&gt;
Resilience sounds amazing, until your CFO sees the cloud bill.&lt;br&gt;
Or your security team starts asking why you've got redundant data flows across three regions.&lt;br&gt;
That's the point where architecture becomes negotiation.&lt;/p&gt;

&lt;h4&gt;
  
  
  The trade-offs&lt;a href="https://shpend-kelmendi.ch/2025/10/30/azure-front-door-incident#the-trade-offs" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Here's the ugly truth:&lt;br&gt;
Every resilience decision is also a complexity decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implement multi-cloud failover?&lt;/strong&gt;&lt;br&gt;
Congratulations, you now have two sets of APIs, IAM policies, monitoring tools, and probably twice as many headaches.&lt;br&gt;
Multi-cloud can make sense - especially for regulatory reasons or when you truly need provider diversity.&lt;br&gt;
But if you're doing it to "avoid downtime," you might just be swapping one type of risk for another.&lt;/p&gt;

&lt;p&gt;In the Azure Well-Architected Framework, this is where &lt;strong&gt;trade-offs between pillars&lt;/strong&gt; come into play:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More &lt;strong&gt;Resilience&lt;/strong&gt; might mean less &lt;strong&gt;Cost Optimization&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Higher &lt;strong&gt;Security&lt;/strong&gt; can sometimes conflict with &lt;strong&gt;Performance Efficiency&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Greater &lt;strong&gt;Scalability&lt;/strong&gt; may reduce &lt;strong&gt;Operational Simplicity&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trick isn't to pick one - it's to be conscious of what you're giving up, and why.&lt;br&gt;
Because architecture isn't about perfection.&lt;br&gt;
It's about &lt;strong&gt;balance&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Lessons Learned&lt;a href="https://shpend-kelmendi.ch/2025/10/30/azure-front-door-incident#3-lessons-learned" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Here's what all of this boils down to, after years of watching clouds both shine and storm:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Everything fails eventually.&lt;/strong&gt;&lt;br&gt;
Design for failure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You own your architecture's reaction to chaos.&lt;/strong&gt;&lt;br&gt;
The provider gives you tools; you choose how to use them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity is the enemy of resilience.&lt;/strong&gt;&lt;br&gt;
Every extra moving part needs love, documentation, and monitoring.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Culture matters as much as code.&lt;/strong&gt;&lt;br&gt;
Teams that share responsibility, learn openly, and test failure often build systems that survive it.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  4. Closing Thoughts&lt;a href="https://shpend-kelmendi.ch/2025/10/30/azure-front-door-incident#4-closing-thoughts" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Cloud architecture isn't about chasing perfection.&lt;br&gt;
It's about designing systems - and teams - that can bend without breaking.&lt;/p&gt;

&lt;p&gt;Outages like the ones from AWS and Azure are humbling reminders that resilience isn't a checkbox.&lt;br&gt;
It's a mindset.&lt;br&gt;
It's what happens when you accept that things will go wrong - and prepare anyway.&lt;/p&gt;

&lt;p&gt;So next time the cloud sneezes, don't roll your eyes.&lt;br&gt;
Take notes.&lt;br&gt;
Because in those few chaotic hours, you'll find more lessons about resilience than in any documentation page.&lt;/p&gt;

&lt;p&gt;And if you’re curious to dig deeper into the Azure Front Door outage, &lt;a href="https://developer.microsoft.com/en-us/reactor/series/S-1605/" rel="noopener noreferrer"&gt;join the Azure Incident Retrospective — sign up here&lt;/a&gt;.&lt;br&gt;
It takes place today, &lt;strong&gt;October 30&lt;/strong&gt; (5:15 PM – 6:00 PM UTC+01:00) and &lt;strong&gt;October 31&lt;/strong&gt; (5:15 PM – 6:00 PM UTC+01:00).&lt;/p&gt;

&lt;p&gt;Remember: &lt;strong&gt;You can't avoid failure. But you can architect for recovery.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>resilience</category>
      <category>azure</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
