assets/diagrams/01-identity-resource-plane.html
Original HTML source
<!doctype html>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Identity Plane vs Resource Plane</title>
<style>
  :root {
    color-scheme: light dark;
    --bg: #f8fafc;
    --fg: #172033;
    --muted: #5b6475;
    --line: #64748b;
    --neutral: #e2e8f0;
    --input: #bfdbfe;
    --process: #c7d2fe;
    --storage: #99f6e4;
    --external: #fde68a;
    --risk: #fecaca;
  }
  @media (prefers-color-scheme: dark) {
    :root {
      --bg: #0f172a;
      --fg: #e5e7eb;
      --muted: #a3adbd;
      --line: #94a3b8;
      --neutral: #334155;
      --input: #1d4ed8;
      --process: #4338ca;
      --storage: #0f766e;
      --external: #92400e;
      --risk: #991b1b;
    }
  }
  body {
    margin: 0;
    background: var(--bg);
    color: var(--fg);
    font: 14px/1.4 ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  }
  main {
    max-width: 980px;
    margin: 32px auto;
    padding: 0 20px;
  }
  svg {
    width: 100%;
    height: auto;
    display: block;
  }
  .title {
    font-size: 22px;
    font-weight: 700;
    fill: var(--fg);
  }
  .zone-label {
    font-size: 16px;
    font-weight: 700;
    fill: var(--fg);
  }
  .label {
    font-size: 14px;
    font-weight: 650;
    fill: var(--fg);
  }
  .small {
    font-size: 12px;
    fill: var(--muted);
  }
  .node {
    stroke: var(--line);
    stroke-width: 1;
  }
  .neutral {
    fill: var(--neutral);
  }
  .input {
    fill: var(--input);
  }
  .process {
    fill: var(--process);
  }
  .storage {
    fill: var(--storage);
  }
  .external {
    fill: var(--external);
  }
  .edge {
    stroke: var(--line);
    stroke-width: 1.6;
    fill: none;
  }
  .zone {
    fill: none;
    stroke: var(--line);
    stroke-width: 1.2;
    stroke-dasharray: 7 6;
    opacity: 0.9;
  }
</style>
<main>
  <svg viewBox="0 0 980 560" role="img" aria-labelledby="title desc">
    <title id="title">Identity Plane vs Resource Plane</title>
    <desc id="desc">Entra tenant objects issue tokens that are used against Microsoft Graph or Azure Resource Manager. Azure resources have separate management and data access boundaries.</desc>
    <defs>
      <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
        <path d="M 0 0 L 10 5 L 0 10 z" fill="var(--line)"></path>
      </marker>
    </defs>

    <text class="title" x="32" y="42">Identity Plane vs Resource Plane</text>
    <text class="small" x="32" y="66">Teaching point: Entra directory, tokens, Azure management, and resource data are related but not the same boundary.</text>

    <path class="edge" fill="none" marker-end="url(#arrow)" d="M 326 180 C 400 180 400 180 474 180"></path>
    <path class="edge" fill="none" marker-end="url(#arrow)" d="M 326 270 C 400 270 400 260 474 244"></path>
    <path class="edge" fill="none" marker-end="url(#arrow)" d="M 650 234 C 710 250 720 270 760 302"></path>
    <path class="edge" fill="none" marker-end="url(#arrow)" d="M 650 234 C 710 216 720 198 760 166"></path>
    <path class="edge" fill="none" marker-end="url(#arrow)" d="M 836 210 C 836 238 836 250 836 280"></path>

    <rect class="zone" x="24" y="92" width="380" height="332" rx="14"></rect>
    <text class="zone-label" x="48" y="126">Identity plane: Entra ID</text>
    <text class="small" x="48" y="148">Directory objects and token issuance decisions</text>

    <rect class="node input" x="58" y="164" width="122" height="70" rx="9"></rect>
    <text class="label" x="82" y="194">User / Group</text>
    <text class="small" x="82" y="214">who signs in</text>

    <rect class="node process" x="204" y="164" width="122" height="70" rx="9"></rect>
    <text class="label" x="232" y="194">Role</text>
    <text class="small" x="232" y="214">admin context</text>

    <rect class="node process" x="58" y="254" width="122" height="70" rx="9"></rect>
    <text class="label" x="82" y="284">Application</text>
    <text class="small" x="82" y="304">app definition</text>

    <rect class="node process" x="204" y="254" width="122" height="70" rx="9"></rect>
    <text class="label" x="226" y="284">Service</text>
    <text class="label" x="226" y="302">Principal</text>

    <rect class="node external" x="150" y="344" width="116" height="52" rx="9"></rect>
    <text class="label" x="177" y="375">Token</text>

    <rect class="zone" x="456" y="92" width="208" height="216" rx="14"></rect>
    <text class="zone-label" x="482" y="126">API boundary</text>
    <text class="small" x="482" y="148">Audience decides who accepts token</text>

    <rect class="node neutral" x="494" y="166" width="136" height="62" rx="9"></rect>
    <text class="label" x="522" y="193">Microsoft</text>
    <text class="label" x="522" y="211">Graph</text>

    <rect class="node neutral" x="494" y="246" width="136" height="62" rx="9"></rect>
    <text class="label" x="520" y="274">Azure ARM</text>
    <text class="small" x="520" y="294">management API</text>

    <rect class="zone" x="720" y="92" width="236" height="332" rx="14"></rect>
    <text class="zone-label" x="746" y="126">Resource plane: Azure</text>
    <text class="small" x="746" y="148">Scopes, providers, and data access</text>

    <rect class="node storage" x="764" y="166" width="144" height="70" rx="9"></rect>
    <text class="label" x="792" y="196">Subscription</text>
    <text class="small" x="792" y="216">RBAC scope</text>

    <rect class="node storage" x="764" y="282" width="144" height="70" rx="9"></rect>
    <text class="label" x="796" y="312">Resource</text>
    <text class="label" x="796" y="330">Group</text>

    <rect class="node storage" x="764" y="370" width="144" height="70" rx="9"></rect>
    <text class="label" x="808" y="400">Resource</text>
    <text class="small" x="808" y="420">data plane may differ</text>
  </svg>
</main>