<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Entra ID Archives - Linuxcent</title>
	<atom:link href="https://linuxcent.com/tag/entra-id/feed/" rel="self" type="application/rss+xml" />
	<link>https://linuxcent.com/tag/entra-id/</link>
	<description>Infrastructure security, from the kernel up.</description>
	<lastBuildDate>Sat, 09 May 2026 18:38:59 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.1</generator>

<image>
	<url>https://linuxcent.com/wp-content/uploads/2026/04/favicon-512x512-1-150x150.png</url>
	<title>Entra ID Archives - Linuxcent</title>
	<link>https://linuxcent.com/tag/entra-id/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">211632295</site>	<item>
		<title>Entra ID Linux Login: SSH Authentication with Azure AD Credentials</title>
		<link>https://linuxcent.com/entra-id-linux-login/</link>
					<comments>https://linuxcent.com/entra-id-linux-login/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Sat, 09 May 2026 02:00:00 +0000</pubDate>
				<category><![CDATA[Identity & Authentication]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Azure AD]]></category>
		<category><![CDATA[Cloud Security]]></category>
		<category><![CDATA[Entra ID]]></category>
		<category><![CDATA[Identity Management]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[SSH]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1805</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span>Enable Entra ID SSH login on Linux: aad-auth package, Conditional Access Policies, pam_aad stack, and Entra ID Connect for hybrid on-prem sync.</p>
<p>The post <a href="https://linuxcent.com/entra-id-linux-login/">Entra ID Linux Login: SSH Authentication with Azure AD Credentials</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>The Identity Stack, Episode 12</em><br />
<a href="/identity-providers-explained/">EP11: Identity Providers</a> → <strong>EP12</strong> → <a href="/zero-trust-identity-spiffe-spire/">EP13: Zero Trust Identity</a> → &#8230;</p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Entra ID (Azure AD) Linux login lets you SSH into a VM using your Azure AD credentials — no local Linux accounts, no SSH keys to distribute</li>
<li>The stack: <code class="" data-line="">aad-auth</code> package + <code class="" data-line="">pam_aad.so</code> + SSSD — Azure authenticates via OIDC device code flow or password, then maps the identity to a local Linux UID</li>
<li>Entra ID is not AD — it&#8217;s OIDC/OAuth2 native, with no LDAP and no Kerberos (unless you add Azure AD DS, a separate managed service)</li>
<li>Conditional Access Policies can gate Linux logins — MFA, device compliance, location restrictions — the same policies as for web apps</li>
<li>Two login modes: interactive (browser-based device code, for non-Azure VMs) and integrated (Azure IMDS-based, for Azure VMs)</li>
<li>Required roles: <code class="" data-line="">Virtual Machine Administrator Login</code> or <code class="" data-line="">Virtual Machine User Login</code> on the VM — IAM, not local sudoers</li>
</ul>
<hr />
<h2 id="the-big-picture-how-entra-id-linux-login-works">The Big Picture: How Entra ID Linux Login Works</h2>
<pre><code class="" data-line="">User: ssh vamshi@vm.corp.azure.com

  sshd on Linux VM
      │
      ▼
  PAM (/etc/pam.d/sshd)
      │
      ├── pam_aad.so (auth)
      │     │
      │     │  OIDC device code flow:
      │     │  &quot;Go to microsoft.com/devicelogin and enter code ABCD-1234&quot;
      │     │  User authenticates in browser with MFA
      │     │  Entra ID issues id_token + access_token
      │     ▼
      │   pam_aad validates token:
      │     • signature (JWKS from Entra ID)
      │     • tenant ID (iss claim)
      │     • VM resource audience (aud claim)
      │     • group membership (groups claim)
      │
      └── pam_mkhomedir (session)
            Creates /home/vamshi@corp.com on first login

  Shell session created
  whoami → vamshi_corp_com (sanitized UPN for Linux username)
</code></pre>
<p>EP11 mapped the IdP landscape. This episode gets specific: Entra ID and Linux. Understanding this matters because Entra ID is increasingly where enterprise identities live, and cloud VMs that SSH into with local accounts are an operational and security liability.</p>
<hr />
<h2 id="entra-id-vs-active-directory-whats-different">Entra ID vs Active Directory: What&#8217;s Different</h2>
<p>This distinction matters before configuring anything.</p>
<table>
<thead>
<tr>
<th></th>
<th>Active Directory (on-prem)</th>
<th>Entra ID (cloud)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Protocol</td>
<td>LDAP + Kerberos</td>
<td>OIDC + OAuth2</td>
</tr>
<tr>
<td>Directory queries</td>
<td><code class="" data-line="">ldapsearch</code></td>
<td>Microsoft Graph API</td>
</tr>
<tr>
<td>Linux join</td>
<td><code class="" data-line="">realm join</code> (adcli + SSSD)</td>
<td><code class="" data-line="">aad-auth</code> package</td>
</tr>
<tr>
<td>Authentication</td>
<td>Kerberos tickets</td>
<td>JWT tokens</td>
</tr>
<tr>
<td>Group policy</td>
<td>GPO via Sysvol</td>
<td>Conditional Access + Intune</td>
</tr>
<tr>
<td>Network requirement</td>
<td>DC reachable on LAN/VPN</td>
<td>HTTPS to login.microsoftonline.com</td>
</tr>
</tbody>
</table>
<p>Entra ID has no LDAP interface and no Kerberos realm. You cannot run <code class="" data-line="">ldapsearch</code> against it. You cannot <code class="" data-line="">kinit</code> to it. The authentication protocol is entirely OIDC/OAuth2 — the same protocol your browser uses to &#8220;Login with Microsoft.&#8221;</p>
<p>If you need LDAP and Kerberos from Azure, that&#8217;s <strong>Azure AD Domain Services</strong> — a separate managed service that Microsoft runs, which does speak LDAP and Kerberos. It&#8217;s not Entra ID; it&#8217;s a managed AD replica in Azure. EP12 covers the Entra ID path — the modern, protocol-native approach.</p>
<hr />
<h2 id="prerequisites">Prerequisites</h2>
<pre><code class="" data-line=""># Azure side:
# 1. The VM&#039;s managed identity must be enabled (System-assigned)
# 2. Two Entra ID roles assigned on the VM resource:
#    - &quot;Virtual Machine Administrator Login&quot; (for sudo access)
#    - &quot;Virtual Machine User Login&quot; (for regular access)
# 3. Conditional Access policies that apply to the VM login scope

# VM side (Ubuntu 20.04+ / RHEL 8+):
# Install the aad-auth package (Microsoft-maintained)
curl -sSL https://packages.microsoft.com/keys/microsoft.asc \
  | gpg --dearmor -o /usr/share/keyrings/microsoft.gpg
echo &quot;deb [signed-by=/usr/share/keyrings/microsoft.gpg] \
  https://packages.microsoft.com/ubuntu/22.04/prod jammy main&quot; \
  &gt; /etc/apt/sources.list.d/microsoft.list
apt-get update &amp;&amp; apt-get install -y aad-auth
</code></pre>
<hr />
<h2 id="configuration">Configuration</h2>
<pre><code class="" data-line=""># Configure the aad-auth package
aad-auth configure \
  --tenant-id 12345678-1234-1234-1234-123456789abc \
  --app-id 87654321-4321-4321-4321-cba987654321

# This writes /etc/aad.conf:
# [aad]
# tenant_id = 12345678-...
# app_id = 87654321-...
# version = 1

# Verify the PAM configuration was updated
grep pam_aad /etc/pam.d/common-auth
# auth [success=1 default=ignore] pam_aad.so
</code></pre>
<p>The <code class="" data-line="">aad-auth</code> package installs <code class="" data-line="">pam_aad.so</code> and configures PAM automatically. It also modifies <code class="" data-line="">/etc/nsswitch.conf</code> to add <code class="" data-line="">aad</code> as a source for <code class="" data-line="">passwd</code> lookups — so <code class="" data-line="">getent passwd vamshi@corp.com</code> works after the first login.</p>
<hr />
<h2 id="the-login-flow">The Login Flow</h2>
<h3 id="on-an-azure-vm-integrated-mode">On an Azure VM (Integrated mode)</h3>
<p>Azure VMs have access to the Instance Metadata Service (IMDS) at <code class="" data-line="">169.254.169.254</code>. <code class="" data-line="">pam_aad</code> uses the VM&#8217;s managed identity to get a token from IMDS, which proves the VM is trusted, then validates the user&#8217;s token against the tenant.</p>
<pre><code class="" data-line=""># User SSHes with username as UPN (user@tenant.onmicrosoft.com or user@corp.com)
ssh vamshi@corp.com@vm.eastus.cloudapp.azure.com

# Or use the short form if the tenant is configured:
ssh vamshi@corp.com@vm.eastus.cloudapp.azure.com
</code></pre>
<p>On first connection, <code class="" data-line="">pam_aad</code> initiates the device code flow:</p>
<pre><code class="" data-line="">To sign in, use a web browser to open https://microsoft.com/devicelogin
and enter the code ABCD-1234 to authenticate.
</code></pre>
<p>The user opens the URL in any browser (on any device), enters the code, and authenticates with their Entra ID credentials + MFA. The SSH session gets a token. Subsequent logins within the token cache TTL skip the device code step.</p>
<h3 id="username-format-on-the-linux-system">Username format on the Linux system</h3>
<p>Entra ID usernames (UPNs) contain <code class="" data-line="">@</code> — not valid in Linux usernames. <code class="" data-line="">aad-auth</code> sanitizes the UPN:</p>
<pre><code class="" data-line="">vamshi@corp.com → vamshi_corp_com    (default)
# or, with shorter_username enabled in /etc/aad.conf:
vamshi@corp.com → vamshi
</code></pre>
<p>The UID is derived from the Azure AD Object ID (a deterministic hash) — stable across logins, same UID on every VM in the tenant.</p>
<hr />
<h2 id="conditional-access-for-linux-logins">Conditional Access for Linux Logins</h2>
<p>Conditional Access Policies in Entra ID apply to Linux VM logins the same way they apply to web app logins.</p>
<pre><code class="" data-line="">Policy: Require MFA for Linux VM Login
  Conditions:
    Cloud apps: &quot;Azure Linux Virtual Machine Sign-In&quot;
    Users: All users (or specific groups)
  Grant:
    Require multi-factor authentication
    Require compliant device (optional)
</code></pre>
<p>With this policy, every SSH login triggers MFA — regardless of whether the client machine supports it. The MFA challenge appears in the device code flow (the browser window the user opens).</p>
<p>You can also enforce:<br />
&#8211; <strong>Location restrictions</strong> — only from corporate IP ranges<br />
&#8211; <strong>Device compliance</strong> — device must be Intune-managed<br />
&#8211; <strong>Sign-in risk</strong> — block logins flagged as risky by Entra ID Identity Protection</p>
<p>This is the operational shift: Linux login security is now managed in the same Conditional Access policy engine as every other Entra ID-protected resource. No more per-machine PAM configuration for MFA.</p>
<hr />
<h2 id="role-based-access-who-can-log-in">Role-Based Access: Who Can Log In</h2>
<p>Access to the VM is controlled by Azure RBAC — not by local Linux groups or sudoers.</p>
<pre><code class="" data-line=""># Grant a user SSH access to the VM
az role assignment create \
  --assignee vamshi@corp.com \
  --role &quot;Virtual Machine User Login&quot; \
  --scope /subscriptions/SUB_ID/resourceGroups/RG/providers/Microsoft.Compute/virtualMachines/VM_NAME

# Grant admin (sudo) access
az role assignment create \
  --assignee vamshi@corp.com \
  --role &quot;Virtual Machine Administrator Login&quot; \
  --scope /subscriptions/SUB_ID/...
</code></pre>
<p><code class="" data-line="">Virtual Machine Administrator Login</code> maps to the <code class="" data-line="">sudo</code> group on the Linux VM. Users with this role get passwordless sudo. Users with <code class="" data-line="">Virtual Machine User Login</code> get a regular shell.</p>
<p>The mapping is enforced by <code class="" data-line="">pam_aad</code> checking the <code class="" data-line="">groups</code> claim in the token against the configured admin group. No <code class="" data-line="">/etc/sudoers.d/</code> files needed.</p>
<hr />
<h2 id="debugging-entra-id-linux-logins">Debugging Entra ID Linux Logins</h2>
<pre><code class="" data-line=""># Check aad-auth service status
systemctl status aad-auth

# View aad-auth logs
journalctl -u aad-auth -f

# Attempt a manual token validation (requires aad-auth debug mode)
aad-auth login --username vamshi@corp.com

# Check the local user cache
getent passwd vamshi_corp_com
# Returns if the user has logged in before

# Clear the local cache (forces re-authentication)
aad-auth clean-cache

# Verify Conditional Access isn&#039;t blocking (check Entra ID Sign-in logs)
# Azure Portal → Entra ID → Sign-in logs → filter by user + app &quot;Azure Linux VM Sign-In&quot;
</code></pre>
<p>The Entra ID Sign-in logs in the Azure Portal show every authentication attempt, the Conditional Access policies that evaluated, which ones passed/failed, and the exact failure reason. This is far more diagnostic than reading PAM logs.</p>
<hr />
<h2 id="entra-id-connect-bringing-on-prem-users-to-entra-id">Entra ID Connect: Bringing On-Prem Users to Entra ID</h2>
<p>For organizations with existing on-prem AD who want to enable Entra ID Linux login:</p>
<pre><code class="" data-line="">On-prem AD users → Entra ID Connect sync → Entra ID
                                                │
                                    Linux VM login (aad-auth)
</code></pre>
<p>Entra ID Connect is a Windows Server application that syncs users from on-prem AD to Entra ID every 30 minutes. Users authenticate against Entra ID (which validates against AD via Password Hash Sync, Pass-Through Authentication, or Federation). The Linux VM doesn&#8217;t know or care — it sees an Entra ID token.</p>
<p>With Password Hash Sync: password hashes (not plaintext) are synced to Entra ID — users authenticate directly in the cloud.<br />
With Pass-Through Authentication: Entra ID forwards authentication requests to an on-prem agent that validates against AD — no password hashes leave the datacenter.<br />
With Federation (AD FS / Entra ID as a relying party): Entra ID delegates authentication to AD FS — the most complex, the most on-prem control.</p>
<hr />
<h2 id="common-misconceptions"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Misconceptions</h2>
<p><strong>&#8220;Entra ID = Azure Active Directory = Active Directory.&#8221;</strong> Three different things. Active Directory: on-prem, LDAP+Kerberos. Azure AD (now Entra ID): cloud, OIDC+OAuth2. Azure AD Domain Services: managed AD replica in Azure, LDAP+Kerberos, not Entra ID.</p>
<p><strong>&#8220;You need Azure AD DS to join Linux to Azure.&#8221;</strong> Azure AD DS is the managed AD service. Entra ID Linux login (via aad-auth) is entirely separate and doesn&#8217;t require AD DS. You can authenticate Linux to Entra ID directly via OIDC.</p>
<p><strong>&#8220;The Linux username matches the Entra ID username.&#8221;</strong> The UPN is sanitized (<code class="" data-line="">@</code> → <code class="" data-line="">_</code>) to produce a valid Linux username. The canonical identity is the UPN or the Entra Object ID. Don&#8217;t hardcode the sanitized username in scripts.</p>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<table>
<thead>
<tr>
<th>Domain</th>
<th>Relevance</th>
</tr>
</thead>
<tbody>
<tr>
<td>CISSP Domain 5: Identity and Access Management</td>
<td>Entra ID Linux login centralizes Linux VM access in the same IAM system as all other enterprise resources — one policy engine, one audit log</td>
</tr>
<tr>
<td>CISSP Domain 3: Security Architecture and Engineering</td>
<td>Eliminating per-VM local accounts removes a class of credential management risk — no SSH keys to rotate, no local accounts to audit</td>
</tr>
<tr>
<td>CISSP Domain 1: Security and Risk Management</td>
<td>Conditional Access Policies enforcing MFA on Linux logins reduce the risk of credential-based compromise of cloud VMs</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>Entra ID Linux login uses OIDC device code flow — no LDAP, no Kerberos, no local Linux accounts</li>
<li><code class="" data-line="">aad-auth</code> package installs <code class="" data-line="">pam_aad.so</code> and handles the full authentication stack: token issuance, validation, user cache, UID mapping</li>
<li>VM access is controlled by Azure RBAC roles (<code class="" data-line="">Virtual Machine Administrator Login</code> / <code class="" data-line="">Virtual Machine User Login</code>) — not by sudoers files</li>
<li>Conditional Access Policies apply to Linux VM logins — MFA, device compliance, and location restrictions use the same engine as every other Entra ID app</li>
<li>Debugging starts in Entra ID Sign-in logs (Azure Portal), not in <code class="" data-line="">/var/log/auth.log</code></li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP12 showed how Entra ID enables Linux logins in the cloud. EP13 is the series closer: Zero Trust identity — what it means to verify identity continuously, how SPIFFE and SPIRE handle workload (non-human) identity, and where the stack goes from <code class="" data-line="">/etc/passwd</code> in 1970 to a Zero Trust policy engine in 2026.</p>
<p><em>Next: <a href="/zero-trust-identity-spiffe-spire/">Zero Trust Identity: SPIFFE, SPIRE, mTLS, and Continuous Verification</a></em></p>
<p>Get EP13 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&#038;title=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" data-a2a-url="https://linuxcent.com/entra-id-linux-login/" data-a2a-title="Entra ID Linux Login: SSH Authentication with Azure AD Credentials"></a></p><p>The post <a href="https://linuxcent.com/entra-id-linux-login/">Entra ID Linux Login: SSH Authentication with Azure AD Credentials</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/entra-id-linux-login/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1805</post-id>	</item>
		<item>
		<title>Identity Providers Explained: On-Prem, Cloud, SCIM, and Federation</title>
		<link>https://linuxcent.com/identity-providers-explained/</link>
					<comments>https://linuxcent.com/identity-providers-explained/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Fri, 08 May 2026 11:00:00 +0000</pubDate>
				<category><![CDATA[Identity & Authentication]]></category>
		<category><![CDATA[Entra ID]]></category>
		<category><![CDATA[IAM]]></category>
		<category><![CDATA[Identity Provider]]></category>
		<category><![CDATA[Okta]]></category>
		<category><![CDATA[SAML]]></category>
		<category><![CDATA[SCIM]]></category>
		<category><![CDATA[Security]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1802</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span>Identity providers explained: on-prem IdPs (AD FS, Keycloak), cloud IdPs (Okta, Entra ID), SCIM provisioning, SAML federation, and directory sync vs identity federation.</p>
<p>The post <a href="https://linuxcent.com/identity-providers-explained/">Identity Providers Explained: On-Prem, Cloud, SCIM, and Federation</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>The Identity Stack, Episode 11</em><br />
<a href="/saml-vs-oidc-vs-oauth2/">EP10: SAML/OIDC</a> → <strong>EP11</strong> → <a href="/entra-id-linux-login/">EP12: Entra ID + Linux</a> → &#8230;</p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>An Identity Provider (IdP) is the system that authenticates users and issues identity assertions (SAML assertions, OIDC tokens) to applications</li>
<li>On-prem IdPs: AD FS (Microsoft), Shibboleth (universities), Keycloak (open source), Ping Identity — they sit in front of AD and speak SAML/OIDC to cloud apps</li>
<li>Cloud IdPs: Okta, Entra ID (Azure AD), Google Workspace, Ping Identity Cloud — they are the directory and the authentication layer in one</li>
<li>Federation: IdPs can trust each other — a corporate IdP can delegate to a cloud IdP, or federate with a partner org&#8217;s IdP</li>
<li>SCIM (System for Cross-domain Identity Management) is provisioning, not authentication — it creates/updates/deactivates user accounts in target systems when the source directory changes</li>
<li>The key distinction: federation (authentication flow) vs directory sync (data copy) — they solve different problems and are often deployed together</li>
</ul>
<hr />
<h2 id="the-big-picture-where-idps-sit">The Big Picture: Where IdPs Sit</h2>
<pre><code class="" data-line="">                        On-prem Directory
                        (Active Directory / OpenLDAP / FreeIPA)
                               │
                               │ LDAP / Kerberos
                               ▼
                         Identity Provider
                         ┌──────────────────────────────────┐
                         │  AD FS / Keycloak / Okta /       │
                         │  Entra ID Connect / Shibboleth   │
                         │                                  │
                         │  Speaks: SAML 2.0 + OIDC + OAuth2│
                         └────────────────┬─────────────────┘
                                          │ assertions / tokens
                      ┌───────────────────┼───────────────────┐
                      ▼                   ▼                   ▼
               Salesforce          GitHub Enterprise      AWS IAM
               (SAML SP)           (OIDC RP)              (OIDC)
</code></pre>
<p>EP10 covered the protocols. This episode covers the systems — what an IdP actually does, how the major ones differ, and how they connect to each other through federation and SCIM.</p>
<hr />
<h2 id="on-premises-identity-providers">On-Premises Identity Providers</h2>
<h3 id="ad-fs-active-directory-federation-services">AD FS (Active Directory Federation Services)</h3>
<p>AD FS is Microsoft&#8217;s on-prem federation server — a Windows Server role that sits in front of Active Directory and speaks SAML 2.0 and OIDC to external applications.</p>
<p>What it does:<br />
&#8211; Authenticates users against AD (Kerberos/LDAP behind the scenes)<br />
&#8211; Issues SAML assertions and OIDC tokens to external SPs<br />
&#8211; Handles claims transformation: maps AD attributes to what the SP expects</p>
<p>What it doesn&#8217;t do well:<br />
&#8211; It&#8217;s Windows Server only<br />
&#8211; Configuration is complex (XML, certificates, claim rule language)<br />
&#8211; No built-in MFA (requires Azure MFA or a third-party provider)<br />
&#8211; Being deprecated in favor of Entra ID for most use cases</p>
<p>AD FS made sense when everything was on-prem. As workloads move to cloud, Entra ID Connect (a lighter sync agent) combined with Entra ID as the IdP replaces AD FS for most enterprises.</p>
<h3 id="keycloak">Keycloak</h3>
<p>Keycloak is the open-source IdP from Red Hat. It&#8217;s what FreeIPA uses for web-based OIDC/SAML SSO, and it&#8217;s widely deployed independently for organizations that want full control over their identity infrastructure.</p>
<pre><code class="" data-line=""># Run Keycloak in development mode (Docker)
docker run -p 8080:8080 \
  -e KEYCLOAK_ADMIN=admin \
  -e KEYCLOAK_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:latest \
  start-dev

# Keycloak concepts:
# Realm     — an isolated namespace (like a tenant)
# Client    — an application that uses Keycloak for auth (SP/RP)
# User federation — connect Keycloak to an existing LDAP/AD directory
# Identity brokering — federate with external IdPs (Google, GitHub, another SAML IdP)
</code></pre>
<p>Keycloak reads users from AD/LDAP via its User Federation feature — it doesn&#8217;t replace the directory, it federates it. Users still live in AD; Keycloak issues SAML/OIDC tokens based on those users.</p>
<h3 id="shibboleth">Shibboleth</h3>
<p>Shibboleth is the dominant IdP in academia. Most universities run it. It&#8217;s SAML-native, designed for federation between institutions — a student can authenticate at their home university&#8217;s IdP and access resources at a partner institution.</p>
<hr />
<h2 id="cloud-identity-providers">Cloud Identity Providers</h2>
<h3 id="okta">Okta</h3>
<p>Okta is a cloud IdP + directory. It can:<br />
&#8211; Act as the primary user directory (storing users, credentials)<br />
&#8211; Connect to on-prem AD via the Okta Active Directory Agent (a lightweight sync service)<br />
&#8211; Federate with other IdPs (act as IdP or SP in a SAML/OIDC chain)<br />
&#8211; Enforce MFA, Adaptive Authentication, Device Trust</p>
<p>Okta&#8217;s Lifecycle Management handles provisioning: when a user is created/disabled in Okta (or synced from AD), Okta can automatically create/deactivate accounts in downstream SaaS apps — via SCIM or app-specific APIs.</p>
<h3 id="entra-id-azure-active-directory">Entra ID (Azure Active Directory)</h3>
<p>Entra ID is Microsoft&#8217;s cloud IdP. It&#8217;s both a directory (stores users, groups) and an IdP (issues tokens). For organizations running on-prem AD, Entra ID Connect syncs users from AD to Entra ID.</p>
<p>Entra ID is OIDC and OAuth2 native — it speaks SAML for legacy apps but JWT/OIDC for everything modern. Its OIDC implementation follows the standard closely; its token validation happens via <code class="" data-line="">/.well-known/openid-configuration</code> and the JWKS endpoint.</p>
<pre><code class="" data-line="">On-prem AD  →  Entra ID Connect (sync agent)  →  Entra ID (cloud)
                                                      │
                                              SAML / OIDC
                                                      │
                                            SaaS apps, Azure resources
</code></pre>
<h3 id="google-workspace">Google Workspace</h3>
<p>Google Workspace is Google&#8217;s combined directory + IdP. Google accounts are the users. Apps integrate via SAML or OIDC. Google&#8217;s OIDC implementation is one of the most widely used reference implementations — most OIDC libraries are tested against it.</p>
<hr />
<h2 id="federation-idps-trusting-each-other">Federation: IdPs Trusting Each Other</h2>
<p>Federation is the mechanism that lets IdPs delegate to each other. Two patterns:</p>
<h3 id="saml-federation-idp-to-idp">SAML Federation (IdP-to-IdP)</h3>
<p>Common in academia and partner integrations:</p>
<pre><code class="" data-line="">User at University A → requests resource at University B
                              │
                              │ doesn&#039;t know user
                              ▼
                    University B SP redirects to...
                    Discovery Service: &quot;which IdP are you from?&quot;
                              │
                              ▼
                    University A IdP authenticates user
                              │
                    Sends SAML assertion to University B SP
</code></pre>
<p>University B&#8217;s SP trusts University A&#8217;s IdP because both are members of a SAML federation (e.g., InCommon in the US, eduGAIN globally). The federation metadata aggregates all members&#8217; SAML metadata — certificates, endpoints — so members don&#8217;t have to manually configure each bilateral trust.</p>
<h3 id="oidc-identity-brokering">OIDC Identity Brokering</h3>
<p>Keycloak, Okta, and Entra ID can all act as identity brokers — they sit between the application and the actual authenticating IdP:</p>
<pre><code class="" data-line="">App (OIDC RP) → Keycloak (broker IdP) → Google / GitHub / SAML IdP
                                               │ authenticate
                                               ▼
                                      Keycloak receives assertion
                                      Maps external claims to local claims
                                      Issues OIDC token to app
</code></pre>
<p>The app only knows Keycloak. Keycloak handles the upstream IdP complexity.</p>
<hr />
<h2 id="scim-provisioning-authentication">SCIM: Provisioning ≠ Authentication</h2>
<p>SCIM (RFC 7644) is a REST API standard for user lifecycle management — creating, updating, and deactivating user accounts in a target system when changes happen in the source directory.</p>
<pre><code class="" data-line="">Source (Okta / Entra ID)           Target (Slack / GitHub / Jira)
         │                                    │
         │  SCIM 2.0 (REST + JSON)            │
         ├─ POST /Users  ─────────────────────► create user
         ├─ PATCH /Users/id ──────────────────► update attributes
         └─ DELETE /Users/id ─────────────────► deactivate account
</code></pre>
<p>SCIM is not SSO. A SCIM-provisioned user in Slack can log in to Slack — but the authentication still goes through the IdP (SAML/OIDC). SCIM ensures the account exists. The IdP proves the user&#8217;s identity.</p>
<p>Why both? Because SSO alone doesn&#8217;t create accounts in target systems — it just authenticates to them. If a user tries to log in to Slack for the first time via SSO, Slack needs an account to map them to. SCIM creates that account before the first login (Just-in-Time provisioning handles it at first login, but SCIM handles it in bulk and handles deprovisioning reliably).</p>
<p>Deprovisioning is where SCIM matters most. When an employee leaves, you disable them in Okta — SCIM deactivates their account in every connected app within minutes. Without SCIM, IT runs a manual checklist. Someone misses Jira. The ex-employee has access for three weeks.</p>
<hr />
<h2 id="directory-sync-vs-federation">Directory Sync vs Federation</h2>
<p>These are commonly confused:</p>
<p><strong>Directory sync</strong> — copy user data from source to target. Entra ID Connect copies users from on-prem AD to Entra ID. This is not authentication; it&#8217;s data replication. After sync, Entra ID has its own copy of the user record.</p>
<p><strong>Federation</strong> — delegate authentication to an external IdP. The target system doesn&#8217;t store credentials; it redirects to the IdP for authentication and trusts the assertion that comes back.</p>
<p>You often need both:<br />
&#8211; Sync: so the target system has the user record and can enforce policies (group membership, license assignment)<br />
&#8211; Federation: so the user authenticates against the source of truth (your IdP) rather than maintaining a separate password in every system</p>
<hr />
<h2 id="common-misconceptions"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Misconceptions</h2>
<p><strong>&#8220;SCIM is an authentication protocol.&#8221;</strong> SCIM is a provisioning protocol. It creates and manages accounts. Authentication is SAML/OIDC. Both solve different parts of the identity lifecycle problem.</p>
<p><strong>&#8220;SSO means you only have one password.&#8221;</strong> SSO means you only authenticate once per session. The password still exists (at the IdP). SSO reduces the number of authentication events, not the number of credentials.</p>
<p><strong>&#8220;On-prem IdP + cloud sync is the same as a cloud IdP.&#8221;</strong> With on-prem IdP + cloud sync (e.g., AD + Entra ID Connect), authentication happens via the on-prem IdP — if it goes down, cloud SSO breaks. A pure cloud IdP (Okta standalone, Entra ID without on-prem AD) authenticates entirely in the cloud.</p>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<table>
<thead>
<tr>
<th>Domain</th>
<th>Relevance</th>
</tr>
</thead>
<tbody>
<tr>
<td>CISSP Domain 5: Identity and Access Management</td>
<td>IdPs are the central control plane for federated identity — their architecture, trust relationships, and provisioning workflows define the enterprise IAM posture</td>
</tr>
<tr>
<td>CISSP Domain 1: Security and Risk Management</td>
<td>SCIM-based deprovisioning is an access control risk management practice — without it, terminated employee access persists across connected systems</td>
</tr>
<tr>
<td>CISSP Domain 3: Security Architecture and Engineering</td>
<td>The choice of on-prem vs cloud IdP, federation vs sync, and SCIM vs JIT provisioning are architectural decisions with long-term operational and security implications</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>An IdP authenticates users and issues assertions (SAML) or tokens (OIDC/OAuth2) — applications trust the IdP, not the user directly</li>
<li>On-prem: AD FS (Windows/legacy), Keycloak (open source, flexible), Shibboleth (academia)</li>
<li>Cloud: Okta (cloud-native, strong lifecycle management), Entra ID (Microsoft-integrated), Google Workspace</li>
<li>Federation = authentication delegation between IdPs; Directory sync = data replication; SCIM = account lifecycle (provisioning/deprovisioning)</li>
<li>SCIM deprovisioning is the critical control — it ensures ex-employees lose access automatically across all connected systems</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP11 covered the IdP landscape. EP12 gets specific: Entra ID and Linux — how you configure a Linux VM to accept SSH logins authenticated against Azure AD credentials, and how the <code class="" data-line="">aad-auth</code> / <code class="" data-line="">pam_aad</code> stack works end to end.</p>
<p><em>Next: <a href="/entra-id-linux-login/">Entra ID Linux Login: SSH Authentication with Azure AD Credentials</a></em></p>
<p>Get EP12 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&#038;title=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" data-a2a-url="https://linuxcent.com/identity-providers-explained/" data-a2a-title="Identity Providers Explained: On-Prem, Cloud, SCIM, and Federation"></a></p><p>The post <a href="https://linuxcent.com/identity-providers-explained/">Identity Providers Explained: On-Prem, Cloud, SCIM, and Federation</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/identity-providers-explained/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1802</post-id>	</item>
		<item>
		<title>Azure RBAC Explained: Management Groups, Subscriptions, and Scope</title>
		<link>https://linuxcent.com/azure-rbac-entra-id-guide/</link>
					<comments>https://linuxcent.com/azure-rbac-entra-id-guide/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Thu, 16 Apr 2026 17:44:58 +0000</pubDate>
				<category><![CDATA[Cloud IAM]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Azure Active Directory]]></category>
		<category><![CDATA[Azure RBAC]]></category>
		<category><![CDATA[Cloud Security]]></category>
		<category><![CDATA[Entra ID]]></category>
		<category><![CDATA[IAM]]></category>
		<category><![CDATA[Managed Identity]]></category>
		<category><![CDATA[PIM]]></category>
		<guid isPermaLink="false">https://linuxcent.com/azure-rbac-entra-id-guide/</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 11</span> <span class="rt-label rt-postfix">minutes</span></span>Azure RBAC and Entra ID deep dive: role definitions, assignments, managed identities, Privileged Identity Management, and federated credentials for workloads.</p>
<p>The post <a href="https://linuxcent.com/azure-rbac-entra-id-guide/">Azure RBAC Explained: Management Groups, Subscriptions, and Scope</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 11</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><a href="/what-is-cloud-iam/">What Is Cloud IAM</a> → <a href="/authentication-vs-authorization-iam/">Authentication vs Authorization</a> → <a href="/iam-roles-policies-permissions-explained/">IAM Roles vs Policies</a> → <a href="/aws-iam-deep-dive/">AWS IAM Deep Dive</a> → <a href="/gcp-iam-deep-dive/">GCP Resource Hierarchy IAM</a> → <strong>Azure RBAC Scopes</strong></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Entra ID and Azure RBAC are two separate authorization planes — Entra ID roles control the identity system; RBAC roles control Azure resources. Global Administrator doesn&#8217;t grant VM access.</li>
<li>Azure RBAC role assignments inherit downward through the hierarchy: Management Group → Subscription → Resource Group → Resource</li>
<li>Use managed identities for all Azure-hosted workloads — system-assigned for one-to-one resource binding, user-assigned for shared access across multiple resources</li>
<li><code class="" data-line="">Contributor</code> is the right role for most service identities — full resource management without the ability to modify RBAC assignments</li>
<li>The <code class="" data-line="">Actions</code> vs <code class="" data-line="">DataActions</code> split means you can audit management access and data access independently — an incomplete audit checks only one</li>
<li>PIM (Privileged Identity Management) should govern all Entra ID privileged roles — nobody should permanently hold Global Admin or Subscription Owner</li>
</ul>
<hr />
<h2 id="the-big-picture">The Big Picture</h2>
<pre><code class="" data-line="">         Azure: Two Separate Authorization Planes
─────────────────────────────────────────────────────────
  Entra ID (Identity Plane)      Azure RBAC (Resource Plane)
  ─────────────────────────      ───────────────────────────
  Controls:                      Controls:
  · Users, groups, apps          · Azure resources
  · Tenant settings              · Management groups
  · App registrations            · Subscriptions
  · Conditional access           · Resource groups
                                 · Individual resources

  Roles (examples):              Scope hierarchy:
  · Global Administrator         Management Group
  · User Administrator             └─ Subscription
  · Security Reader                     └─ Resource Group
  · Application Administrator                └─ Resource

  Scope: tenant-wide             Role assignment at any level
                                 inherits down to all nodes below

  Both planes use Entra ID identities.
  Authorization in each plane is completely independent.
  Global Admin ≠ Subscription Owner.
</code></pre>
<p>Azure RBAC scopes determine how far a role assignment reaches — and the blast radius of a misconfiguration scales directly with how high in the hierarchy it sits.</p>
<hr />
<h2 id="introduction">Introduction</h2>
<p>Azure RBAC scopes define where a role assignment applies and everything it inherits. A role at the Management Group level touches every subscription, every resource group, and every resource across your entire Azure estate. A role at the resource level touches only that resource. Understanding scope before making any assignment is the difference between &#8220;access for this storage account&#8221; and &#8220;access for your entire org.&#8221;</p>
<p>When I first worked seriously in Azure environments, I had a mental model carried over from Active Directory administration. Users, groups, directory roles — I knew how that worked. I assumed Azure&#8217;s IAM would be an extension of the same system, just with cloud resources bolted on.</p>
<p>That assumption got me into trouble within the first week.</p>
<p>I was trying to understand why an engineer had Global Administrator access in Entra ID but couldn&#8217;t see the resources in a Subscription. In Active Directory terms, if you&#8217;re a Domain Admin, you can see everything. In Azure, it doesn&#8217;t work that way.</p>
<p>Entra ID roles and Azure RBAC roles are <strong>two different systems</strong>. Global Administrator is an Entra ID role — it controls who can manage the identity plane: create users, manage app registrations, configure tenant settings. It has nothing to do with Azure resources like virtual machines, storage accounts, or Kubernetes clusters. Those are governed by Azure RBAC, which is an entirely separate authorization system.</p>
<p>I spent two hours trying to understand why a Global Admin couldn&#8217;t list VMs before someone explained this. I&#8217;m putting it at the top of this episode so you don&#8217;t lose those two hours.</p>
<hr />
<h2 id="entra-id-vs-azure-rbac-the-two-separate-planes">Entra ID vs Azure RBAC — The Two Separate Planes</h2>
<table>
<thead>
<tr>
<th></th>
<th>Entra ID</th>
<th>Azure RBAC</th>
</tr>
</thead>
<tbody>
<tr>
<td>Controls access to</td>
<td>Entra ID itself — users, groups, apps, tenant settings</td>
<td>Azure resources — VMs, storage, databases, subscriptions</td>
</tr>
<tr>
<td>Role types</td>
<td>Entra ID directory roles</td>
<td>Azure resource roles</td>
</tr>
<tr>
<td>Example roles</td>
<td>Global Admin, User Admin, Security Reader</td>
<td>Owner, Contributor, Storage Blob Data Reader</td>
</tr>
<tr>
<td>Scope</td>
<td>Tenant-wide</td>
<td>Management group → Subscription → Resource Group → Resource</td>
</tr>
<tr>
<td>Managed via</td>
<td>Entra ID admin center</td>
<td>Azure portal / ARM / Azure CLI</td>
</tr>
</tbody>
</table>
<p>A user can be Global Administrator — the highest Entra ID role — and have zero access to Azure resources unless explicitly assigned an Azure RBAC role. And vice versa: a user with Subscription Owner (highest Azure RBAC role) has no ability to manage Entra ID user accounts without an Entra ID role assignment.</p>
<p>These are not the same system. They&#8217;re connected — both use Entra ID identities as principals — but authorization in each plane is independent.</p>
<hr />
<h2 id="the-azure-resource-hierarchy">The Azure Resource Hierarchy</h2>
<p>Azure RBAC role assignments can be made at any level of the resource hierarchy, and they inherit downward:</p>
<pre><code class="" data-line="">Tenant (Entra ID)
  └── Management Group  (policy and RBAC inheritance across subscriptions)
        └── Management Group  (nested, up to 6 levels)
              └── Subscription  (billing and resource boundary)
                    └── Resource Group  (logical container for resources)
                          └── Resource  (VM, storage account, key vault, AKS cluster...)
</code></pre>
<p>A role assigned at the Subscription level applies to every resource group and resource in that subscription. A role at the Management Group level applies to every subscription beneath it.</p>
<p>The blast radius of a misconfiguration scales with how high in the hierarchy it sits. Subscription Owner at the subscription level is contained to that subscription. Management Group Contributor at the root management group touches your entire Azure estate.</p>
<pre><code class="" data-line=""># View management group hierarchy
az account management-group list --output table

# List subscriptions
az account list --output table

# View all role assignments at a scope — start here in any audit
az role assignment list \
  --scope /subscriptions/SUB_ID \
  --include-inherited \
  --output table
</code></pre>
<hr />
<h2 id="principal-types-in-azure-rbac">Principal Types in Azure RBAC</h2>
<table>
<thead>
<tr>
<th>Type</th>
<th>What It Is</th>
<th>Best For</th>
</tr>
</thead>
<tbody>
<tr>
<td>User</td>
<td>Entra ID user account</td>
<td>Human access</td>
</tr>
<tr>
<td>Group</td>
<td>Entra ID security group</td>
<td>Team-based access</td>
</tr>
<tr>
<td>Service Principal</td>
<td>App registration with credentials (secret or cert)</td>
<td>External systems, apps with their own identity</td>
</tr>
<tr>
<td>Managed Identity</td>
<td>Credential-less identity for Azure-hosted workloads</td>
<td>Everything running in Azure</td>
</tr>
</tbody>
</table>
<h3 id="managed-identities-the-right-model-for-workloads">Managed Identities — The Right Model for Workloads</h3>
<p>Managed identities are Azure&#8217;s answer to AWS instance profiles and GCP service accounts attached to compute. Azure manages the entire credential lifecycle — tokens are issued automatically, there&#8217;s nothing to create, rotate, or revoke manually.</p>
<p><strong>System-assigned managed identity</strong> is tied to a specific Azure resource. When the resource is deleted, the identity is deleted. One-to-one, no sharing.</p>
<pre><code class="" data-line=""># Enable system-assigned managed identity on a VM
az vm identity assign \
  --name my-vm \
  --resource-group rg-prod

# Get the principal ID (needed to assign RBAC roles to it)
az vm show \
  --name my-vm \
  --resource-group rg-prod \
  --query identity.principalId \
  --output tsv
</code></pre>
<p><strong>User-assigned managed identity</strong> is a standalone resource that can be attached to multiple Azure resources and persists independently. This is the right model when multiple services need the same access — instead of assigning the same RBAC roles to ten separate system-assigned identities, you create one user-assigned identity, grant it the roles, and attach it to all ten resources.</p>
<pre><code class="" data-line=""># Create a user-assigned managed identity
az identity create \
  --name app-backend-identity \
  --resource-group rg-identities

# Get its identifiers
az identity show \
  --name app-backend-identity \
  --resource-group rg-identities \
  --query &#039;{principalId:principalId, clientId:clientId}&#039;

# Attach to a VM
az vm identity assign \
  --name my-vm \
  --resource-group rg-prod \
  --identities /subscriptions/SUB/resourceGroups/rg-identities/providers/Microsoft.ManagedIdentity/userAssignedIdentities/app-backend-identity
</code></pre>
<p>Code running inside an Azure VM or App Service with a managed identity gets tokens via IMDS, with no credential management required:</p>
<pre><code class="" data-line="">from azure.identity import DefaultAzureCredential
from azure.storage.blob import BlobServiceClient

# DefaultAzureCredential automatically picks up the managed identity in Azure
credential = DefaultAzureCredential()
client = BlobServiceClient(
    account_url=&quot;https://myaccount.blob.core.windows.net&quot;,
    credential=credential
)
</code></pre>
<p>The <code class="" data-line="">DefaultAzureCredential</code> chain: managed identity → environment variables → workload identity → Visual Studio / VS Code authentication → Azure CLI. In Azure-hosted services, the managed identity path is used automatically. In local development, it falls through to the developer&#8217;s <code class="" data-line="">az login</code> session.</p>
<hr />
<h2 id="azure-role-definitions-understanding-actions-vs-dataactions">Azure Role Definitions — Understanding Actions vs DataActions</h2>
<p>A role definition specifies what actions it grants. Azure distinguishes two planes:</p>
<ul>
<li><strong>Actions:</strong> Control plane — managing the resource itself (create, delete, configure)</li>
<li><strong>DataActions:</strong> Data plane — accessing data within the resource (read blob contents, get secrets)</li>
<li><strong>NotActions / NotDataActions:</strong> Exceptions carved out from the grant</li>
</ul>
<pre><code class="" data-line="">{
  &quot;Name&quot;: &quot;Storage Blob Data Reader&quot;,
  &quot;IsCustom&quot;: false,
  &quot;Actions&quot;: [
    &quot;Microsoft.Storage/storageAccounts/blobServices/containers/read&quot;,
    &quot;Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKey/action&quot;
  ],
  &quot;NotActions&quot;: [],
  &quot;DataActions&quot;: [
    &quot;Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read&quot;
  ],
  &quot;NotDataActions&quot;: [],
  &quot;AssignableScopes&quot;: [&quot;/&quot;]
}
</code></pre>
<p>The control/data plane split matters in audits. An identity with <code class="" data-line="">Microsoft.Storage/storageAccounts/read</code> (an Action) can see the storage account exists and view its properties. To actually read blob contents, it needs the DataAction <code class="" data-line="">Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read</code>. These are separate grants. In an access audit, checking only Actions and missing DataActions is an incomplete picture.</p>
<h3 id="built-in-roles-worth-understanding">Built-in Roles Worth Understanding</h3>
<table>
<thead>
<tr>
<th>Role</th>
<th>Scope</th>
<th>What It Grants</th>
</tr>
</thead>
<tbody>
<tr>
<td>Owner</td>
<td>Any</td>
<td>Full access + can manage RBAC assignments — the highest trust role</td>
</tr>
<tr>
<td>Contributor</td>
<td>Any</td>
<td>Full resource management, but cannot manage RBAC</td>
</tr>
<tr>
<td>Reader</td>
<td>Any</td>
<td>Read-only on all resources</td>
</tr>
<tr>
<td>User Access Administrator</td>
<td>Any</td>
<td>Can manage RBAC assignments, no resource access</td>
</tr>
<tr>
<td>Storage Blob Data Contributor</td>
<td>Storage</td>
<td>Read/write/delete blob data</td>
</tr>
<tr>
<td>Storage Blob Data Reader</td>
<td>Storage</td>
<td>Read blob data only</td>
</tr>
<tr>
<td>Key Vault Secrets Officer</td>
<td>Key Vault</td>
<td>Manage secrets, not keys or certificates</td>
</tr>
<tr>
<td>AcrPush / AcrPull</td>
<td>Container Registry</td>
<td>Push or pull images</td>
</tr>
</tbody>
</table>
<p>The gap between <code class="" data-line="">Owner</code> and <code class="" data-line="">Contributor</code> is important: <code class="" data-line="">Contributor</code> can do everything to a resource except manage who has access to it. This is the right role for most service identities and automation — they need to manage resources, not manage permissions. If a compromised Contributor identity can&#8217;t modify RBAC assignments, it can&#8217;t grant itself or an attacker additional access.</p>
<p><code class="" data-line="">Owner</code> should be granted to people, not service identities, and only at the narrowest scope necessary.</p>
<h3 id="custom-roles">Custom Roles</h3>
<pre><code class="" data-line="">cat &gt; custom-app-storage.json &lt;&lt; &#039;EOF&#039;
{
  &quot;Name&quot;: &quot;App Storage Blob Reader&quot;,
  &quot;IsCustom&quot;: true,
  &quot;Description&quot;: &quot;Read app blobs only — no container management, no key operations&quot;,
  &quot;Actions&quot;: [
    &quot;Microsoft.Storage/storageAccounts/blobServices/containers/read&quot;
  ],
  &quot;DataActions&quot;: [
    &quot;Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read&quot;
  ],
  &quot;NotActions&quot;: [],
  &quot;NotDataActions&quot;: [],
  &quot;AssignableScopes&quot;: [&quot;/subscriptions/SUB_ID&quot;]
}
EOF

az role definition create --role-definition custom-app-storage.json

# Assign it — specifically to this storage account
az role assignment create \
  --assignee-object-id &quot;$(az identity show --name app-backend-identity -g rg-identities --query principalId -o tsv)&quot; \
  --assignee-principal-type ServicePrincipal \
  --role &quot;App Storage Blob Reader&quot; \
  --scope /subscriptions/SUB_ID/resourceGroups/rg-prod/providers/Microsoft.Storage/storageAccounts/appstore
</code></pre>
<hr />
<h2 id="role-assignments-where-access-is-actually-granted">Role Assignments — Where Access Is Actually Granted</h2>
<p>The assignment brings everything together: principal + role + scope. This is the actual grant.</p>
<pre><code class="" data-line=""># Assign to a user (less common — prefer group assignments)
az role assignment create \
  --assignee alice@company.com \
  --role &quot;Storage Blob Data Reader&quot; \
  --scope /subscriptions/SUB_ID/resourceGroups/rg-prod/providers/Microsoft.Storage/storageAccounts/prodstore

# Assign to a group (better — one assignment, maintained via group membership)
GROUP_ID=$(az ad group show --group &quot;Backend-Team&quot; --query id -o tsv)
az role assignment create \
  --assignee-object-id &quot;$GROUP_ID&quot; \
  --assignee-principal-type Group \
  --role &quot;Contributor&quot; \
  --scope /subscriptions/SUB_ID/resourceGroups/rg-dev

# Assign to a managed identity
MI_PRINCIPAL=$(az identity show --name app-backend-identity --resource-group rg-identities --query principalId -o tsv)
az role assignment create \
  --assignee-object-id &quot;$MI_PRINCIPAL&quot; \
  --assignee-principal-type ServicePrincipal \
  --role &quot;Storage Blob Data Contributor&quot; \
  --scope /subscriptions/SUB_ID/resourceGroups/rg-prod/providers/Microsoft.Storage/storageAccounts/appstore

# Audit all assignments at and below a scope (including inherited)
az role assignment list \
  --scope /subscriptions/SUB_ID/resourceGroups/rg-prod \
  --include-inherited \
  --output table
</code></pre>
<p>Group-based assignments are the right model for humans at scale. When an engineer joins the Backend team, they join the Entra ID group. Their access follows. When they leave, you remove them from the group or disable their account. You never need to hunt down individual role assignments.</p>
<hr />
<h2 id="entra-id-roles-the-other-layer">Entra ID Roles — The Other Layer</h2>
<p>Entra ID roles control the identity infrastructure itself. These are distinct from Azure RBAC roles and deserve separate treatment:</p>
<table>
<thead>
<tr>
<th>Role</th>
<th>What It Controls</th>
</tr>
</thead>
<tbody>
<tr>
<td>Global Administrator</td>
<td>Everything in the tenant — highest privilege</td>
</tr>
<tr>
<td>Privileged Role Administrator</td>
<td>Assign and remove Entra ID roles</td>
</tr>
<tr>
<td>User Administrator</td>
<td>Create and manage users and groups</td>
</tr>
<tr>
<td>Application Administrator</td>
<td>Register and manage app registrations</td>
</tr>
<tr>
<td>Security Administrator</td>
<td>Manage security features and read reports</td>
</tr>
<tr>
<td>Security Reader</td>
<td>Read-only on security features</td>
</tr>
</tbody>
</table>
<p>Global Administrator in Entra ID is one of the most powerful identities in a Microsoft environment. It can modify any user, any app registration, any conditional access policy. Combined with the fact that Entra ID is also the identity provider for Microsoft 365, a Global Admin compromise can extend far beyond Azure resources into email, Teams, SharePoint — the entire Microsoft 365 estate.</p>
<p>Nobody should hold Global Administrator as a permanent assignment. This is where Privileged Identity Management (PIM) matters.</p>
<h3 id="privileged-identity-management-just-in-time-elevated-access">Privileged Identity Management — Just-in-Time Elevated Access</h3>
<p>PIM is Azure&#8217;s answer to the problem of permanent privileged role assignments. Instead of permanently holding Global Admin or Subscription Owner, users are made <em>eligible</em> for these roles. When they need elevated access, they activate it with a justification (and optionally an approval and MFA requirement). The access is time-limited — typically 8 hours — and automatically expires.</p>
<pre><code class="" data-line=""># List roles where the user is eligible (not permanently assigned)
az rest --method GET \
  --uri &quot;https://graph.microsoft.com/v1.0/roleManagement/directory/roleEligibilitySchedules&quot; \
  --query &quot;value[?principalId==&#039;USER_OBJECT_ID&#039;]&quot;

# A user activates an eligible role (calls this themselves when needed)
az rest --method POST \
  --uri &quot;https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignmentScheduleRequests&quot; \
  --body &#039;{
    &quot;action&quot;: &quot;selfActivate&quot;,
    &quot;principalId&quot;: &quot;USER_OBJECT_ID&quot;,
    &quot;roleDefinitionId&quot;: &quot;ROLE_DEF_ID&quot;,
    &quot;directoryScopeId&quot;: &quot;/&quot;,
    &quot;justification&quot;: &quot;Investigating security alert in tenant audit logs&quot;,
    &quot;scheduleInfo&quot;: {
      &quot;startDateTime&quot;: &quot;2026-04-16T00:00:00Z&quot;,
      &quot;expiration&quot;: { &quot;type&quot;: &quot;AfterDuration&quot;, &quot;duration&quot;: &quot;PT8H&quot; }
    }
  }&#039;
</code></pre>
<p>PIM is the right model for any role that could be used to escalate privileges: Global Administrator, Subscription Owner, Privileged Role Administrator, User Access Administrator. Nobody should have these permanently assigned unless there&#8217;s a strong operational reason — and even then, the assignment should be reviewed quarterly.</p>
<p>In one Azure environment I audited, I found 11 permanent Global Administrator assignments. The team thought this was normal because they&#8217;d all been made admins when the tenant was set up two years earlier and nobody had revisited it. Of the 11, three were former employees whose Entra ID accounts had been disabled — but the Global Admin role assignment was still there. Disabled users can&#8217;t use their accounts, but this is not a pattern you want to rely on.</p>
<hr />
<h2 id="federated-identity-for-external-workloads">Federated Identity for External Workloads</h2>
<p>For GitHub Actions, Kubernetes workloads, and other external systems that need to call Azure APIs, federated credentials eliminate service principal secrets:</p>
<pre><code class="" data-line=""># Create an app registration
APP_ID=$(az ad app create --display-name &quot;github-actions-deploy&quot; --query appId -o tsv)
SP_ID=$(az ad sp create --id &quot;$APP_ID&quot; --query id -o tsv)

# Add a federated credential for a specific GitHub repo and branch
az ad app federated-credential create \
  --id &quot;$APP_ID&quot; \
  --parameters &#039;{
    &quot;name&quot;: &quot;github-main-branch&quot;,
    &quot;issuer&quot;: &quot;https://token.actions.githubusercontent.com&quot;,
    &quot;subject&quot;: &quot;repo:my-org/my-repo:ref:refs/heads/main&quot;,
    &quot;audiences&quot;: [&quot;api://AzureADTokenExchange&quot;]
  }&#039;

# Grant the service principal an RBAC role
az role assignment create \
  --assignee-object-id &quot;$SP_ID&quot; \
  --role &quot;Contributor&quot; \
  --scope /subscriptions/SUB_ID/resourceGroups/rg-prod
</code></pre>
<p>GitHub Actions — no secrets stored in GitHub:</p>
<pre><code class="" data-line="">jobs:
  deploy:
    permissions:
      id-token: write   # required for OIDC token request
    steps:
      - uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - run: az storage blob upload --account-name prodstore ...
</code></pre>
<p>The <code class="" data-line="">client-id</code>, <code class="" data-line="">tenant-id</code>, and <code class="" data-line="">subscription-id</code> values are not secrets — they&#8217;re identifiers. The actual authentication is the OIDC JWT from GitHub, verified against GitHub&#8217;s public keys, subject-matched against the configured condition (<code class="" data-line="">repo:my-org/my-repo:ref:refs/heads/main</code>). If the repo or branch doesn&#8217;t match, the token exchange fails. If it matches, a short-lived Azure token is issued.</p>
<hr />
<h2 id="production-gotchas"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Production Gotchas</h2>
<p><strong>Global Admin ≠ Azure resource access</strong><br />
This trips up every team migrating from on-prem AD. Entra ID roles and Azure RBAC roles are independent systems. A Global Admin with no RBAC assignments cannot list VMs. Don&#8217;t assume directory privilege translates to resource access.</p>
<p><strong>Permanent Global Admin assignments are a standing breach risk</strong><br />
In the environment I audited: 11 permanent Global Admins, three of them disabled accounts. Disabled accounts can&#8217;t authenticate, but relying on that is not a security control. PIM eligible assignments + regular access reviews is the right answer.</p>
<p><strong><code class="" data-line="">Owner</code> on service identities lets compromised workloads modify RBAC</strong><br />
If a managed identity or service principal holds Owner, a compromised workload can grant additional permissions to itself or an attacker. Use <code class="" data-line="">Contributor</code> for workloads — full resource management, no RBAC modification.</p>
<p><strong>Checking only <code class="" data-line="">Actions</code> misses data-plane access</strong><br />
An audit that enumerates role <code class="" data-line="">Actions</code> and ignores <code class="" data-line="">DataActions</code> will miss identities with read access to blob contents, Key Vault secrets, or database records. Both planes need to be in scope.</p>
<p><strong>System-assigned identity is deleted with the resource</strong><br />
If you delete and recreate a VM using a system-assigned identity, the new identity is different. Any RBAC assignments made to the old identity are gone. User-assigned identities persist independently — use them for workloads where the resource lifecycle is separate from the identity lifecycle.</p>
<hr />
<h2 id="quick-reference">Quick Reference</h2>
<pre><code class="" data-line=""># Audit all role assignments at a subscription (including inherited)
az role assignment list \
  --scope /subscriptions/SUB_ID \
  --include-inherited \
  --output table

# Find all Owner assignments at subscription scope
az role assignment list \
  --scope /subscriptions/SUB_ID \
  --role Owner \
  --output table

# Get principal ID of a VM&#039;s managed identity
az vm show \
  --name my-vm \
  --resource-group rg-prod \
  --query identity.principalId \
  --output tsv

# View role definition — check Actions AND DataActions
az role definition list --name &quot;Storage Blob Data Reader&quot; --output json \
  | jq &#039;.[0] | {Actions: .permissions[0].actions, DataActions: .permissions[0].dataActions}&#039;

# List management group hierarchy
az account management-group list --output table

# Create user-assigned managed identity
az identity create --name app-identity --resource-group rg-identities

# Assign role to managed identity at resource scope
az role assignment create \
  --assignee-object-id &quot;$(az identity show -n app-identity -g rg-identities --query principalId -o tsv)&quot; \
  --assignee-principal-type ServicePrincipal \
  --role &quot;Storage Blob Data Contributor&quot; \
  --scope /subscriptions/SUB_ID/resourceGroups/rg-prod/providers/Microsoft.Storage/storageAccounts/mystore

# Check PIM eligible roles for a user
az rest --method GET \
  --uri &quot;https://graph.microsoft.com/v1.0/roleManagement/directory/roleEligibilitySchedules&quot; \
  --query &quot;value[?principalId==&#039;USER_OBJECT_ID&#039;].{role:roleDefinitionId,scope:directoryScopeId}&quot;
</code></pre>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<table>
<thead>
<tr>
<th>Framework</th>
<th>Reference</th>
<th>What It Covers Here</th>
</tr>
</thead>
<tbody>
<tr>
<td>CISSP</td>
<td>Domain 5 — Identity and Access Management</td>
<td>Azure&#8217;s directory-centric model; managed identities and PIM are the primary IAM constructs</td>
</tr>
<tr>
<td>CISSP</td>
<td>Domain 3 — Security Architecture</td>
<td>Entra ID spans Azure, M365, and third-party SaaS — scope boundaries determine the blast radius of a compromise</td>
</tr>
<tr>
<td>ISO 27001:2022</td>
<td>5.15 Access control</td>
<td>Azure RBAC role definitions and assignments implement access control policy</td>
</tr>
<tr>
<td>ISO 27001:2022</td>
<td>5.16 Identity management</td>
<td>Entra ID is the identity management platform — user lifecycle, group management, application registrations</td>
</tr>
<tr>
<td>ISO 27001:2022</td>
<td>8.2 Privileged access rights</td>
<td>PIM (Privileged Identity Management) directly implements JIT controls for privileged roles</td>
</tr>
<tr>
<td>ISO 27001:2022</td>
<td>5.18 Access rights</td>
<td>Role assignment scoping, managed identity provisioning, federated credential lifecycle</td>
</tr>
<tr>
<td>SOC 2</td>
<td>CC6.1</td>
<td>Managed identities and RBAC are the primary technical controls for CC6.1 in Azure-hosted environments</td>
</tr>
<tr>
<td>SOC 2</td>
<td>CC6.3</td>
<td>PIM activation expiry and access reviews directly satisfy time-bound access removal requirements</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>Entra ID and Azure RBAC are separate authorization planes — Entra ID roles control the identity system; RBAC roles control Azure resources. Global Administrator doesn&#8217;t grant VM access.</li>
<li>Use managed identities for all Azure-hosted workloads — system-assigned for one-to-one, user-assigned for shared identities across multiple resources</li>
<li><code class="" data-line="">Contributor</code> is the right role for most service identities — full resource management without RBAC modification ability</li>
<li>The control/data plane split (<code class="" data-line="">Actions</code> vs <code class="" data-line="">DataActions</code>) in role definitions means you can grant management access without data access or vice versa — use this</li>
<li>PIM should govern all Entra ID privileged roles and high-scope Azure roles — nobody should permanently hold Global Admin or Subscription Owner</li>
<li>Federated identity credentials replace service principal secrets for external workloads — no secrets stored in CI/CD systems</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP07 goes cross-cloud: workload identity federation — the shift away from static credentials entirely, with IRSA for EKS, GKE Workload Identity, AKS workload identity, and GitHub Actions-to-all-three-clouds patterns.</p>
<p><em>Next: <a href="/workload-identity-oidc-service-accounts/">OIDC Workload Identity</a> — Eliminate Cloud Access Keys Entirely.</em></p>
<p>Get EP07 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe/">subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fazure-rbac-entra-id-guide%2F&amp;linkname=Azure%20RBAC%20Explained%3A%20Management%20Groups%2C%20Subscriptions%2C%20and%20Scope" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fazure-rbac-entra-id-guide%2F&amp;linkname=Azure%20RBAC%20Explained%3A%20Management%20Groups%2C%20Subscriptions%2C%20and%20Scope" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fazure-rbac-entra-id-guide%2F&amp;linkname=Azure%20RBAC%20Explained%3A%20Management%20Groups%2C%20Subscriptions%2C%20and%20Scope" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fazure-rbac-entra-id-guide%2F&amp;linkname=Azure%20RBAC%20Explained%3A%20Management%20Groups%2C%20Subscriptions%2C%20and%20Scope" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fazure-rbac-entra-id-guide%2F&amp;linkname=Azure%20RBAC%20Explained%3A%20Management%20Groups%2C%20Subscriptions%2C%20and%20Scope" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fazure-rbac-entra-id-guide%2F&amp;linkname=Azure%20RBAC%20Explained%3A%20Management%20Groups%2C%20Subscriptions%2C%20and%20Scope" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fazure-rbac-entra-id-guide%2F&amp;linkname=Azure%20RBAC%20Explained%3A%20Management%20Groups%2C%20Subscriptions%2C%20and%20Scope" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fazure-rbac-entra-id-guide%2F&#038;title=Azure%20RBAC%20Explained%3A%20Management%20Groups%2C%20Subscriptions%2C%20and%20Scope" data-a2a-url="https://linuxcent.com/azure-rbac-entra-id-guide/" data-a2a-title="Azure RBAC Explained: Management Groups, Subscriptions, and Scope"></a></p><p>The post <a href="https://linuxcent.com/azure-rbac-entra-id-guide/">Azure RBAC Explained: Management Groups, Subscriptions, and Scope</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/azure-rbac-entra-id-guide/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1479</post-id>	</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: linuxcent.com @ 2026-07-13 04:17:24 by W3 Total Cache
-->