<?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>IAM Policies Archives - Linuxcent</title>
	<atom:link href="https://linuxcent.com/tag/iam-policies/feed/" rel="self" type="application/rss+xml" />
	<link>https://linuxcent.com/tag/iam-policies/</link>
	<description>Infrastructure security, from the kernel up.</description>
	<lastBuildDate>Sat, 09 May 2026 18:38:02 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.4</generator>

<image>
	<url>https://linuxcent.com/wp-content/uploads/2026/04/favicon-512x512-1-150x150.png</url>
	<title>IAM Policies Archives - Linuxcent</title>
	<link>https://linuxcent.com/tag/iam-policies/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">211632295</site>	<item>
		<title>IAM Roles vs Policies: How Cloud Authorization Actually Works</title>
		<link>https://linuxcent.com/iam-roles-policies-permissions-explained/</link>
					<comments>https://linuxcent.com/iam-roles-policies-permissions-explained/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Tue, 14 Apr 2026 05:05:54 +0000</pubDate>
				<category><![CDATA[Cloud IAM]]></category>
		<category><![CDATA[ABAC]]></category>
		<category><![CDATA[AWS IAM Policy]]></category>
		<category><![CDATA[Cloud Security]]></category>
		<category><![CDATA[IAM]]></category>
		<category><![CDATA[IAM Policies]]></category>
		<category><![CDATA[IAM Roles]]></category>
		<category><![CDATA[Least Privilege]]></category>
		<category><![CDATA[RBAC]]></category>
		<guid isPermaLink="false">https://linuxcent.com/iam-roles-policies-permissions-explained/</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"> 12</span> <span class="rt-label rt-postfix">minutes</span></span>A clear breakdown of IAM roles, policies, and permissions — the building blocks of cloud access control in AWS, GCP, and Azure with RBAC and ABAC patterns.</p>
<p>The post <a href="https://linuxcent.com/iam-roles-policies-permissions-explained/">IAM Roles vs Policies: How Cloud Authorization Actually Works</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"> 12</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>
<hr />
<p><a href="/what-is-cloud-iam/">What Is Cloud IAM</a> → <a href="/authentication-vs-authorization-iam/">Authentication vs Authorization</a> → <strong>IAM Roles vs Policies</strong> → <a href="/aws-iam-deep-dive/">AWS IAM Deep Dive</a> → <a href="/gcp-iam-deep-dive/">GCP Resource Hierarchy IAM</a> → <a href="/azure-rbac-entra-id-guide/">Azure RBAC Scopes</a></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Every cloud permission is atomic: one action (<code class="" data-line="">s3:GetObject</code>) on one resource class — the indivisible unit of access</li>
<li>Policies group permissions into documents with conditions; roles carry policies and are assigned to identities</li>
<li>Never attach policies directly to users — roles are the indirection layer that makes access auditable and revocable</li>
<li>AWS roles have <strong>two</strong> required configs: trust policy (who can assume) + permission policy (what they can do) — both must be right</li>
<li>GCP binds roles to resources; AWS attaches policies to identities — the mental models run in <strong>opposite directions</strong></li>
<li><code class="" data-line="">iam:PassRole</code> in AWS and <code class="" data-line="">iam.serviceAccounts.actAs</code> in GCP are privilege escalation vectors — always scope to specific ARNs, never <code class="" data-line="">*</code></li>
</ul>
<hr />
<h2 id="the-big-picture">The Big Picture</h2>
<p>Three primitives underlie every cloud IAM system. Learn how they connect and any cloud access model becomes readable.</p>
<pre><code class="" data-line="">  THE THREE-LAYER STACK
  Build bottom-up. Assign top-down. Change one layer without touching the others.

  ┌──────────────────────────────────────────────────────────────────────┐
  │  LAYER 3 — IDENTITY                                                  │
  │  alice@company.com  ·  backend-service  ·  ci-runner@proj           │
  │  &quot;who is acting — a human, a service, or a machine&quot;                 │
  ├──────────────────────────────────────────────────────────────────────┤
  │  LAYER 2 — ROLE                                                      │
  │  BackendDeveloper  ·  DataAnalyst  ·  DeployBot  ·  S3ReadOnly      │
  │  &quot;what function does this identity serve — the job title&quot;           │
  ├──────────────────────────────────────────────────────────────────────┤
  │  LAYER 1 — POLICY                                                    │
  │  AllowS3Read  ·  AllowECRPush  ·  DenyProdDelete  ·  RequireMFA    │
  │  &quot;what is explicitly permitted or denied, under what conditions&quot;    │
  ├──────────────────────────────────────────────────────────────────────┤
  │  LAYER 0 — PERMISSION                                                │
  │  s3:GetObject  ·  ecr:PutImage  ·  s3:DeleteObject  ·  iam:PassRole│
  │  &quot;one verb on one class of resource — the atom of access control&quot;  │
  └──────────────────────────────────────────────────────────────────────┘

  When alice joins the backend team → assign her the BackendDeveloper role
  When the S3 bucket changes → update the policy once; alice gets it automatically
  When alice leaves → remove the role assignment; policy and permissions are untouched
</code></pre>
<p>If this maps better to something physical:</p>
<pre><code class="" data-line="">  PHYSICAL WORLD            →    CLOUD IAM

  A specific door rule           Permission      s3:GetObject
  Keycard access profile    →    Policy          AllowS3Read
  Job title                 →    Role            BackendDeveloper
  The employee              →    Identity        alice@company.com

  When the employee leaves: revoke the role assignment.
  The job title, the keycard profile, the door rules — all unchanged.
  Next hire gets the same role. Same access. No manual work.
</code></pre>
<hr />
<h2 id="introduction">Introduction</h2>
<p>IAM roles vs policies is a distinction that defines how cloud authorization actually works — and getting it wrong is how access sprawl starts. Every <a href="/authentication-vs-authorization-iam/">authentication vs authorization</a> failure at the authorization layer traces back to how these three primitives are — or aren&#8217;t — structured.</p>
<p>Every cloud IAM system — AWS, GCP, Azure — is built on the same three primitives: permissions, policies, and roles. Learn these well and any cloud provider becomes readable. Skip them and you spend years pattern-matching without understanding why anything is structured the way it is.</p>
<p><a href="/what-is-cloud-iam/">What Is Cloud IAM</a> established the foundation: IAM is the system that governs who can access what in cloud infrastructure, and its default answer is always deny. <a href="/authentication-vs-authorization-iam/">Authentication vs Authorization: AWS AccessDenied Explained</a> drew the line between authentication — proving identity — and authorization — proving you&#8217;re allowed to act. This episode is about the authorization layer specifically. These three building blocks are how authorization is expressed in practice.</p>
<p>Before walking through each one, here&#8217;s what access control looks like without any of this structure — because that&#8217;s the fastest way to understand why the layers exist.</p>
<p>In 2015 I inherited an AWS account from a 12-engineer team that had been building for 18 months. When I ran <code class="" data-line="">aws iam list-attached-user-policies</code> across the 23 users, 17 had policies attached directly to the user object — not to groups, not to roles.</p>
<p>One engineer had left six months earlier. His access key was still active. Three policies still attached: read access to prod S3, write to a DynamoDB table, ability to invoke Lambda functions. When I asked what the DynamoDB table was for, nobody could tell me. The Lambda functions no longer existed.</p>
<p>That account wasn&#8217;t built by negligent engineers. It was built by engineers reaching for whatever granted access fastest, under deadline, without a framework. Permissions scattered. Nothing tracked. Nothing removed.</p>
<p>Roles, policies, and permissions are the framework that prevents that. Understanding them is the difference between an IAM configuration you can audit in an afternoon and one that takes a week and still leaves you uncertain.</p>
<hr />
<h2 id="what-are-iam-permissions-the-atomic-unit-of-access-control">What Are IAM Permissions? The Atomic Unit of Access Control</h2>
<p>A <strong>permission</strong> is a single action on a class of resources. It is the most granular thing you can grant or deny — the atom of access control.</p>
<p>Cloud providers express permissions differently, but the structure is consistent: a service, a resource type, and an action verb.</p>
<pre><code class="" data-line=""># AWS: service:Action
s3:GetObject               # read an object from S3
ec2:StartInstances         # start EC2 instances
iam:PassRole               # assign a role to an AWS service — one of the most dangerous
kms:Decrypt                # use a KMS key to decrypt

# GCP: service.resource.verb
storage.objects.get
compute.instances.start
iam.serviceAccounts.actAs  # impersonate a service account — equivalent risk to iam:PassRole
cloudkms.cryptoKeyVersions.useToDecrypt

# Azure: Provider/ResourceType/Action
Microsoft.Storage/storageAccounts/blobServices/containers/read
Microsoft.Compute/virtualMachines/start/action
Microsoft.Authorization/roleAssignments/write   # grant roles — highest risk
Microsoft.KeyVault/vaults/secrets/getSecret/action
</code></pre>
<p>You generally don&#8217;t assign individual permissions directly to identities — that&#8217;s like handing someone 47 keys with no labels and expecting the system to remain auditable. Permissions are grouped into policies.</p>
<hr />
<h2 id="what-are-iam-policies-grouping-permissions-with-conditions">What Are IAM Policies? Grouping Permissions with Conditions</h2>
<p>A <strong>policy</strong> is a document that groups permissions and defines the conditions under which they apply.</p>
<h3 id="aws-policy-structure">AWS policy structure</h3>
<p>An AWS policy document is JSON. Every field is a deliberate decision:</p>
<pre><code class="" data-line="">{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [
    {
      &quot;Sid&quot;: &quot;AllowReadS3Backups&quot;,
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Action&quot;: [&quot;s3:GetObject&quot;, &quot;s3:ListBucket&quot;],
      &quot;Resource&quot;: [
        &quot;arn:aws:s3:::company-backups&quot;,
        &quot;arn:aws:s3:::company-backups/*&quot;
      ],
      &quot;Condition&quot;: {
        &quot;StringEquals&quot;: { &quot;s3:prefix&quot;: [&quot;2024/&quot;, &quot;2025/&quot;] }
      }
    },
    {
      &quot;Sid&quot;: &quot;DenyDeleteEverywhere&quot;,
      &quot;Effect&quot;: &quot;Deny&quot;,
      &quot;Action&quot;: &quot;s3:DeleteObject&quot;,
      &quot;Resource&quot;: &quot;*&quot;
    }
  ]
}
</code></pre>
<p>The <code class="" data-line="">Sid</code> is a comment — use it. <code class="" data-line="">AllowReadS3Backups</code> tells a future auditor why this statement exists. <code class="" data-line="">Statement1</code> is technical debt.</p>
<p>The <code class="" data-line="">Effect</code> is either <code class="" data-line="">Allow</code> or <code class="" data-line="">Deny</code>. A <code class="" data-line="">Deny</code> always wins — it cannot be overridden by any <code class="" data-line="">Allow</code> anywhere in any policy on the same identity. If you have a <code class="" data-line="">Deny</code> on <code class="" data-line="">s3:DeleteObject</code> with <code class="" data-line="">&quot;Resource&quot;: &quot;*&quot;</code>, nothing can grant delete access to that identity. This asymmetry is deliberate: it&#8217;s how guardrails work.</p>
<p>The <code class="" data-line="">Resource</code> field is where access most often creeps wider than intended. <code class="" data-line="">&quot;Resource&quot;: &quot;*&quot;</code> on a write action means &#8220;every resource of this type in the account.&#8221; It works. It outlives the context that made it feel reasonable.</p>
<h3 id="aws-policy-types-which-to-reach-for">AWS policy types — which to reach for</h3>
<pre><code class="" data-line="">┌──────────────────────────┬────────────────────────────┬────────────────────────────┐
│ Type                     │ Attached to                │ What it does               │
├──────────────────────────┼────────────────────────────┼────────────────────────────┤
│ Identity-based           │ User, Group, Role          │ What the identity can do   │
│ Resource-based           │ S3 bucket, KMS key, Lambda │ Who can touch this resource │
│ Permissions boundary     │ User or Role               │ Maximum possible — ceiling  │
│ Service Control Policy   │ AWS Org OU or Account      │ Org-level guardrail         │
│ Session policy           │ AssumeRole session         │ Restricts a specific session│
│ Resource Control Policy  │ AWS Org resources          │ Resource-level org guardrail│
└──────────────────────────┴────────────────────────────┴────────────────────────────┘
</code></pre>
<p>Critical: <strong>Permissions boundaries and SCPs do not grant permissions</strong>. They constrain them. A boundary that allows <code class="" data-line="">s3:*</code> doesn&#8217;t mean the identity has S3 access. It means the identity <em>can have at most</em> S3 access, if an identity-based policy actually grants it. Many engineers set a boundary and expect it to work as a grant. It doesn&#8217;t.</p>
<h3 id="gcp-policy-bindings">GCP policy bindings</h3>
<p>GCP doesn&#8217;t attach policy documents to identities. Each resource has an IAM policy — a set of <strong>bindings</strong> mapping roles to members:</p>
<pre><code class="" data-line="">{
  &quot;bindings&quot;: [
    {
      &quot;role&quot;: &quot;roles/storage.objectViewer&quot;,
      &quot;members&quot;: [
        &quot;user:alice@company.com&quot;,
        &quot;serviceAccount:app-backend@project.iam.gserviceaccount.com&quot;
      ]
    },
    {
      &quot;role&quot;: &quot;roles/storage.objectCreator&quot;,
      &quot;members&quot;: [&quot;serviceAccount:upload-service@project.iam.gserviceaccount.com&quot;],
      &quot;condition&quot;: {
        &quot;title&quot;: &quot;Business hours only&quot;,
        &quot;expression&quot;: &quot;request.time.getHours(&#039;America/New_York&#039;) &gt;= 9 &amp;&amp; request.time.getHours(&#039;America/New_York&#039;) &lt; 18&quot;
      }
    }
  ]
}
</code></pre>
<p>The mental model shift: in AWS you ask &#8220;what can this identity do?&#8221; by looking at the identity. In GCP you ask &#8220;who can access this resource?&#8221; by looking at the resource. The question runs in the opposite direction.</p>
<h3 id="azure-role-definitions">Azure role definitions</h3>
<p>Azure separates what a role grants (role definition) from who gets it where (role assignment). Define once, assign at multiple scopes.</p>
<pre><code class="" data-line="">{
  &quot;Name&quot;: &quot;Custom Storage Reader&quot;,
  &quot;IsCustom&quot;: true,
  &quot;Actions&quot;: [
    &quot;Microsoft.Storage/storageAccounts/blobServices/containers/read&quot;,
    &quot;Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKey/action&quot;
  ],
  &quot;DataActions&quot;: [
    &quot;Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read&quot;
  ],
  &quot;AssignableScopes&quot;: [&quot;/subscriptions/SUB_ID&quot;]
}
</code></pre>
<p><code class="" data-line="">Actions</code> vs <code class="" data-line="">DataActions</code> catches people. <code class="" data-line="">Actions</code> are control plane — you can see the storage account exists. <code class="" data-line="">DataActions</code> are data plane — you can read actual blob contents. A user with <code class="" data-line="">Actions</code> can list the container but cannot read a single byte without a <code class="" data-line="">DataAction</code>. Both planes must be covered for the access to be complete.</p>
<hr />
<h2 id="what-are-iam-roles-the-layer-that-scales-access-control">What Are IAM Roles? The Layer That Scales Access Control</h2>
<p>A <strong>role</strong> is a collection of policies assigned to identities. It&#8217;s the indirection layer that makes access manageable at scale.</p>
<p>Going back to the 2015 account: the problem wasn&#8217;t that engineers had access — they needed it. The problem was that access was scattered across 23 individual user objects with no shared structure. This is what <a href="/what-is-cloud-iam/">what is cloud IAM</a> establishes as the core problem IAM exists to solve. Roles are the structural answer.</p>
<p>The role model solves this:</p>
<pre><code class="" data-line="">Policy: S3ReadAccess (s3:GetObject, s3:ListBucket on s3:::app-data/*)
  ↓ attached to
Role: BackendDeveloper
  ↓ assigned to
Users: alice, bob, charlie, dave (and six more)

When the bucket changes  → update one policy
When someone joins       → assign one role
When someone leaves      → remove one role
Access model stays coherent because it&#039;s structured.
</code></pre>
<h3 id="aws-roles-the-identity-that-issues-temporary-credentials">AWS roles — the identity that issues temporary credentials</h3>
<p>AWS roles are themselves IAM identities, not just permission containers. When something assumes a role, it gets temporary credentials from STS. Two things must be configured:</p>
<p><strong>Trust policy — who can assume:</strong></p>
<pre><code class="" data-line="">{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [{
    &quot;Effect&quot;: &quot;Allow&quot;,
    &quot;Principal&quot;: { &quot;Service&quot;: &quot;ec2.amazonaws.com&quot; },
    &quot;Action&quot;: &quot;sts:AssumeRole&quot;
  }]
}
</code></pre>
<p>Without this, nobody can use the role regardless of its permissions. The trust policy is the gatekeeper.</p>
<p><strong>Permission policy — what it can do:</strong></p>
<pre><code class="" data-line="">aws iam create-role \
  --role-name AppServerRole \
  --assume-role-policy-document file://ec2-trust-policy.json

aws iam attach-role-policy \
  --role-name AppServerRole \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
</code></pre>
<p>When debugging &#8220;why can&#8217;t this Lambda/EC2/ECS task do X?&#8221;, the first thing I check is the trust policy. Many times the permission policy is correct — the service simply isn&#8217;t in the trust policy and cannot assume the role at all.</p>
<h3 id="gcp-role-types">GCP role types</h3>
<pre><code class="" data-line="">┌──────────────────┬──────────────────────────────┬──────────────────────────────────┐
│ Type             │ Example                      │ When to use                      │
├──────────────────┼──────────────────────────────┼──────────────────────────────────┤
│ Basic/Primitive  │ roles/editor, roles/owner    │ Never in production              │
│ Predefined       │ roles/storage.objectViewer   │ Default — service-specific       │
│ Custom           │ Your org defines             │ When predefined is too broad     │
└──────────────────┴──────────────────────────────┴──────────────────────────────────┘
</code></pre>
<p><code class="" data-line="">roles/editor</code> at the project level grants write access to almost every GCP service. I&#8217;ve seen it granted &#8220;temporarily&#8221; and found it attached six months later. Always use predefined roles.</p>
<pre><code class="" data-line=""># Find the right predefined role
gcloud iam roles list --filter=&quot;name:roles/storage&quot; --format=&quot;table(name,title)&quot;

# See exactly what permissions it includes
gcloud iam roles describe roles/storage.objectViewer

# Create a custom role when predefined is still too broad
cat &gt; custom-log-reader.yaml &lt;&lt; &#039;EOF&#039;
title: &quot;Log Reader&quot;
description: &quot;Read application logs — nothing else&quot;
stage: &quot;GA&quot;
includedPermissions:
  - logging.logEntries.list
  - logging.logs.list
  - logging.logMetrics.get
EOF
gcloud iam roles create LogReader --project=my-project --file=custom-log-reader.yaml
</code></pre>
<h3 id="azure-built-in-and-custom-roles">Azure built-in and custom roles</h3>
<pre><code class="" data-line=""># List built-in roles containing &quot;Storage&quot;
az role definition list --output table | grep Storage

# View what a built-in role grants
az role definition list --name &quot;Storage Blob Data Reader&quot;

# Create a custom role
az role definition create --role-definition custom-app-storage.json

# Assign at a specific scope
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
</code></pre>
<hr />
<h2 id="rbac-vs-abac-which-access-control-model-to-use">RBAC vs ABAC: Which Access Control Model to Use</h2>
<h3 id="rbac-role-based-access-control">RBAC — Role-Based Access Control</h3>
<p>The dominant model. Access flows from role membership:</p>
<pre><code class="" data-line="">alice     ∈ BackendDeveloper  →  s3:GetObject on app-data/*
bob       ∈ DataAnalyst       →  athena:* on analytics-queries
ci-runner ∈ DeployRole        →  ecr:PutImage, ecs:UpdateService
</code></pre>
<p>RBAC degrades two ways: <strong>role explosion</strong> (200 roles, nobody can explain what they all do) and <strong>coarse roles</strong> (avoid explosion by making roles broad, now BackendDeveloper has prod access with no distinction from dev). Both look the same on a spreadsheet — lots of access, no clear principle.</p>
<h3 id="abac-attribute-based-access-control">ABAC — Attribute-Based Access Control</h3>
<p>ABAC grants access based on attributes of the principal, resource, or environment — not role membership. This one policy replaced 12 team-specific policies in one account:</p>
<pre><code class="" data-line="">{
  &quot;Effect&quot;: &quot;Allow&quot;,
  &quot;Action&quot;: &quot;ec2:*&quot;,
  &quot;Resource&quot;: &quot;*&quot;,
  &quot;Condition&quot;: {
    &quot;StringEquals&quot;: {
      &quot;aws:ResourceTag/Team&quot;: &quot;${aws:PrincipalTag/Team}&quot;
    }
  }
}
</code></pre>
<p>An engineer tagged <code class="" data-line="">Team=Platform</code> can only act on EC2 resources tagged <code class="" data-line="">Team=Platform</code>. Add a new team — tag their resources and their identity. No new policy. No new role.</p>
<p>The risk is tag drift. If someone tags a resource incorrectly, the access model breaks silently. In practice, I use ABAC for environment and team scoping, and explicit policies for sensitive services like KMS and IAM. How these primitives combine in a full AWS account is covered in the <a href="/aws-iam-deep-dive/">AWS IAM deep dive</a>.</p>
<h3 id="conditions-when-context-determines-access">Conditions — when context determines access</h3>
<pre><code class="" data-line="">// Require MFA for any IAM or Organizations action
{
  &quot;Effect&quot;: &quot;Deny&quot;,
  &quot;Action&quot;: [&quot;iam:*&quot;, &quot;organizations:*&quot;],
  &quot;Resource&quot;: &quot;*&quot;,
  &quot;Condition&quot;: { &quot;BoolIfExists&quot;: { &quot;aws:MultiFactorAuthPresent&quot;: &quot;false&quot; } }
}

// Restrict to corporate IP range
{
  &quot;Effect&quot;: &quot;Deny&quot;,
  &quot;Action&quot;: &quot;*&quot;,
  &quot;Resource&quot;: &quot;*&quot;,
  &quot;Condition&quot;: {
    &quot;NotIpAddress&quot;: { &quot;aws:SourceIp&quot;: [&quot;10.0.0.0/8&quot;, &quot;172.16.0.0/12&quot;] }
  }
}
</code></pre>
<p>The MFA condition is in every account I manage. A compromised API key without an MFA session can&#8217;t escalate IAM privileges — the Deny blocks it at the condition level. This single statement meaningfully reduces the blast radius of a credential compromise.</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>
<pre><code class="" data-line="">╔══════════════════════════════════════════════════════════════════════╗
║  &#x26a0;  GOTCHA 1 — Policies attached directly to users                 ║
║                                                                      ║
║  Feels fast. Creates the exact problem from 2015: access scattered  ║
║  across individual user objects with no shared structure.            ║
║  When the user leaves, their policies don&#039;t follow — they stay.     ║
║                                                                      ║
║  Fix: always use roles. Attach policies to roles. Assign roles to   ║
║  users. The role outlives the person.                               ║
╚══════════════════════════════════════════════════════════════════════╝

╔══════════════════════════════════════════════════════════════════════╗
║  &#x26a0;  GOTCHA 2 — Using AWS managed policies in production            ║
║                                                                      ║
║  AmazonS3FullAccess grants s3:* on *. For a Lambda that reads one  ║
║  specific bucket, that&#039;s ~30 permissions you didn&#039;t need, all live. ║
║                                                                      ║
║  Fix: create customer managed policies scoped to the specific       ║
║  actions and ARNs the workload actually uses.                       ║
╚══════════════════════════════════════════════════════════════════════╝

╔══════════════════════════════════════════════════════════════════════╗
║  &#x26a0;  GOTCHA 3 — iam:PassRole with &quot;Resource&quot;: &quot;*&quot;                   ║
║                                                                      ║
║  iam:PassRole lets an identity assign a role to an AWS service.     ║
║  With Resource: *, it can pass ANY role — including ones with more  ║
║  permissions than it currently has. That is a privilege escalation. ║
║                                                                      ║
║  Fix: always scope iam:PassRole to a specific role ARN:             ║
║  &quot;Resource&quot;: &quot;arn:aws:iam::ACCOUNT:role/SpecificRoleName&quot;          ║
╚══════════════════════════════════════════════════════════════════════╝

╔══════════════════════════════════════════════════════════════════════╗
║  &#x26a0;  GOTCHA 4 — Permissions boundary ≠ policy grant                 ║
║                                                                      ║
║  Setting a boundary that allows s3:* does NOT grant S3 access.     ║
║  The boundary is a ceiling — it limits maximum possible permissions. ║
║  The identity-based policy still needs to explicitly Allow the      ║
║  action. Both must be present for the access to work.               ║
╚══════════════════════════════════════════════════════════════════════╝
</code></pre>
<hr />
<h2 id="cross-cloud-rosetta-stone">Cross-Cloud Rosetta Stone</h2>
<p>Same concepts, different names and different directions. Bookmark this table.</p>
<pre><code class="" data-line="">┌─────────────────────────┬──────────────────────────┬──────────────────────────┬──────────────────────────┐
│ Concept                 │ AWS                      │ GCP                      │ Azure                    │
├─────────────────────────┼──────────────────────────┼──────────────────────────┼──────────────────────────┤
│ Atomic permission       │ s3:GetObject             │ storage.objects.get      │ .../blobs/read           │
│ Permission document     │ Policy (JSON)            │ (built into role def)    │ Role Definition          │
│ Access grant            │ Policy attachment        │ IAM Binding              │ Role Assignment          │
│ Job-function identity   │ IAM Role                 │ Predefined Role          │ Built-in Role            │
│ Non-human identity      │ IAM Role (assumed)       │ Service Account          │ Managed Identity         │
│ Org-level guardrail     │ SCP                      │ Org Policy               │ Management Group Policy  │
│ Permission ceiling      │ Permissions Boundary     │ —                        │ —                        │
│ Session restriction     │ Session Policy           │ —                        │ —                        │
│ Attribute-based grant   │ Tag conditions in policy │ IAM Conditions           │ Conditions in assignment │
└─────────────────────────┴──────────────────────────┴──────────────────────────┴──────────────────────────┘
</code></pre>
<hr />
<h2 id="quick-reference">Quick Reference</h2>
<pre><code class="" data-line="">┌──────────────────────────┬────────────────────────────────────────────────────────────┐
│ Term                     │ What it is                                                 │
├──────────────────────────┼────────────────────────────────────────────────────────────┤
│ Permission               │ Atomic: one action on one resource class                   │
│ Policy                   │ Document grouping permissions + conditions                 │
│ Role (AWS)               │ Assumable identity — carries policies, issues temp creds   │
│ Trust policy (AWS)       │ Who can assume this role — separate from permissions       │
│ Permissions boundary     │ Ceiling — limits max possible permissions; does not grant  │
│ SCP                      │ Org guardrail — constrains all identities in scope         │
│ IAM Binding (GCP)        │ Maps a role to a member on a specific resource             │
│ Role Assignment (Azure)  │ Grants a role definition at a specific scope               │
│ ABAC                     │ Access by tag/attribute — one policy replaces many roles   │
│ RBAC                     │ Access by role membership — clean until roles proliferate  │
│ iam:PassRole             │ Privilege escalation vector — always scope to specific ARN │
└──────────────────────────┴────────────────────────────────────────────────────────────┘

Commands to know:
┌────────────────────────────────────────────────────────────────────────────────┐
│  # AWS — list policies attached to a role                                     │
│  aws iam list-attached-role-policies --role-name MyRole                       │
│                                                                                │
│  # AWS — view what a managed policy actually grants                           │
│  aws iam get-policy-version \                                                  │
│    --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \              │
│    --version-id v1                                                             │
│                                                                                │
│  # AWS — who can assume this role?                                            │
│  aws iam get-role --role-name MyRole --query &#039;Role.AssumeRolePolicyDocument&#039;  │
│                                                                                │
│  # GCP — view the IAM policy on a project                                    │
│  gcloud projects get-iam-policy PROJECT_ID --format=json                      │
│                                                                                │
│  # GCP — list all roles and what permissions they include                    │
│  gcloud iam roles describe roles/storage.objectViewer                         │
│                                                                                │
│  # Azure — list role assignments in a subscription                           │
│  az role assignment list --all --output table                                 │
│                                                                                │
│  # Azure — view exactly what a built-in role grants                          │
│  az role definition list --name &quot;Storage Blob Data Reader&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>RBAC and ABAC are the implementation models for authorization at scale</td>
</tr>
<tr>
<td>CISSP</td>
<td>Domain 1 — Security &amp; Risk Management</td>
<td>Role design implements separation of duties and least privilege</td>
</tr>
<tr>
<td>ISO 27001:2022</td>
<td>5.15 Access control</td>
<td>Access control policy — roles and policies are the mechanism</td>
</tr>
<tr>
<td>ISO 27001:2022</td>
<td>5.18 Access rights</td>
<td>Provisioning, review, and removal of access rights — roles make this auditable</td>
</tr>
<tr>
<td>ISO 27001:2022</td>
<td>8.2 Privileged access rights</td>
<td>Permissions boundaries and conditions applied to elevated access</td>
</tr>
<tr>
<td>SOC 2</td>
<td>CC6.1</td>
<td>Logical access security — policy documents are the technical implementation</td>
</tr>
<tr>
<td>SOC 2</td>
<td>CC6.3</td>
<td>Access revocation — role-based model makes removal consistent and auditable</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>Permissions are atomic — one action on one resource class. Policies group permissions. Roles carry policies for assignment</li>
<li>AWS roles have two required configs: trust policy (who can assume) and permission policy (what it can do) — both must be correct</li>
<li>GCP binds roles to resources; AWS attaches policies to identities — the mental model runs in opposite directions</li>
<li>Azure separates role definition (what) from role assignment (who, where) — define once, assign at multiple scopes</li>
<li>RBAC scales through role design; ABAC scales through tag/attribute conditions — use ABAC where roles would proliferate</li>
<li><code class="" data-line="">iam:PassRole</code> and <code class="" data-line="">iam.serviceAccounts.actAs</code> are privilege escalation vectors — scope them to specific ARNs, never <code class="" data-line="">*</code></li>
<li>Conditions add context (MFA, IP, tags, time) to policies — the MFA condition on IAM actions is essential in every account</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP04 goes deep on AWS IAM — the most complex of the three cloud models. Policy evaluation order, cross-account trust, permissions boundaries in practice, SCPs, and IAM Identity Center for human access. We&#8217;ll work through the patterns that make AWS IAM maintainable at production scale.</p>
<p><em>Next: <a href="/aws-iam-deep-dive/">AWS IAM Deep Dive: Users, Groups, Roles, and Policies Explained</a></em></p>
<p>Get the AWS IAM deep dive 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%2Fiam-roles-policies-permissions-explained%2F&amp;linkname=IAM%20Roles%20vs%20Policies%3A%20How%20Cloud%20Authorization%20Actually%20Works" 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%2Fiam-roles-policies-permissions-explained%2F&amp;linkname=IAM%20Roles%20vs%20Policies%3A%20How%20Cloud%20Authorization%20Actually%20Works" 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%2Fiam-roles-policies-permissions-explained%2F&amp;linkname=IAM%20Roles%20vs%20Policies%3A%20How%20Cloud%20Authorization%20Actually%20Works" 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%2Fiam-roles-policies-permissions-explained%2F&amp;linkname=IAM%20Roles%20vs%20Policies%3A%20How%20Cloud%20Authorization%20Actually%20Works" 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%2Fiam-roles-policies-permissions-explained%2F&amp;linkname=IAM%20Roles%20vs%20Policies%3A%20How%20Cloud%20Authorization%20Actually%20Works" 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%2Fiam-roles-policies-permissions-explained%2F&amp;linkname=IAM%20Roles%20vs%20Policies%3A%20How%20Cloud%20Authorization%20Actually%20Works" 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%2Fiam-roles-policies-permissions-explained%2F&amp;linkname=IAM%20Roles%20vs%20Policies%3A%20How%20Cloud%20Authorization%20Actually%20Works" 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%2Fiam-roles-policies-permissions-explained%2F&#038;title=IAM%20Roles%20vs%20Policies%3A%20How%20Cloud%20Authorization%20Actually%20Works" data-a2a-url="https://linuxcent.com/iam-roles-policies-permissions-explained/" data-a2a-title="IAM Roles vs Policies: How Cloud Authorization Actually Works"></a></p><p>The post <a href="https://linuxcent.com/iam-roles-policies-permissions-explained/">IAM Roles vs Policies: How Cloud Authorization Actually Works</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/iam-roles-policies-permissions-explained/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1458</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-09-02 14:43:53 by W3 Total Cache
-->