π AWS Identity and Access Management (IAM)
IAM (Identity and Access Management) is an AWS global service that controls who can access AWS resources and what actions they can perform.
Being a global service means IAM is not tied to a specific AWS Region. You configure it once, and it applies across all regions worldwide.
π‘ Why IAM Matters
IAM is the security foundation of AWS. It ensures:
The right people or systems have access
Access is restricted to only what is necessary
AWS accounts are protected from misuse
π You have already interacted with IAM when creating your AWS root account.
β οΈ Best Practice: Never use the root account for day-to-day operations.
π§© Core IAM Concepts
IAM is built around four main elements:
Users
Groups
Roles
Policies
π€ IAM Users
IAM users represent real people or applications in your organization.
IAM users can represent either:
π€ A person β e.g., a developer logging into the AWS Console.
π€ An application β e.g., a Node.js app using AWS SDK with an Access Key and Secret Key to access services like S3.
Example:
Node.js App
β
IAM User (Access Key + Secret Key)
β
Amazon S3Note: While applications can use IAM users, AWS recommends using IAM Roles instead because they provide temporary credentials and are more secure.
π§ Key Points:
Each user has individual login credentials
Credentials should never be shared
One IAM user = one person or application
<div class="iam-users-row" id="iamUsersRow">
<style>
.iam-users-row {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--line: #ddd6c9;
--radius: 10px;
--mono-font: 'IBM Plex Mono', 'SFMono-Regular', Menlo, monospace;
--body-font: 'Inter', -apple-system, sans-serif;
background: var(--paper-alt);
border-radius: var(--radius);
font-family: var(--body-font);
max-width: 880px;
margin: 0 auto;
padding: clamp(1.25rem, 4vw, 2rem);
box-sizing: border-box;
}
.iam-users-row[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #13171F;
--paper-alt: #1c212c;
--accent: #5b8def;
--line: #2c313d;
}
.iam-users-row * { box-sizing: border-box; }
.iam-users-row .grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
column-gap: clamp(0.75rem, 2.5vw, 1.75rem);
row-gap: 1.5rem;
}
.iam-users-row .user-card {
text-align: center;
min-width: 0;
}
.iam-users-row .user-card svg {
width: clamp(38px, 9vw, 48px);
height: clamp(38px, 9vw, 48px);
}
.iam-users-row .icon-person circle,
.iam-users-row .icon-person path {
fill: none; stroke: var(--ink); stroke-width: 5; stroke-linecap: round;
}
.iam-users-row .user-card.is-app .icon-person circle,
.iam-users-row .user-card.is-app .icon-person path {
stroke: var(--accent);
}
.iam-users-row .user-name {
font-weight: 700;
font-size: clamp(0.85rem, 2.6vw, 1rem);
margin-top: 0.35rem;
white-space: nowrap;
}
.iam-users-row .cred-id {
font-family: var(--mono-font);
font-size: clamp(0.62rem, 2.2vw, 0.78rem);
background: var(--paper);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 0.4rem 0.55rem;
margin-top: 0.55rem;
display: flex;
align-items: center;
justify-content: center;
gap: 0.3rem;
color: var(--ink-soft);
line-height: 1.3;
word-break: break-word;
}
.iam-users-row .cred-key { flex-shrink: 0; }
@media (max-width: 640px) {
.iam-users-row .grid {
grid-template-columns: repeat(3, 1fr);
row-gap: 2rem;
}
}
</style>
<div class="grid">
<div class="user-card" data-user="alice">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-name">Alice</div>
<div class="cred-id"><span class="cred-key">π</span> alice.iam</div>
</div>
<div class="user-card" data-user="bob">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-name">Bob</div>
<div class="cred-id"><span class="cred-key">π</span> bob.iam</div>
</div>
<div class="user-card" data-user="charles">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-name">Charles</div>
<div class="cred-id"><span class="cred-key">π</span> charles.iam</div>
</div>
<div class="user-card is-app" data-user="david">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-name">David</div>
<div class="cred-id"><span class="cred-key">π</span> david-app</div>
</div>
<div class="user-card is-app" data-user="edward">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-name">Edward</div>
<div class="cred-id"><span class="cred-key">π</span> edward-app</div>
</div>
<div class="user-card is-app" data-user="fred">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-name">Fred</div>
<div class="cred-id"><span class="cred-key">π</span> fred-app</div>
</div>
</div>
<script>
(function () {
var wrapper = document.currentScript.closest('.iam-users-row')
|| document.querySelector('.iam-users-row');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
</script>
</div>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});π₯ IAM Groups
Groups are collections of IAM users with shared permissions.
π§ Key Rules:
Groups can contain only users
Groups cannot contain other groups
Users do not have to belong to a group
<div class="iam-groups" id="iamGroups">
<style>
.iam-groups {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--group-dev: #2563eb;
--group-ops: #f97316;
--line: #ddd6c9;
--radius: 10px;
--body-font: 'Inter', -apple-system, sans-serif;
background: var(--paper-alt);
border-radius: var(--radius);
font-family: var(--body-font);
max-width: 880px;
margin: 0 auto;
padding: clamp(1.25rem, 4vw, 2rem);
box-sizing: border-box;
}
.iam-groups[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #13171F;
--paper-alt: #1c212c;
--accent: #5b8def;
--group-dev: #5b8def;
--group-ops: #fb923c;
--line: #2c313d;
}
.iam-groups * { box-sizing: border-box; }
.iam-groups .row {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: clamp(1rem, 3vw, 1.75rem);
}
.iam-groups .group-card {
background: var(--paper);
border: 2px solid var(--group-color);
border-radius: var(--radius);
padding: clamp(1rem, 3vw, 1.5rem);
flex: 1 1 320px;
min-width: 0;
}
.iam-groups .group-card.dev { --group-color: var(--group-dev); }
.iam-groups .group-card.ops { --group-color: var(--group-ops); }
.iam-groups .group-label {
font-weight: 700;
font-size: clamp(0.95rem, 2.6vw, 1.15rem);
margin-bottom: clamp(0.75rem, 3vw, 1.25rem);
}
.iam-groups .members {
display: grid;
grid-template-columns: repeat(3, 1fr);
column-gap: clamp(0.5rem, 3vw, 1.25rem);
row-gap: 1.25rem;
}
.iam-groups .ungrouped {
flex: 0 1 120px;
display: flex;
flex-direction: column;
align-items: center;
align-self: center;
}
.iam-groups .user-card {
text-align: center;
min-width: 0;
}
.iam-groups .user-card svg {
width: clamp(40px, 9vw, 52px);
height: clamp(40px, 9vw, 52px);
}
.iam-groups .icon-person circle,
.iam-groups .icon-person path {
fill: none; stroke: var(--ink); stroke-width: 5; stroke-linecap: round;
}
.iam-groups .user-name {
font-weight: 700;
font-size: clamp(0.82rem, 2.6vw, 1rem);
margin-top: 0.35rem;
white-space: nowrap;
}
@media (max-width: 640px) {
.iam-groups .row { flex-direction: column; }
.iam-groups .group-card { flex-basis: auto; width: 100%; }
.iam-groups .ungrouped { align-self: flex-start; margin-left: 0.25rem; }
}
</style>
<div class="row">
<div class="group-card dev">
<div class="group-label">Group: Developers</div>
<div class="members">
<div class="user-card" data-user="alice">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-name">Alice</div>
</div>
<div class="user-card" data-user="bob">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-name">Bob</div>
</div>
<div class="user-card" data-user="charles">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-name">Charles</div>
</div>
</div>
</div>
<div class="group-card ops">
<div class="group-label">Group: Operations</div>
<div class="members">
<div class="user-card" data-user="david">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-name">David</div>
</div>
<div class="user-card" data-user="edward">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-name">Edward</div>
</div>
</div>
</div>
<div class="ungrouped">
<div class="user-card" data-user="fred">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-name">Fred</div>
</div>
</div>
</div>
<script>
(function () {
var wrapper = document.currentScript.closest('.iam-groups')
|| document.querySelector('.iam-groups');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
</script>
</div>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});Users can belong to multiple groups
<div class="iam-overlap" id="iamOverlap">
<style>
.iam-overlap {
--ink: #1a1a1a;
--paper-alt: #f0ece4;
--paper: #ffffff;
--group-dev: #2563eb;
--group-ops: #f97316;
--group-audit:#16a34a;
--radius: 10px;
--body-font: 'Inter', -apple-system, sans-serif;
background: var(--paper-alt);
border-radius: var(--radius);
font-family: var(--body-font);
max-width: 880px;
margin: 0 auto;
padding: clamp(1rem, 4vw, 1.75rem);
box-sizing: border-box;
}
.iam-overlap[data-theme="dark"] {
--ink: #e8e6e1;
--paper-alt: #1c212c;
--paper: #13171F;
--group-dev: #5b8def;
--group-ops: #fb923c;
--group-audit:#4ade80;
}
.iam-overlap * { box-sizing: border-box; }
.iam-overlap .diagram-scroll {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
padding-bottom: 0.25rem;
}
.iam-overlap svg {
display: block;
width: 100%;
min-width: 620px;
height: auto;
}
.iam-overlap .scroll-hint {
display: none;
font-size: 0.75rem;
color: var(--ink);
opacity: 0.55;
text-align: center;
margin-top: 0.4rem;
}
@media (max-width: 640px) {
.iam-overlap .scroll-hint { display: block; }
}
</style>
<div class="diagram-scroll">
<svg viewBox="0 0 1700 420" xmlns="http://www.w3.org/2000/svg">
<defs>
<g id="personIcon">
<circle cx="24" cy="14" r="9" fill="none" stroke="var(--ink)" stroke-width="4.5"/>
<path d="M6 44c0-11 8-18 18-18s18 7 18 18" fill="none" stroke="var(--ink)" stroke-width="4.5" stroke-linecap="round"/>
</g>
</defs>
<!-- Developers group -->
<rect x="40" y="40" width="710" height="340" rx="28" fill="none" stroke="var(--group-dev)" stroke-width="5"/>
<text x="80" y="90" font-size="34" font-weight="700" fill="var(--ink)">Group: Developers</text>
<!-- Operations group -->
<rect x="1000" y="40" width="650" height="340" rx="28" fill="none" stroke="var(--group-ops)" stroke-width="5"/>
<text x="1325" y="90" text-anchor="middle" font-size="34" font-weight="700" fill="var(--ink)">Group: Operations</text>
<!-- Audit Team group (overlaps both) -->
<rect x="600" y="110" width="560" height="200" rx="28" fill="none" stroke="var(--group-audit)" stroke-width="5"/>
<text x="880" y="185" text-anchor="middle" font-size="32" font-weight="700" fill="var(--ink)">Group</text>
<text x="880" y="230" text-anchor="middle" font-size="32" font-weight="700" fill="var(--ink)">Audit Team</text>
<!-- Alice -->
<use href="#personIcon" x="140" y="140" width="80" height="80"/>
<text x="180" y="260" text-anchor="middle" font-size="32" font-weight="700" fill="var(--ink)">Alice</text>
<!-- Bob -->
<use href="#personIcon" x="390" y="140" width="80" height="80"/>
<text x="430" y="260" text-anchor="middle" font-size="32" font-weight="700" fill="var(--ink)">Bob</text>
<!-- Charles (Developers + Audit) -->
<use href="#personIcon" x="640" y="140" width="80" height="80"/>
<text x="680" y="260" text-anchor="middle" font-size="32" font-weight="700" fill="var(--ink)">Charles</text>
<!-- David (Audit + Operations) -->
<use href="#personIcon" x="1040" y="140" width="80" height="80"/>
<text x="1080" y="260" text-anchor="middle" font-size="32" font-weight="700" fill="var(--ink)">David</text>
<!-- Edward -->
<use href="#personIcon" x="1290" y="140" width="80" height="80"/>
<text x="1330" y="260" text-anchor="middle" font-size="32" font-weight="700" fill="var(--ink)">Edward</text>
<!-- Fred -->
<use href="#personIcon" x="1540" y="140" width="80" height="80"/>
<text x="1580" y="260" text-anchor="middle" font-size="32" font-weight="700" fill="var(--ink)">Fred</text>
</svg>
</div>
<div class="scroll-hint">β scroll to see full diagram β</div>
<script>
(function () {
var wrapper = document.currentScript.closest('.iam-overlap')
|| document.querySelector('.iam-overlap');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
</script>
</div>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});β Why Groups Matter: Simplifies permission management, makes it scalable and easier to maintain.
π Example: IAM Groups in a Company
A company organizes users by role. Each user gets unique login credentials and permissions through their group β pick an action below to see who can actually do it.
<div class="infographic-iam-groups-ex" data-scenario="none">
<style>
.infographic-iam-groups-ex {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--line: #ddd6c9;
--error: #c0392b;
--error-soft: #fbe9e7;
--ok: #16a34a;
--ok-soft: #e3f5e9;
--group-admins: #2563eb;
--group-dev: #16a34a;
--group-fin: #f97316;
--radius: 10px;
--display-font: 'Fraunces', Georgia, serif;
--body-font: 'Inter', -apple-system, sans-serif;
--mono-font: 'IBM Plex Mono', 'SFMono-Regular', Menlo, monospace;
background: var(--paper);
color: var(--ink);
font-family: var(--body-font);
max-width: 880px;
margin: 0 auto;
padding: clamp(1.25rem, 5vw, 3rem);
box-sizing: border-box;
}
.infographic-iam-groups-ex[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #13171F;
--paper-alt: #1c212c;
--accent: #5b8def;
--line: #2c313d;
--error: #e57373;
--error-soft: #3a2222;
--ok: #4ade80;
--ok-soft: #1a3324;
--group-admins: #5b8def;
--group-dev: #4ade80;
--group-fin: #fb923c;
}
.infographic-iam-groups-ex * { box-sizing: border-box; }
.infographic-iam-groups-ex .eyebrow {
font-family: var(--mono-font); font-size: 0.75rem; letter-spacing: 0.08em;
text-transform: uppercase; color: var(--accent); margin-bottom: 0.4rem;
}
.infographic-iam-groups-ex h2 {
font-family: var(--display-font); font-weight: 600; font-size: clamp(1.5rem, 4vw, 2.1rem);
margin: 0 0 0.5rem 0;
}
.infographic-iam-groups-ex .subtitle {
color: var(--ink-soft); font-size: 0.95rem; max-width: 56ch; margin: 0 0 1.75rem 0; line-height: 1.5;
}
.infographic-iam-groups-ex .scenario-row {
display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 1.5rem;
}
.infographic-iam-groups-ex .scenario-btn {
font: inherit; font-size: 0.82rem; font-weight: 600; color: var(--ink-soft);
background: var(--paper-alt); border: 1px solid var(--line); border-radius: var(--radius);
padding: 0.6rem 0.95rem; cursor: pointer; transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.infographic-iam-groups-ex .scenario-btn.is-active {
background: var(--accent-soft, #e3ebfd); border-color: var(--accent); color: var(--accent);
}
.infographic-iam-groups-ex[data-theme="dark"] .scenario-btn.is-active { background: #202942; }
.infographic-iam-groups-ex .scenario-btn:hover { border-color: var(--accent); }
.infographic-iam-groups-ex .compare {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
background: var(--line); border: 1px solid var(--line); border-radius: var(--radius);
overflow: hidden; margin-bottom: 1.25rem;
}
.infographic-iam-groups-ex .compare-col {
background: var(--paper); padding: 1.5rem 1.25rem; text-align: center;
border-top: 3px solid var(--group-color);
}
.infographic-iam-groups-ex .compare-col[data-group="admins"] { --group-color: var(--group-admins); }
.infographic-iam-groups-ex .compare-col[data-group="developers"] { --group-color: var(--group-dev); }
.infographic-iam-groups-ex .compare-col[data-group="finance"] { --group-color: var(--group-fin); }
.infographic-iam-groups-ex .compare-col svg { width: 40px; height: 40px; }
.infographic-iam-groups-ex .icon-person circle,
.infographic-iam-groups-ex .icon-person path { fill: none; stroke: var(--group-color); stroke-width: 5; stroke-linecap: round; }
.infographic-iam-groups-ex .user-id {
font-family: var(--mono-font); font-size: 0.72rem; color: var(--ink-soft);
background: var(--paper-alt); border-radius: var(--radius); padding: 0.2rem 0.5rem;
display: inline-block; margin-top: 0.5rem;
}
.infographic-iam-groups-ex .group-name {
font-weight: 700; font-size: 1.05rem; margin-top: 0.65rem; color: var(--group-color);
}
.infographic-iam-groups-ex .group-desc {
font-size: 0.82rem; color: var(--ink-soft); margin-top: 0.35rem; line-height: 1.4;
min-height: 2.4rem;
}
.infographic-iam-groups-ex .result-badge {
margin-top: 0.9rem; font-family: var(--mono-font); font-size: 0.78rem; font-weight: 600;
padding: 0.4rem 0.5rem; border-radius: var(--radius); display: none;
}
.infographic-iam-groups-ex[data-scenario="none"] .result-badge { display: none; }
.infographic-iam-groups-ex:not([data-scenario="none"]) .result-badge { display: inline-block; }
.infographic-iam-groups-ex .result-badge.allowed { background: var(--ok-soft); color: var(--ok); }
.infographic-iam-groups-ex .result-badge.denied { background: var(--error-soft); color: var(--error); animation: iam-shake 0.4s; }
@keyframes iam-shake {
0%, 100% { transform: translateX(0); }
20% { transform: translateX(-4px); }
40% { transform: translateX(4px); }
60% { transform: translateX(-3px); }
80% { transform: translateX(3px); }
}
.infographic-iam-groups-ex .caption {
font-size: 0.92rem; color: var(--ink-soft); line-height: 1.5;
border-left: 3px solid var(--accent); padding-left: 0.9rem;
}
@media (max-width: 640px) {
.infographic-iam-groups-ex .compare { grid-template-columns: 1fr; }
.infographic-iam-groups-ex .compare-col { border-top: none; border-left: 3px solid var(--group-color); text-align: left; }
}
@media (prefers-reduced-motion: reduce) {
.infographic-iam-groups-ex *, .infographic-iam-groups-ex *::before, .infographic-iam-groups-ex *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="scenario-row">
<button class="scenario-btn" data-scenario="launch">π Launch a server</button>
<button class="scenario-btn" data-scenario="billing">π³ View the AWS bill</button>
<button class="scenario-btn" data-scenario="deleteuser">ποΈ Delete an IAM user</button>
</div>
<div class="compare">
<div class="compare-col" data-group="admins">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-id">admin-user</div>
<div class="group-name">Admins</div>
<div class="group-desc">Full control of AWS resources</div>
<div class="result-badge">β</div>
</div>
<div class="compare-col" data-group="developers">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-id">dev-user</div>
<div class="group-name">Developers</div>
<div class="group-desc">Manage application resources</div>
<div class="result-badge">β</div>
</div>
<div class="compare-col" data-group="finance">
<svg class="icon-person" viewBox="0 0 48 48"><circle cx="24" cy="14" r="9"/><path d="M6 44c0-11 8-18 18-18s18 7 18 18"/></svg>
<div class="user-id">billing-user</div>
<div class="group-name">Finance</div>
<div class="group-desc">View billing and cost data only</div>
<div class="result-badge">β</div>
</div>
</div>
<div class="caption">Permissions come from group membership, not a shared root account β every user still logs in with their own credentials.</div>
<script>
(function () {
var wrapper = document.currentScript.closest('.infographic-iam-groups-ex')
|| document.querySelector('.infographic-iam-groups-ex');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
var results = {
launch: { admins: true, developers: true, finance: false },
billing: { admins: true, developers: false, finance: true },
deleteuser: { admins: true, developers: false, finance: false }
};
var scenarioBtns = wrapper.querySelectorAll('.scenario-btn');
var cols = wrapper.querySelectorAll('.compare-col');
scenarioBtns.forEach(function (btn) {
btn.addEventListener('click', function () {
var scenario = btn.dataset.scenario;
wrapper.setAttribute('data-scenario', scenario);
scenarioBtns.forEach(function (b) { b.classList.toggle('is-active', b === btn); });
cols.forEach(function (col) {
var group = col.dataset.group;
var allowed = results[scenario][group];
var badge = col.querySelector('.result-badge');
badge.classList.remove('allowed', 'denied');
badge.textContent = allowed ? 'β
Allowed' : 'β Denied';
requestAnimationFrame(function () {
badge.classList.add(allowed ? 'allowed' : 'denied');
});
});
});
});
})();
</script>
</div>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});π IAM Roles
Roles are temporary identities that users, apps, or AWS services can assume to gain permissions β no long-term credentials involved.
π§ Key Rules:
Roles have no permanent password or access keys
Any trusted identity (user, app, or AWS service) can assume a role
AWS STS (Security Token Service) issues temporary credentials that expire automatically
One role can be assumed by many different identities, one at a time each
<div class="infographic-iam-roles-anim" id="rolesAnim">
<style>
.infographic-iam-roles-anim {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--ok: #16a34a;
--ok-soft: #e3f5e9;
--error: #c0392b;
--error-soft: #fbe9e7;
--radius: 10px;
--body-font: 'Inter', -apple-system, sans-serif;
--mono-font: 'IBM Plex Mono', 'SFMono-Regular', Menlo, monospace;
background: var(--paper-alt);
border-radius: var(--radius);
font-family: var(--body-font);
max-width: 720px;
margin: 0 auto;
padding: clamp(1rem, 4vw, 2rem);
box-sizing: border-box;
}
.infographic-iam-roles-anim[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #13171F;
--paper-alt: #1c212c;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--ok: #4ade80;
--ok-soft: #1a3324;
--error: #e57373;
--error-soft: #3a2222;
}
.infographic-iam-roles-anim * { box-sizing: border-box; }
.infographic-iam-roles-anim svg { width: 100%; height: auto; display: block; overflow: visible; }
.infographic-iam-roles-anim .hint {
text-align: center; font-size: 0.82rem; color: var(--ink-soft);
margin: 0.75rem 0 1.1rem; min-height: 1.4em;
}
.infographic-iam-roles-anim .hint strong { color: var(--ink); }
.infographic-iam-roles-anim .btn-row {
display: flex; justify-content: center; gap: 0.75rem; flex-wrap: wrap;
}
.infographic-iam-roles-anim .req-btn {
font: inherit; font-size: 0.85rem; font-weight: 600; border-radius: var(--radius);
padding: 0.6rem 1.1rem; cursor: pointer; background: var(--paper);
border: 1px solid var(--accent); color: var(--accent); transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.infographic-iam-roles-anim .req-btn:hover { background: var(--accent-soft); }
.infographic-iam-roles-anim .req-btn.is-expired-state {
border-color: var(--error); color: var(--error);
}
.infographic-iam-roles-anim .req-btn.is-expired-state:hover { background: var(--error-soft); }
.infographic-iam-roles-anim .connector {
stroke: var(--line); stroke-width: 3; stroke-dasharray: 6 8; fill: none;
transition: stroke 0.3s ease, opacity 0.3s ease;
}
.infographic-iam-roles-anim .connector.is-active { stroke: var(--accent); opacity: 1; }
.infographic-iam-roles-anim .role-circle {
fill: var(--paper); stroke: var(--accent); stroke-width: 4;
transform-box: fill-box; transform-origin: center;
animation: rolePulse 3s ease-in-out infinite;
transition: stroke 0.3s ease;
}
.infographic-iam-roles-anim .role-circle.is-hit { stroke: var(--ok); }
.infographic-iam-roles-anim .service-circle {
fill: var(--paper); stroke: var(--line); stroke-width: 3;
transform-box: fill-box; transform-origin: center;
transition: stroke 0.3s ease, transform 0.3s ease;
}
.infographic-iam-roles-anim .service-circle.is-hit {
stroke: var(--ok); transform: scale(1.08);
}
.infographic-iam-roles-anim .identity-circle {
fill: var(--paper); stroke: var(--line); stroke-width: 3;
transform-box: fill-box; transform-origin: center;
transition: stroke 0.3s ease, transform 0.3s ease, filter 0.3s ease;
}
.infographic-iam-roles-anim .identity-circle.is-valid-hit {
stroke: var(--ok); transform: scale(1.08); filter: brightness(1.1);
}
.infographic-iam-roles-anim .identity-circle.is-expired {
stroke: var(--error);
}
.infographic-iam-roles-anim .identity-circle.is-denied-shake {
animation: shakeX 0.4s ease;
}
@keyframes shakeX {
0%, 100% { transform: translateX(0); }
20% { transform: translateX(-5px); }
40% { transform: translateX(5px); }
60% { transform: translateX(-4px); }
80% { transform: translateX(4px); }
}
.infographic-iam-roles-anim .node-emoji { font-size: 34px; text-anchor: middle; dominant-baseline: central; }
.infographic-iam-roles-anim .node-label { font-size: 16px; font-weight: 700; fill: var(--ink); text-anchor: middle; }
.infographic-iam-roles-anim .timer-text {
font-family: var(--mono-font); font-size: 15px; font-weight: 700;
text-anchor: middle; fill: var(--accent);
transition: fill 0.25s ease;
}
.infographic-iam-roles-anim .timer-text.is-expired { fill: var(--error); }
.infographic-iam-roles-anim .req-dot { opacity: 0; }
@keyframes rolePulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.035); }
}
@media (prefers-reduced-motion: reduce) {
.infographic-iam-roles-anim .role-circle { animation: none; }
.infographic-iam-roles-anim .identity-circle,
.infographic-iam-roles-anim .service-circle,
.infographic-iam-roles-anim .connector,
.infographic-iam-roles-anim .timer-text { transition: none; }
.infographic-iam-roles-anim .identity-circle.is-denied-shake { animation: none; }
}
</style>
<svg viewBox="0 0 560 540" xmlns="http://www.w3.org/2000/svg">
<line class="connector" id="lineUser" x1="280" y1="220" x2="110" y2="150"/>
<line class="connector" id="lineApp" x1="280" y1="220" x2="450" y2="150"/>
<line class="connector" id="lineService" x1="280" y1="220" x2="280" y2="410"/>
<circle class="role-circle" id="roleCircle" cx="280" cy="220" r="58"/>
<text class="node-emoji" x="280" y="216">π</text>
<text class="node-label" x="280" y="304">Role</text>
<g id="nodeUser">
<text class="timer-text" id="timerUser" x="110" y="88">β</text>
<circle class="identity-circle" cx="110" cy="150" r="44"/>
<text class="node-emoji" x="110" y="146">π§</text>
<text class="node-label" x="110" y="220">User</text>
</g>
<g id="nodeApp">
<text class="timer-text" id="timerApp" x="450" y="88">β</text>
<circle class="identity-circle" cx="450" cy="150" r="44"/>
<text class="node-emoji" x="450" y="146">π»</text>
<text class="node-label" x="450" y="220">App</text>
</g>
<g id="nodeService">
<circle class="service-circle" cx="280" cy="410" r="44"/>
<text class="node-emoji" x="280" y="406">ποΈ</text>
<text class="node-label" x="280" y="480">Service</text>
</g>
<circle class="req-dot" id="dotUser" r="7" fill="var(--accent)"/>
<circle class="req-dot" id="dotApp" r="7" fill="var(--accent)"/>
</svg>
<div class="hint" id="hintText">Press a button below to assume the role and start sending requests.</div>
<div class="btn-row">
<button class="req-btn" id="sendUser">π§ User: Send Request</button>
<button class="req-btn" id="sendApp">π» App: Send Message</button>
</div>
<script>
(function () {
var wrapper = document.currentScript.closest('.infographic-iam-roles-anim')
|| document.querySelector('.infographic-iam-roles-anim');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
var ACTIVE_SECONDS = 8;
var keys = ['user', 'app'];
var names = { user: 'User', app: 'App' };
var pos = {
user: { x: 110, y: 150 },
app: { x: 450, y: 150 },
role: { x: 280, y: 220 },
service: { x: 280, y: 410 }
};
function capitalize(s) { return s.charAt(0).toUpperCase() + s.slice(1); }
var nodes = {};
keys.forEach(function (k) {
nodes[k] = {
circle: wrapper.querySelector('#node' + capitalize(k) + ' .identity-circle'),
timer: wrapper.querySelector('#timer' + capitalize(k)),
line: wrapper.querySelector('#line' + capitalize(k)),
dot: wrapper.querySelector('#dot' + capitalize(k)),
sendBtn: wrapper.querySelector('#send' + capitalize(k)),
remaining: ACTIVE_SECONDS,
valid: false,
running: false,
busy: false,
tickId: null,
reqId: null
};
});
var roleCircle = wrapper.querySelector('#roleCircle');
var serviceCircle = wrapper.querySelector('.service-circle');
var hint = wrapper.querySelector('#hintText');
function lerp(a, b, t) { return a + (b - a) * t; }
function moveDot(dot, from, to, duration, cb) {
var start = null;
function step(ts) {
if (!start) start = ts;
var t = Math.min((ts - start) / duration, 1);
dot.setAttribute('cx', lerp(from.x, to.x, t));
dot.setAttribute('cy', lerp(from.y, to.y, t));
if (t < 1) requestAnimationFrame(step);
else if (cb) cb();
}
requestAnimationFrame(step);
}
function flashRole() {
roleCircle.classList.add('is-hit');
setTimeout(function () { roleCircle.classList.remove('is-hit'); }, 280);
}
function flashService() {
serviceCircle.classList.add('is-hit');
setTimeout(function () { serviceCircle.classList.remove('is-hit'); }, 280);
}
function flashIdentityOk(k) {
nodes[k].circle.classList.add('is-valid-hit');
setTimeout(function () { nodes[k].circle.classList.remove('is-valid-hit'); }, 280);
}
function shakeIdentity(k) {
nodes[k].circle.classList.add('is-denied-shake');
setTimeout(function () { nodes[k].circle.classList.remove('is-denied-shake'); }, 400);
}
function updateTimerDisplay(k) {
var n = nodes[k];
if (!n.running && n.valid === false && n.remaining === ACTIVE_SECONDS) {
n.timer.textContent = 'β';
n.timer.classList.remove('is-expired');
return;
}
n.timer.textContent = n.valid ? n.remaining + 's' : 'expired';
n.timer.classList.toggle('is-expired', !n.valid);
}
function updateButton(k) {
var n = nodes[k];
n.sendBtn.classList.toggle('is-expired-state', n.remaining === 0 && !n.running);
}
function sendPulse(k) {
var n = nodes[k];
if (n.busy || !n.valid) return;
n.busy = true;
n.line.classList.add('is-active');
var dot = n.dot;
dot.setAttribute('fill', 'var(--accent)');
dot.setAttribute('r', 7);
dot.setAttribute('cx', pos[k].x);
dot.setAttribute('cy', pos[k].y);
dot.style.opacity = 1;
hint.innerHTML = '\u27A1\uFE0F <strong>' + names[k] + '</strong> \u2192 Role \u2192 Service';
moveDot(dot, pos[k], pos.role, 380, function () {
flashRole();
moveDot(dot, pos.role, pos.service, 380, function () {
flashService();
hint.innerHTML = '\u2705 Request served \u2014 sending response back to <strong>' + names[k] + '</strong>';
moveDot(dot, pos.service, pos.role, 380, function () {
flashRole();
moveDot(dot, pos.role, pos[k], 380, function () {
dot.style.opacity = 0;
n.line.classList.remove('is-active');
flashIdentityOk(k);
n.busy = false;
if (n.valid) {
hint.innerHTML = '\u2705 <strong>' + names[k] + '</strong>\u2019s request completed successfully.';
}
});
});
});
});
}
function sendDeny(k) {
var n = nodes[k];
var dot = n.dot;
var mid = { x: lerp(pos[k].x, pos.role.x, 0.3), y: lerp(pos[k].y, pos.role.y, 0.3) };
dot.setAttribute('fill', 'var(--error)');
dot.setAttribute('r', 7);
dot.setAttribute('cx', pos[k].x);
dot.setAttribute('cy', pos[k].y);
dot.style.opacity = 1;
hint.innerHTML = '\u23F1\uFE0F <strong>' + names[k] + '</strong>\u2019s credential expired \u2014 request denied \u274c. Session ended.';
moveDot(dot, pos[k], mid, 220, function () {
dot.setAttribute('cx', pos[k].x);
dot.setAttribute('cy', pos[k].y);
shakeIdentity(k);
setTimeout(function () { dot.style.opacity = 0; }, 350);
});
}
function stopSession(k) {
var n = nodes[k];
if (n.tickId) { clearInterval(n.tickId); n.tickId = null; }
if (n.reqId) { clearInterval(n.reqId); n.reqId = null; }
n.running = false;
}
function startSession(k) {
var n = nodes[k];
stopSession(k);
n.remaining = ACTIVE_SECONDS;
n.valid = true;
n.running = true;
n.circle.classList.remove('is-expired');
updateTimerDisplay(k);
updateButton(k);
sendPulse(k);
n.reqId = setInterval(function () { sendPulse(k); }, 1500);
n.tickId = setInterval(function () {
n.remaining -= 1;
if (n.remaining > 0) {
updateTimerDisplay(k);
} else {
n.remaining = 0;
n.valid = false;
n.circle.classList.add('is-expired');
updateTimerDisplay(k);
stopSession(k);
sendDeny(k);
updateButton(k);
}
}, 1000);
}
keys.forEach(function (k) {
updateTimerDisplay(k);
updateButton(k);
nodes[k].sendBtn.addEventListener('click', function () { startSession(k); });
});
})();
</script>
</div>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});π IAM Permissions & Policies
IAM permissions are defined using policies.
A policy is a JSON document that defines:
Actions β What can be done
Resources β What the actions apply to
Effect β Allow or Deny
Conditions β Optional rules for access
Policies can be attached to: IAM Users, IAM Groups, IAM Roles
π Example Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:Describe*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"cloudwatch:ListMetrics",
"cloudwatch:GetMetricStatistics",
"cloudwatch:Describe*"
],
"Resource": "*"
}
]
}Version β Policy language version.
Effect: Allow β Grants permission.
Action β Allowed operations (EC2 and CloudWatch read-only).
Resource: "*" β Applies to all resources.
Give users only the permissions they needβnothing more.
Example: If a user only works with EC2 and S3, grant access only to those services. This improves security and reduces the risk of accidental changes.
β Remember
IAM is not just about giving access β itβs about giving the right access, to the right entity, at the right time, and nothing more.
By properly structuring users, groups, roles, and policies, you build a secure, scalable, and professionally managed AWS environment.