<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: John  Ajera</title>
    <description>The latest articles on DEV Community by John  Ajera (@jajera).</description>
    <link>https://dev.to/jajera</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2461160%2Fefda9ea8-49f8-4d07-b358-445b8e7d5a20.png</url>
      <title>DEV Community: John  Ajera</title>
      <link>https://dev.to/jajera</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jajera"/>
    <language>en</language>
    <item>
      <title>Embed a public GA4 dashboard with Looker Studio</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sun, 27 Sep 2026 02:04:52 +0000</pubDate>
      <link>https://dev.to/jajera/embed-a-public-ga4-dashboard-with-looker-studio-g72</link>
      <guid>https://dev.to/jajera/embed-a-public-ga4-dashboard-with-looker-studio-g72</guid>
      <description>&lt;h2&gt;
  
  
  Embed a public GA4 dashboard with Looker Studio
&lt;/h2&gt;

&lt;p&gt;Goal: open &lt;code&gt;https://your.domain/stats&lt;/code&gt; and see three GA4 embeds (this year to date monthly, last 30 days daily, top URLs this week). One Looker Studio report per chart: three Public shares, three embed URLs. No Analytics login for viewers. No API keys.&lt;/p&gt;

&lt;p&gt;You need gtag already on the site (&lt;a href="https://dev.to/jajera/set-up-google-analytics-4-on-a-static-site-gtag-csp-and-amplify-60c"&gt;GA4 static site setup&lt;/a&gt;). Know your Measurement ID (&lt;code&gt;G-...&lt;/code&gt;).&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Browser signed into a Google account that can open your property in &lt;a href="https://analytics.google.com/" rel="noopener noreferrer"&gt;Google Analytics&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A folder you can deploy as the site (or add one HTML file to the host you already use).&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Create the Looker Studio reports
&lt;/h3&gt;

&lt;p&gt;Build &lt;strong&gt;three reports&lt;/strong&gt; (one chart each). Shared create steps once; then configure each report.&lt;/p&gt;

&lt;h4&gt;
  
  
  Shared setup (first report)
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://lookerstudio.google.com/" rel="noopener noreferrer"&gt;https://lookerstudio.google.com/&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt; → &lt;strong&gt;Report&lt;/strong&gt; (or &lt;strong&gt;Create a report&lt;/strong&gt; / &lt;strong&gt;+ Create new report&lt;/strong&gt; on the home page).&lt;/li&gt;
&lt;li&gt;First time only: complete &lt;strong&gt;account setup&lt;/strong&gt; (country, company if asked, agree to terms) → &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;Add data to report&lt;/strong&gt;, choose &lt;strong&gt;Google Analytics&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Authorize&lt;/strong&gt; if Data Studio asks to connect to your Analytics accounts.&lt;/li&gt;
&lt;li&gt;Pick the &lt;strong&gt;property&lt;/strong&gt; that matches your &lt;code&gt;G-...&lt;/code&gt; Measurement ID → &lt;strong&gt;Add&lt;/strong&gt; → &lt;strong&gt;Add to report&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your property is missing from the list, that Google account cannot see GA4. Use the account that owns the property and start this section again.&lt;/p&gt;

&lt;p&gt;For reports 2 and 3: &lt;strong&gt;Create&lt;/strong&gt; → &lt;strong&gt;Report&lt;/strong&gt; again. Reuse the same GA data source if Looker offers it; otherwise pick the same property.&lt;/p&gt;

&lt;h4&gt;
  
  
  Report A - &lt;code&gt;Site traffic - this year to date&lt;/code&gt;
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Rename &lt;strong&gt;Untitled Report&lt;/strong&gt; to &lt;code&gt;Site traffic - this year to date&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Keep one &lt;strong&gt;Bar chart&lt;/strong&gt; (&lt;strong&gt;Add a chart&lt;/strong&gt; → &lt;strong&gt;Bar chart&lt;/strong&gt; / &lt;strong&gt;Column chart&lt;/strong&gt;; delete other charts or tables). Monthly buckets read clearer as bars than a line.&lt;/li&gt;
&lt;li&gt;Select the chart. Under &lt;strong&gt;Setup&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dimension:&lt;/strong&gt; Date → granularity &lt;strong&gt;Year Month&lt;/strong&gt; (or pick &lt;strong&gt;Year month&lt;/strong&gt; as the X-axis / category dimension).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metric:&lt;/strong&gt; Sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default date range:&lt;/strong&gt; &lt;strong&gt;Custom&lt;/strong&gt; → &lt;strong&gt;This year to date&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Drag the chart to fill the canvas (full width and height, little empty margin).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share&lt;/strong&gt; menu → &lt;strong&gt;Invite people&lt;/strong&gt; → &lt;strong&gt;Public&lt;/strong&gt; → &lt;strong&gt;Viewer&lt;/strong&gt; → &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Report B - &lt;code&gt;Site traffic - last 30 days&lt;/code&gt;
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Rename &lt;strong&gt;Untitled Report&lt;/strong&gt; to &lt;code&gt;Site traffic - last 30 days&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Keep one &lt;strong&gt;Time series&lt;/strong&gt; (prefer this over a bar chart for ~30 days); delete extras. If you use a &lt;strong&gt;Bar chart&lt;/strong&gt;, under &lt;strong&gt;Setup&lt;/strong&gt; → &lt;strong&gt;Display&lt;/strong&gt; turn &lt;strong&gt;Top N&lt;/strong&gt; / &lt;strong&gt;Group the rest as "Others"&lt;/strong&gt; &lt;strong&gt;off&lt;/strong&gt;. Otherwise you only get 10 bars and a useless Others column.&lt;/li&gt;
&lt;li&gt;Select the chart. Under &lt;strong&gt;Setup&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dimension:&lt;/strong&gt; &lt;strong&gt;Date&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metric:&lt;/strong&gt; Sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default date range:&lt;/strong&gt; &lt;strong&gt;Custom&lt;/strong&gt; → &lt;strong&gt;Last 30 days&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Drag the chart to fill the canvas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share&lt;/strong&gt; → &lt;strong&gt;Invite people&lt;/strong&gt; → &lt;strong&gt;Public&lt;/strong&gt; → &lt;strong&gt;Viewer&lt;/strong&gt; → &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Report C - &lt;code&gt;Top URLs - this week&lt;/code&gt;
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Rename &lt;strong&gt;Untitled Report&lt;/strong&gt; to &lt;code&gt;Top URLs - this week&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Keep one &lt;strong&gt;Table&lt;/strong&gt; (&lt;strong&gt;Add a chart&lt;/strong&gt; → &lt;strong&gt;Table&lt;/strong&gt;; delete extras).&lt;/li&gt;
&lt;li&gt;Select the table. Under &lt;strong&gt;Setup&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dimension:&lt;/strong&gt; &lt;strong&gt;Page location&lt;/strong&gt; (full URL, so subdomains do not collapse into the same path).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metric:&lt;/strong&gt; Views.&lt;/li&gt;
&lt;li&gt;Sort by Views descending.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rows per page:&lt;/strong&gt; 10 (or &lt;strong&gt;Top N&lt;/strong&gt; = 10; leave &lt;strong&gt;Group the rest as "Others"&lt;/strong&gt; off).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default date range:&lt;/strong&gt; &lt;strong&gt;Custom&lt;/strong&gt; → &lt;strong&gt;Last 7 days&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Drag the table to fill the canvas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share&lt;/strong&gt; → &lt;strong&gt;Invite people&lt;/strong&gt; → &lt;strong&gt;Public&lt;/strong&gt; → &lt;strong&gt;Viewer&lt;/strong&gt; → &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note: if local preview uses the same &lt;code&gt;G-...&lt;/code&gt;, top rows may show &lt;code&gt;localhost&lt;/code&gt; or &lt;code&gt;127.0.0.1&lt;/code&gt;. Optional chart filter: &lt;strong&gt;Hostname&lt;/strong&gt; does not equal &lt;code&gt;localhost&lt;/code&gt; (add another for &lt;code&gt;127.0.0.1&lt;/code&gt; if needed). Skip if that traffic is not an issue for your site.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Get the embed URLs
&lt;/h3&gt;

&lt;p&gt;Do this &lt;strong&gt;for each report&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the report → &lt;strong&gt;Share&lt;/strong&gt; menu → &lt;strong&gt;Embed report&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Turn &lt;strong&gt;Enable embedding&lt;/strong&gt; on.&lt;/li&gt;
&lt;li&gt;Uncheck &lt;strong&gt;Show report navigation in embedded mode&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Leave &lt;strong&gt;Embed Code&lt;/strong&gt; selected (or &lt;strong&gt;Embed URL&lt;/strong&gt;; same link either way).&lt;/li&gt;
&lt;li&gt;Copy only the URL inside &lt;code&gt;src="..."&lt;/code&gt;. It must contain &lt;code&gt;/embed/reporting/&lt;/code&gt; (not plain &lt;code&gt;/reporting/&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://lookerstudio.google.com/embed/reporting/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/page/XXXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dialog may show &lt;code&gt;datastudio.google.com&lt;/code&gt; instead of &lt;code&gt;lookerstudio.google.com&lt;/code&gt;; either host is fine. You need three URLs for the next section.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Add &lt;code&gt;/stats&lt;/code&gt; on the site
&lt;/h3&gt;

&lt;p&gt;Create a file the host serves at &lt;code&gt;/stats&lt;/code&gt; (for a plain static host: &lt;code&gt;stats/index.html&lt;/code&gt; in the published root).&lt;/p&gt;

&lt;p&gt;Replace each &lt;code&gt;PASTE_EMBED_URL_*&lt;/code&gt; with the matching URL from section 3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"robots"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"noindex, nofollow"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Site traffic&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt;
      &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"PASTE_EMBED_URL_A"&lt;/span&gt;
      &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Site traffic - this year to date"&lt;/span&gt;
      &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:100%;height:60vh;border:0"&lt;/span&gt;
      &lt;span class="na"&gt;allowfullscreen&lt;/span&gt;
      &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt;
      &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"PASTE_EMBED_URL_B"&lt;/span&gt;
      &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Site traffic - last 30 days"&lt;/span&gt;
      &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:100%;height:60vh;border:0"&lt;/span&gt;
      &lt;span class="na"&gt;allowfullscreen&lt;/span&gt;
      &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt;
      &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"PASTE_EMBED_URL_C"&lt;/span&gt;
      &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Top URLs - this week"&lt;/span&gt;
      &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width:100%;height:60vh;border:0"&lt;/span&gt;
      &lt;span class="na"&gt;allowfullscreen&lt;/span&gt;
      &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not add &lt;code&gt;/stats&lt;/code&gt; to the site nav. Deploy (or refresh local preview).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSP:&lt;/strong&gt; if an iframe is blank and DevTools says blocked by Content-Security-Policy, add this and redeploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;frame-src https://lookerstudio.google.com https://datastudio.google.com;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Check it worked
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;https://your.domain/stats&lt;/code&gt; in a &lt;strong&gt;private/incognito&lt;/strong&gt; window (not signed into Google). You should see all three embeds.&lt;/p&gt;

&lt;p&gt;If one is missing or shows a sign-in wall, fix that report's share/embed (sections 2-3) and update its iframe &lt;code&gt;src&lt;/code&gt;. Later chart edits stay in Looker Studio; the site keeps the same URLs unless you change a report.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Troubleshooting
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you see&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sign-in wall on &lt;code&gt;/stats&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;That report: Share → Invite people → &lt;strong&gt;Public&lt;/strong&gt; → &lt;strong&gt;Viewer&lt;/strong&gt; → &lt;strong&gt;Save&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Empty chart&lt;/td&gt;
&lt;td&gt;Confirm &lt;code&gt;G-...&lt;/code&gt; on the live site; Realtime in Analytics; correct property. Avoid &lt;strong&gt;Last year&lt;/strong&gt; if the property is new (that is the previous calendar year, not a rolling year)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Top URLs show &lt;code&gt;localhost&lt;/code&gt; / &lt;code&gt;127.0.0.1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Optional: filter those hostnames out (see Report C note)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bar chart shows a huge &lt;strong&gt;Others&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Turn off &lt;strong&gt;Top N&lt;/strong&gt; / &lt;strong&gt;Group the rest as "Others"&lt;/strong&gt; (or use a time series)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iframe blocked&lt;/td&gt;
&lt;td&gt;Section 4 CSP &lt;code&gt;frame-src&lt;/code&gt;; redeploy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chart looks tiny&lt;/td&gt;
&lt;td&gt;In that report, resize the chart to fill the canvas&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  7. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://lookerstudio.google.com/" rel="noopener noreferrer"&gt;Looker Studio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.google.com/looker-studio/answer/7450241" rel="noopener noreferrer"&gt;Embed a report&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.google.com/looker-studio/answer/6371090" rel="noopener noreferrer"&gt;Share a report&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/jajera/set-up-google-analytics-4-on-a-static-site-gtag-csp-and-amplify-60c"&gt;Set up Google Analytics 4 on a static site&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googleanalytics</category>
      <category>ga4</category>
      <category>lookerstudio</category>
      <category>staticsite</category>
    </item>
    <item>
      <title>Connect your phone to Kiro Crew (Windows)</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sun, 20 Sep 2026 10:17:11 +0000</pubDate>
      <link>https://dev.to/jajera/connect-your-phone-to-kiro-crew-windows-9e3</link>
      <guid>https://dev.to/jajera/connect-your-phone-to-kiro-crew-windows-9e3</guid>
      <description>&lt;h2&gt;
  
  
  Connect your phone to Kiro Crew (Windows)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Part 2&lt;/strong&gt; of &lt;a href="https://dev.to/jajera/install-kiro-crew-on-windows-4lpn"&gt;Kiro Crew on Windows&lt;/a&gt; (install + CLI + Builder ID). The dashboard is &lt;code&gt;localhost:5476&lt;/code&gt;—your phone cannot open that. Crew’s official path uses &lt;strong&gt;Tailscale Serve&lt;/strong&gt; (HTTPS inside your private tailnet), guided from the UI.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;not&lt;/strong&gt; Telegram/Slack, and &lt;strong&gt;not&lt;/strong&gt; a public Cloudflare/ngrok tunnel. There is &lt;strong&gt;no&lt;/strong&gt; Kiro Crew app on iPhone or Android.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;PC: &lt;strong&gt;Connect your phone&lt;/strong&gt; → &lt;strong&gt;Finish setup&lt;/strong&gt; → install/sign in &lt;strong&gt;Tailscale&lt;/strong&gt; → enable &lt;strong&gt;HTTPS&lt;/strong&gt; if asked → &lt;strong&gt;Set up &amp;amp; show QR&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Phone: install &lt;strong&gt;Tailscale&lt;/strong&gt; → allow VPN → &lt;strong&gt;sign in&lt;/strong&gt; (same account)—not the Crew QR.&lt;/li&gt;
&lt;li&gt;With Tailscale connected on the phone, scan the &lt;strong&gt;Crew&lt;/strong&gt; QR (or open the token link).&lt;/li&gt;
&lt;li&gt;Keep the PC awake; laptop sleep = phone offline.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Part 1 done: Crew desktop up (&lt;strong&gt;All systems running&lt;/strong&gt; on Overview).&lt;/li&gt;
&lt;li&gt;Phone (iOS or Android) that can install Tailscale.&lt;/li&gt;
&lt;li&gt;Browser for Tailscale sign-in / admin when prompted.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Open Connect your phone
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Crew sidebar → &lt;strong&gt;Connect your phone&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7968h8t33pthv8u07nxl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7968h8t33pthv8u07nxl.png" alt="Kiro Crew Connect your phone entry in the UI" width="306" height="67"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dialog &lt;strong&gt;Use Kiro Crew on your phone&lt;/strong&gt;. If remote access is not ready: &lt;strong&gt;Remote access is not set up yet. Finish setup to get a scannable code.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff9lzds5vxkyx28tnz58w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff9lzds5vxkyx28tnz58w.png" alt="Kiro Crew phone dialog before remote access" width="649" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click that row (&lt;strong&gt;Finish setup&lt;/strong&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That only opens &lt;strong&gt;Settings → Overview&lt;/strong&gt;. It does not install Tailscale or show a QR yet. Crew’s dialog says “scan with your camera”; it does &lt;strong&gt;not&lt;/strong&gt; spell out Tailscale-on-phone—do that before the Crew QR (step 6).&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Install Tailscale on the PC
&lt;/h3&gt;

&lt;p&gt;Overview may show &lt;strong&gt;Setup needed&lt;/strong&gt; / &lt;strong&gt;Download Tailscale&lt;/strong&gt;. Use Crew’s button, or the official stable installer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pkgs.tailscale.com/stable/tailscale-setup-latest.exe" rel="noopener noreferrer"&gt;https://pkgs.tailscale.com/stable/tailscale-setup-latest.exe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefer the browser&lt;/strong&gt; (Save link / open the URL), or Windows &lt;code&gt;curl.exe&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;curl.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-L&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://pkgs.tailscale.com/stable/tailscale-setup-latest.exe"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\Downloads\tailscale-setup-latest.exe"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Start-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\Downloads\tailscale-setup-latest.exe"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Agree to the license → &lt;strong&gt;Install&lt;/strong&gt; (UAC).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo4gw2jk4fsk8ifi0u1g0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo4gw2jk4fsk8ifi0u1g0.png" alt="Tailscale Setup: agree to license and Install" width="578" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Installation Successfully Completed&lt;/strong&gt; → &lt;strong&gt;Close&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr3wrnknyqbmah66y2hsm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr3wrnknyqbmah66y2hsm.png" alt="Tailscale Setup: installation successfully completed" width="578" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Welcome to Tailscale&lt;/strong&gt; → &lt;strong&gt;Get Started&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fns9vi1ham2dkp9ew4e8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fns9vi1ham2dkp9ew4e8m.png" alt="Tailscale: Welcome to Tailscale, Get Started" width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Join your network&lt;/strong&gt; → &lt;strong&gt;Sign in to your network&lt;/strong&gt; (browser SSO). Same account you register with. Web &lt;strong&gt;Machines&lt;/strong&gt; stays empty until the PC app finishes sign-in—use the app, not &lt;strong&gt;Add first device&lt;/strong&gt; in the admin console.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpc6ow6kxovsbhxqo74rw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpc6ow6kxovsbhxqo74rw.png" alt="Tailscale: Join your network, Sign in" width="800" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browser &lt;strong&gt;Connect device&lt;/strong&gt; (PC name → your tailnet) → &lt;strong&gt;Connect&lt;/strong&gt;. (No screenshot—page shows account and hostname.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connect to your tailnet devices&lt;/strong&gt; → &lt;strong&gt;Close&lt;/strong&gt; (tray icon manages Tailscale day to day).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdvxttrpcyczovbuq8m9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdvxttrpcyczovbuq8m9s.png" alt="Tailscale: connected, manage via tray icon" width="776" height="710"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Confirm the PC under &lt;a href="https://login.tailscale.com/admin/machines" rel="noopener noreferrer"&gt;Machines&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tailscale &lt;strong&gt;Personal&lt;/strong&gt; is free for this (PC + phone = user devices). Prefer &lt;strong&gt;Serve&lt;/strong&gt; (private) over &lt;strong&gt;Funnel&lt;/strong&gt; (public).&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Enable HTTPS (one-time)
&lt;/h3&gt;

&lt;p&gt;MagicDNS is usually already on. HTTPS Certificates often need a one-time enable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://login.tailscale.com/admin/dns" rel="noopener noreferrer"&gt;DNS&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Confirm &lt;strong&gt;MagicDNS&lt;/strong&gt; is enabled (button reads &lt;strong&gt;Disable MagicDNS…&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTPS Certificates&lt;/strong&gt; → &lt;strong&gt;Enable HTTPS…&lt;/strong&gt; → &lt;strong&gt;Enable&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The dialog warns that cert names (e.g. &lt;code&gt;machine-name.tail….ts.net&lt;/code&gt;) go in a public Certificate Transparency ledger—normal for Let’s Encrypt; Serve still stays inside your tailnet.&lt;/li&gt;
&lt;li&gt;Crew Overview → &lt;strong&gt;Re-check&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  6. Set up &amp;amp; show QR (PC), then phone
&lt;/h3&gt;

&lt;p&gt;Overview card: &lt;strong&gt;Phone access · Setup needed&lt;/strong&gt; — allow access by this computer’s network name — &lt;strong&gt;Set up &amp;amp; show QR&lt;/strong&gt; / &lt;strong&gt;Re-check&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fagf4wpxeaknxmg0kgo5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fagf4wpxeaknxmg0kgo5f.png" alt="Kiro Crew Overview: Phone access Set up and show QR" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If HTTPS still blocks the card, finish §5 → &lt;strong&gt;Re-check&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up &amp;amp; show QR&lt;/strong&gt; once (Windows may need elevation for Tailscale Serve).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Crew enables Tailscale origin, restarts the Gateway, runs Serve (&lt;code&gt;443&lt;/code&gt; → local dashboard), and shows a short-lived sign-in QR. Do &lt;strong&gt;not&lt;/strong&gt; screenshot or publish that QR—it embeds a live session token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phone (iOS or Android)—Tailscale first, Crew QR second:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;strong&gt;Tailscale&lt;/strong&gt; (&lt;a href="https://apps.apple.com/app/tailscale/id1470499037" rel="noopener noreferrer"&gt;App Store&lt;/a&gt; / &lt;a href="https://play.google.com/store/apps/details?id=com.tailscale.ipn" rel="noopener noreferrer"&gt;Play Store&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Allow &lt;strong&gt;VPN&lt;/strong&gt; / VPN configuration when asked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sign in&lt;/strong&gt; with the &lt;strong&gt;same&lt;/strong&gt; account as the PC. Tailscale does &lt;strong&gt;not&lt;/strong&gt; use the Crew QR for this step.&lt;/li&gt;
&lt;li&gt;Leave Tailscale &lt;strong&gt;connected&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Scan the &lt;strong&gt;Crew&lt;/strong&gt; QR with the system Camera (or open the &lt;code&gt;https://…/?token=…&lt;/code&gt; link in the mobile browser). Mint a fresh QR if the old one expired.&lt;/li&gt;
&lt;li&gt;Dashboard loads. Optional: &lt;strong&gt;Add to Home Screen&lt;/strong&gt; (PWA).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do &lt;strong&gt;not&lt;/strong&gt; open the bare MagicDNS hostname without a token. &lt;strong&gt;Create sign-in link&lt;/strong&gt; in the first Crew dialog is a fallback after remote access works—it does not replace Tailscale.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. CLI alternative (desktop install)
&lt;/h3&gt;

&lt;p&gt;The Setup.exe build does &lt;strong&gt;not&lt;/strong&gt; put &lt;code&gt;kirocrew&lt;/code&gt; on PATH. Use the bundled CLI if the UI sticks (Admin PowerShell if Serve needs elevation):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$kc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;LOCALAPPDATA&lt;/span&gt;&lt;span class="s2"&gt;\Programs\KiroCrew\resources\backend-dist\kirocrew-backend\bin\kirocrew.cmd"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$kc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dashboard.tailscale.enabled&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$kc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$kc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tailnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$kc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$kc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tailnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;… enabled true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trust Tailscale Serve origin (once)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tailnet up&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Publish HTTPS → dashboard (URL alone has &lt;strong&gt;no&lt;/strong&gt; session)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prints &lt;code&gt;https://…/?token=…&lt;/code&gt; for the phone&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;&amp;amp; $kc tailnet down&lt;/code&gt; stops publishing without clearing the enabled flag.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. Sessions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;Typical duration&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;QR / link click window&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;td&gt;Mint again if stale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access cookie&lt;/td&gt;
&lt;td&gt;Up to ~20h&lt;/td&gt;
&lt;td&gt;Short-lived access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refresh cookie&lt;/td&gt;
&lt;td&gt;~30 days idle&lt;/td&gt;
&lt;td&gt;Phone can stay signed in&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Config: &lt;code&gt;%USERPROFILE%\.kiro\crew\&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  9. Summary
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Part 1: Crew + kiro-cli on Windows
1. Connect your phone → Finish setup → Tailscale on PC → Connect
1. Admin DNS: Enable HTTPS → Re-check
1. Set up &amp;amp; show QR
1. Phone: Tailscale + VPN + same account → then scan Crew QR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;curl.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-L&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://pkgs.tailscale.com/stable/tailscale-setup-latest.exe"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\Downloads\tailscale-setup-latest.exe"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Start-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\Downloads\tailscale-setup-latest.exe"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  10. Troubleshooting
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Finish setup “does nothing”&lt;/td&gt;
&lt;td&gt;It only opens Overview—install Tailscale next&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Failed to fetch&lt;/strong&gt; / gateway stuck on :5476&lt;/td&gt;
&lt;td&gt;Restart PC; confirm Overview loads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;kirocrew&lt;/code&gt; not found in PowerShell&lt;/td&gt;
&lt;td&gt;Expected for Setup.exe—use bundled &lt;code&gt;kirocrew.cmd&lt;/code&gt; path in §7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Card stays &lt;strong&gt;Setup needed&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;PC Tailscale signed in; HTTPS enabled; &lt;strong&gt;Re-check&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crew QR scan does nothing&lt;/td&gt;
&lt;td&gt;Tailscale connected on phone first; use Camera/Safari/Chrome; mint a fresh QR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phone login screen only&lt;/td&gt;
&lt;td&gt;Open token/QR URL, not bare hostname&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serve permission error&lt;/td&gt;
&lt;td&gt;Admin PowerShell for &lt;code&gt;tailnet up&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Laptop sleep&lt;/td&gt;
&lt;td&gt;Keep PC awake or use an always-on host later&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  11. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/jajera/install-kiro-crew-on-windows-4lpn"&gt;Install Kiro Crew on Windows&lt;/a&gt; (Part 1)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kiro.dev/docs/crew/interfaces/" rel="noopener noreferrer"&gt;Kiro Crew – Interfaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kirodotdev/KiroCrew/blob/main/docs/guides/remote-and-mobile.md" rel="noopener noreferrer"&gt;Remote and mobile (repo guide)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pkgs.tailscale.com/stable/tailscale-setup-latest.exe" rel="noopener noreferrer"&gt;Tailscale Windows setup (latest)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailscale.com/kb/1242/tailscale-serve" rel="noopener noreferrer"&gt;Tailscale Serve&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailscale.com/kb/1153/enabling-https" rel="noopener noreferrer"&gt;Tailscale HTTPS certificates&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>kiro</category>
      <category>crew</category>
      <category>windows</category>
    </item>
    <item>
      <title>Install Kiro Crew on Windows</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sun, 20 Sep 2026 05:38:56 +0000</pubDate>
      <link>https://dev.to/jajera/install-kiro-crew-on-windows-4lpn</link>
      <guid>https://dev.to/jajera/install-kiro-crew-on-windows-4lpn</guid>
      <description>&lt;h2&gt;
  
  
  Install Kiro Crew on Windows
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Part 1&lt;/strong&gt; of &lt;strong&gt;Kiro Crew on Windows&lt;/strong&gt;. &lt;strong&gt;Kiro Crew&lt;/strong&gt; is a local agent Gateway + dashboard. It talks to the LLM through &lt;strong&gt;Kiro CLI&lt;/strong&gt; (ACP). This is &lt;strong&gt;not&lt;/strong&gt; the Kiro IDE.&lt;/p&gt;

&lt;p&gt;Next: connect the dashboard from your phone with Tailscale (&lt;strong&gt;Part 2&lt;/strong&gt; of this series).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feeioi09q7jf80agrormz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feeioi09q7jf80agrormz.png" alt="Kiro Crew Gateway talks to kiro-cli over ACP; Crew data lives under the user profile" width="799" height="383"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;strong&gt;KiroCrew-Setup.exe&lt;/strong&gt; (Gateway bundled; no Python needed).&lt;/li&gt;
&lt;li&gt;First launch: install &lt;strong&gt;Kiro CLI&lt;/strong&gt;, then &lt;code&gt;kiro-cli login&lt;/code&gt; (Builder ID in this guide).&lt;/li&gt;
&lt;li&gt;State: &lt;code&gt;%USERPROFILE%\.kiro\crew\&lt;/code&gt; (survives uninstall).&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Windows 10/11 x64&lt;/li&gt;
&lt;li&gt;Browser for Kiro sign-in&lt;/li&gt;
&lt;li&gt;Network (installer + ~610 MB embedding model on first start)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Download
&lt;/h3&gt;

&lt;p&gt;Direct stable installer (~260 MB, always latest):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://download.crew.kiro.dev/desktop/stable/latest/KiroCrew-Setup.exe" rel="noopener noreferrer"&gt;https://download.crew.kiro.dev/desktop/stable/latest/KiroCrew-Setup.exe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefer the browser&lt;/strong&gt; (Save link / open the URL)—usually faster than PowerShell.&lt;/p&gt;

&lt;p&gt;CLI: use Windows &lt;code&gt;curl.exe&lt;/code&gt;, not &lt;code&gt;Invoke-WebRequest&lt;/code&gt; (IWR is slow on large files):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;curl.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-L&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://download.crew.kiro.dev/desktop/stable/latest/KiroCrew-Setup.exe"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\Downloads\KiroCrew-Setup.exe"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Start-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\Downloads\KiroCrew-Setup.exe"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Install
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;KiroCrew-Setup.exe&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;SmartScreen: &lt;strong&gt;More info → Run anyway&lt;/strong&gt; only for this official signed build.&lt;/li&gt;
&lt;li&gt;Welcome → &lt;strong&gt;Next&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faxhqmj4lbkky6hl8bssg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faxhqmj4lbkky6hl8bssg.png" alt="KiroCrew Setup welcome screen" width="793" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose &lt;strong&gt;Only for me&lt;/strong&gt; (default, no UAC) or &lt;strong&gt;Anyone who uses this computer&lt;/strong&gt; → &lt;strong&gt;Install&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9p3id718b3sene286eeu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9p3id718b3sene286eeu.png" alt="KiroCrew Setup choose installation options" width="795" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Wait for install to finish.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fosydxma8g2kh7m7m1mx9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fosydxma8g2kh7m7m1mx9.png" alt="KiroCrew Setup installing progress" width="796" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Leave &lt;strong&gt;Run KiroCrew&lt;/strong&gt; checked → &lt;strong&gt;Finish&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftdo7fdc8zucgb6hjxh1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftdo7fdc8zucgb6hjxh1j.png" alt="KiroCrew Setup completing with Run KiroCrew checked" width="796" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Uninstall: &lt;strong&gt;Settings → Apps → Kiro Crew&lt;/strong&gt;. Does not delete &lt;code&gt;%USERPROFILE%\.kiro\crew&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. First launch
&lt;/h3&gt;

&lt;p&gt;App starts the Gateway, then opens setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2bpm3fks27t36tn8nh4c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2bpm3fks27t36tn8nh4c.png" alt="Kiro Crew waiting for gateway" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw4h57azos3m64gvnl4ir.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw4h57azos3m64gvnl4ir.png" alt="Kiro Crew Set up Kiro: install CLI then sign in" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Leave Crew open. Install Kiro CLI in &lt;strong&gt;PowerShell&lt;/strong&gt;. Source of the one-liner: &lt;a href="https://kiro.dev/cli/" rel="noopener noreferrer"&gt;kiro.dev/cli&lt;/a&gt; (Crew’s &lt;strong&gt;Open Kiro CLI setup&lt;/strong&gt; only opens that page).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffafxsys3fqyc3ui7674k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffafxsys3fqyc3ui7674k.png" alt="Kiro CLI Windows install: Download CLI or irm install.ps1" width="800" height="114"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;irm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'https://cli.kiro.dev/install.ps1'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frvtqyyomx4wiw4ull4lu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frvtqyyomx4wiw4ull4lu.png" alt="Kiro CLI installer via irm install.ps1 succeeding in PowerShell" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crew shows &lt;strong&gt;Sign in to Kiro&lt;/strong&gt; (CLI &lt;strong&gt;Complete&lt;/strong&gt;). This guide uses &lt;strong&gt;Builder ID&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;kiro-cli&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff6boquhbfncx7b0qxo20.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff6boquhbfncx7b0qxo20.png" alt="Kiro Crew Sign in to Kiro required; use kiro-cli login for Builder ID" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fswep6mzh0jfr72pkepj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fswep6mzh0jfr72pkepj5.png" alt="kiro-cli login returns Logged in" width="530" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Back in Crew → &lt;strong&gt;Check again&lt;/strong&gt;. Continue past &lt;strong&gt;Privacy&lt;/strong&gt;, then &lt;strong&gt;Tell Kiro about you&lt;/strong&gt; → &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F35gzidncxcic9rqoqazz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F35gzidncxcic9rqoqazz.png" alt="Kiro Crew Privacy screen" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feh0b4ctyzltit0t80pou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feh0b4ctyzltit0t80pou.png" alt="Kiro Crew Tell Kiro about you onboarding" width="800" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dashboard opens (app UI or &lt;a href="http://localhost:5476" rel="noopener noreferrer"&gt;http://localhost:5476&lt;/a&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg9an3a3yh8lxxv30xiix.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg9an3a3yh8lxxv30xiix.png" alt="Kiro Crew dashboard Schedule view" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Summary: Copy-Paste
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;curl.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-L&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://download.crew.kiro.dev/desktop/stable/latest/KiroCrew-Setup.exe"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\Downloads\KiroCrew-Setup.exe"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Start-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\Downloads\KiroCrew-Setup.exe"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Setup → Only for me → Finish → Launch
1. irm 'https://cli.kiro.dev/install.ps1' | iex
1. kiro-cli login
1. Check again → Privacy → Tell Kiro about you → Continue
1. Chat (app or http://localhost:5476)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  7. Troubleshooting
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SmartScreen&lt;/td&gt;
&lt;td&gt;Official CDN link above; &lt;strong&gt;Run anyway&lt;/strong&gt; only for that signed build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI missing / no &lt;code&gt;acp&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Re-run `irm '&lt;a href="https://cli.kiro.dev/install.ps1" rel="noopener noreferrer"&gt;https://cli.kiro.dev/install.ps1&lt;/a&gt;' \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sign-in stuck&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;kiro-cli login&lt;/code&gt; in a new PowerShell window, then &lt;strong&gt;Check again&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uninstall left data&lt;/td&gt;
&lt;td&gt;Expected—delete &lt;code&gt;%USERPROFILE%\.kiro\crew&lt;/code&gt; manually&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  8. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kiro.dev/docs/crew/" rel="noopener noreferrer"&gt;Kiro Crew&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kiro.dev/docs/crew/installation/" rel="noopener noreferrer"&gt;Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://download.crew.kiro.dev/desktop/stable/latest/KiroCrew-Setup.exe" rel="noopener noreferrer"&gt;Stable Setup.exe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kiro.dev/cli/" rel="noopener noreferrer"&gt;Kiro CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kirodotdev/KiroCrew/releases" rel="noopener noreferrer"&gt;KiroCrew releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 2 of this series: Connect your phone to Kiro Crew (Windows)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>kiro</category>
      <category>crew</category>
      <category>windows</category>
    </item>
    <item>
      <title>Set up Google Analytics 4 on a static site (gtag, CSP, and Amplify)</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sat, 29 Aug 2026 08:02:37 +0000</pubDate>
      <link>https://dev.to/jajera/set-up-google-analytics-4-on-a-static-site-gtag-csp-and-amplify-60c</link>
      <guid>https://dev.to/jajera/set-up-google-analytics-4-on-a-static-site-gtag-csp-and-amplify-60c</guid>
      <description>&lt;h2&gt;
  
  
  Set up Google Analytics 4 on a static site (gtag, CSP, and Amplify)
&lt;/h2&gt;

&lt;p&gt;You want page views on a static personal site (for example an Amplify-hosted home page). &lt;strong&gt;Google Analytics 4 (GA4)&lt;/strong&gt; is free and not a Google Cloud product - you sign in with a normal Google account, create an &lt;strong&gt;Analytics account&lt;/strong&gt; and &lt;strong&gt;property&lt;/strong&gt;, then put a &lt;strong&gt;Measurement ID&lt;/strong&gt; (&lt;code&gt;G-...&lt;/code&gt;) on the site.&lt;/p&gt;

&lt;p&gt;This guide walks through console setup, a &lt;strong&gt;complete one-page smoke test&lt;/strong&gt; you can deploy as-is, and the Amplify &lt;strong&gt;Content-Security-Policy&lt;/strong&gt; gotcha that blocks fonts, theme toggles, and Analytics if you forget it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsmyckpxyq0qj27uf4b95.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsmyckpxyq0qj27uf4b95.png" alt="GA4 request path: browser to CloudFront to Amplify to Google Analytics" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Icons for &lt;strong&gt;Amplify&lt;/strong&gt; and &lt;strong&gt;CloudFront&lt;/strong&gt; come from &lt;a href="https://jajera.github.io/aws-icons" rel="noopener noreferrer"&gt;AWS Icons&lt;/a&gt; (&lt;a href="https://aws.amazon.com/architecture/icons/" rel="noopener noreferrer"&gt;AWS Architecture Icons&lt;/a&gt;; use per &lt;a href="https://aws.amazon.com/trademark-guidelines/" rel="noopener noreferrer"&gt;AWS Trademark Guidelines&lt;/a&gt;). GA4 is not a Google Cloud product, so there is no matching icon in &lt;a href="https://jajera.github.io/gcp-icons/" rel="noopener noreferrer"&gt;GCP Icons&lt;/a&gt; - the last hop is a plain &lt;strong&gt;GA4&lt;/strong&gt; label.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a GA4 &lt;strong&gt;account&lt;/strong&gt; (label only) and &lt;strong&gt;property&lt;/strong&gt; for your site.&lt;/li&gt;
&lt;li&gt;Choose data-sharing options (optional extras for Google).&lt;/li&gt;
&lt;li&gt;Pick business objective &lt;strong&gt;Understand web and/or app traffic&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;Web&lt;/strong&gt; data stream and copy the &lt;strong&gt;Measurement ID&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Deploy a &lt;strong&gt;full HTML smoke-test page&lt;/strong&gt; with &lt;code&gt;gtag&lt;/code&gt; (then wire production the same way).&lt;/li&gt;
&lt;li&gt;Allow Google Tag Manager, Analytics, and (if used) Google Fonts in your &lt;strong&gt;CSP&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Confirm with GA &lt;strong&gt;Realtime&lt;/strong&gt; or the tag &lt;strong&gt;Retest&lt;/strong&gt; UI.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A public HTTPS host you control (this walkthrough uses &lt;strong&gt;AWS Amplify&lt;/strong&gt; Hosting).&lt;/li&gt;
&lt;li&gt;A Google account with access to &lt;a href="https://analytics.google.com/" rel="noopener noreferrer"&gt;Google Analytics&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Ability to publish a static HTML file and edit Amplify &lt;strong&gt;custom headers&lt;/strong&gt; / CSP.&lt;/li&gt;
&lt;li&gt;Optional: Terraform or Console access if Amplify headers are managed as infra.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Create the GA4 account and property
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://analytics.google.com/" rel="noopener noreferrer"&gt;Google Analytics&lt;/a&gt; → &lt;strong&gt;Admin&lt;/strong&gt; → &lt;strong&gt;Create&lt;/strong&gt; → &lt;strong&gt;Account&lt;/strong&gt; (or start the guided setup).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account name&lt;/strong&gt; is only a label (for example &lt;code&gt;johna.kiwi&lt;/code&gt; or your name). It is &lt;strong&gt;not&lt;/strong&gt; a GCP project ID.&lt;/li&gt;
&lt;li&gt;Create a &lt;strong&gt;property&lt;/strong&gt; for the site (property name can match the domain).&lt;/li&gt;
&lt;li&gt;Complete &lt;strong&gt;business details&lt;/strong&gt; as prompted.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Google Analytics hierarchy
  -------------------------
  Account          "johna.kiwi"          &amp;lt;-- folder / org label
    Property       "johna.kiwi"          &amp;lt;-- one site (or app)
      Data stream  Web / johna.kiwi      &amp;lt;-- where G-XXXXXXXX lives
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GA sits next to Ads / Tag Manager / Search Console. You do &lt;strong&gt;not&lt;/strong&gt; need a Google Cloud billing account for basic page-view tracking.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Account data sharing settings
&lt;/h3&gt;

&lt;p&gt;You will see checkboxes such as:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Typical choice for a personal site&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Google products and services&lt;/td&gt;
&lt;td&gt;Off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modeling contributions and business insights&lt;/td&gt;
&lt;td&gt;Optional (on or off)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technical support&lt;/td&gt;
&lt;td&gt;Off unless you need Google support access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recommendations for your business&lt;/td&gt;
&lt;td&gt;Off&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Turning all of them off still allows basic hits, pages, and referrers. These controls are &lt;strong&gt;extra sharing with Google&lt;/strong&gt;, not a requirement to collect page views.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Business objectives
&lt;/h3&gt;

&lt;p&gt;Select &lt;strong&gt;Understand web and/or app traffic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Skip leads, sales, and engagement unless you care about those recommended report packs. You can change suggestions later; page views still work either way.&lt;/p&gt;

&lt;p&gt;Then &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Start collecting data: Web stream
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Choose platform &lt;strong&gt;Web&lt;/strong&gt; (not Android / iOS).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website URL:&lt;/strong&gt; your apex, for example &lt;code&gt;https://johna.kiwi&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stream name:&lt;/strong&gt; something clear, for example &lt;code&gt;johna.kiwi&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create the stream and copy the &lt;strong&gt;Measurement ID&lt;/strong&gt; (&lt;code&gt;G-XXXXXXXX&lt;/code&gt;).
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Stream URL says:     https://johna.kiwi
  Hits actually come:  wherever this exact G-XXXXXXXX script runs

  johna.kiwi          [tag]  --&amp;gt; counted
  staging.johna.kiwi  [   ]  --&amp;gt; not counted (unless you add the tag)
  labs / guides       [   ]  --&amp;gt; not counted (unless you add the tag)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Subdomains&lt;/strong&gt; are not auto-tracked from DNS alone. Same Measurement ID on each host = one property; different IDs = separate properties.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. Smoke-test page (full HTML)
&lt;/h3&gt;

&lt;p&gt;Do not start with a half snippet in an existing theme. Save this as &lt;code&gt;index.html&lt;/code&gt;, replace &lt;code&gt;G-XXXXXXXX&lt;/code&gt;, and deploy it (Amplify, S3 website, GitHub Pages, or any static host). Then open the live URL and check &lt;strong&gt;Realtime&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;GA4 smoke test&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Minimal page to verify Google Analytics 4 gtag."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Google tag (gtag.js) --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXX"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
      &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;dataLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G-XXXXXXXX&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;system-ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;GA4 smoke test&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
      If this page is public over HTTPS and CSP allows
      &lt;span class="nt"&gt;&amp;lt;code&amp;gt;&lt;/span&gt;googletagmanager.com&lt;span class="nt"&gt;&amp;lt;/code&amp;gt;&lt;/span&gt;, a hit should appear in
      Analytics Realtime within a minute.
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Measurement ID on this page: &lt;span class="nt"&gt;&amp;lt;code&amp;gt;&lt;/span&gt;G-XXXXXXXX&lt;span class="nt"&gt;&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is enough to prove the tag. After it works, copy the same &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; block into your real site's &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; (and &lt;code&gt;404.html&lt;/code&gt; if you want those hits).&lt;/p&gt;

&lt;h4&gt;
  
  
  7.1 Amplify: inject only on production
&lt;/h4&gt;

&lt;p&gt;For a real app like &lt;a href="https://johna.kiwi" rel="noopener noreferrer"&gt;johna.kiwi&lt;/a&gt;, keep the smoke-test pattern in spirit: put &lt;code&gt;gtag&lt;/code&gt; in HTML at &lt;strong&gt;build&lt;/strong&gt; time from an env var so staging stays clean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Amplify main branch env
  -----------------------
  GA_MEASUREMENT_ID = G-XXXXXXXX
  SITE_ENV          = production

  build step
  ----------
  if GA_MEASUREMENT_ID set  --&amp;gt;  write gtag into index.html / 404.html
  else                      --&amp;gt;  leave markers empty (staging / PR)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example branch env:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GA_MEASUREMENT_ID=G-XXXXXXXX
SITE_ENV=production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Local check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GA_MEASUREMENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;G-XXXXXXXX npm run build
&lt;span class="c"&gt;# confirm index.html / 404.html contain gtag and your G- id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unset the var and rebuild to clear markers for local preview.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. Content-Security-Policy (the usual failure mode)
&lt;/h3&gt;

&lt;p&gt;The HTML can be perfect and GA still says &lt;strong&gt;tag wasn't detected&lt;/strong&gt;. Strict Amplify CSP is usually why - and it also breaks Google Fonts and inline theme scripts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6n7h1qr1mj7utpo55i1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6n7h1qr1mj7utpo55i1g.png" alt="CSP compare: strict policy blocks gtag versus allow-list policy that works" width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your Amplify header still looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;default-src 'self'; style-src 'self'; font-src 'self'; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you will hit exactly the left-hand failures above. Use an allow-list closer to this (adjust to your needs):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;default-src 'self';
script-src 'self' 'unsafe-inline' https://www.googletagmanager.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https://www.google-analytics.com https://www.googletagmanager.com;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://*.google-analytics.com https://*.analytics.google.com https://www.googletagmanager.com;
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update Amplify &lt;strong&gt;App settings → Custom headers&lt;/strong&gt;, then &lt;strong&gt;redeploy&lt;/strong&gt; the branch so CloudFront picks up the new policy. Confirm with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; https://your.domain/ | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; content-security-policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you manage Amplify with Terraform, do &lt;strong&gt;not&lt;/strong&gt; put &lt;code&gt;custom_headers&lt;/code&gt; in &lt;code&gt;lifecycle.ignore_changes&lt;/code&gt; unless you intentionally manage CSP only in the Console. Ignoring headers for "YAML vs JSON drift" means CSP fixes never apply via CI/CD.&lt;/p&gt;




&lt;h3&gt;
  
  
  9. Verify
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Hard-refresh the live site (cache can keep an old CSP).&lt;/li&gt;
&lt;li&gt;DevTools → Network: &lt;code&gt;gtag/js?id=G-...&lt;/code&gt; should be &lt;strong&gt;200&lt;/strong&gt;, not blocked (blocked requests often show provisional headers).&lt;/li&gt;
&lt;li&gt;Analytics → &lt;strong&gt;Reports → Realtime&lt;/strong&gt;, or the setup &lt;strong&gt;Retest&lt;/strong&gt; button for your domain.&lt;/li&gt;
&lt;li&gt;If you use a theme toggle in inline JS, it should work once &lt;code&gt;script-src&lt;/code&gt; allows &lt;code&gt;'unsafe-inline'&lt;/code&gt; (or move scripts to same-origin &lt;code&gt;.js&lt;/code&gt; files).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It can take a short time for the first hit to show; Realtime is the quick check.&lt;/p&gt;




&lt;h3&gt;
  
  
  10. Summary: copy-paste
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Measurement ID (Amplify main env):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GA_MEASUREMENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;G-XXXXXXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Full smoke-test page:&lt;/strong&gt; use the &lt;code&gt;index.html&lt;/code&gt; in section 7 (replace every &lt;code&gt;G-XXXXXXXX&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Confirm CSP after deploy:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; https://your.domain/ | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; content-security-policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Optional: gtag-only fragment&lt;/strong&gt; for an existing layout's &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXX"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dataLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G-XXXXXXXX&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  11. Troubleshooting
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GA: tag wasn't detected&lt;/td&gt;
&lt;td&gt;CSP blocks googletagmanager or inline script&lt;/td&gt;
&lt;td&gt;Loosen CSP; redeploy; hard-refresh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fonts fail in Network&lt;/td&gt;
&lt;td&gt;CSP &lt;code&gt;style-src&lt;/code&gt; / &lt;code&gt;font-src&lt;/code&gt; self-only&lt;/td&gt;
&lt;td&gt;Allow fonts.googleapis.com and fonts.gstatic.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Theme / dark mode toggle dead&lt;/td&gt;
&lt;td&gt;Inline script blocked by CSP&lt;/td&gt;
&lt;td&gt;Allow &lt;code&gt;'unsafe-inline'&lt;/code&gt; under &lt;code&gt;script-src&lt;/code&gt;, or use an external JS file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tag in HTML but Retest fails&lt;/td&gt;
&lt;td&gt;Old CSP still on CloudFront&lt;/td&gt;
&lt;td&gt;Redeploy branch after header change; wait for cache miss&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Staging shows no gtag&lt;/td&gt;
&lt;td&gt;Env only on &lt;code&gt;main&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Expected if you inject only when &lt;code&gt;GA_MEASUREMENT_ID&lt;/code&gt; is set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subdomain traffic missing&lt;/td&gt;
&lt;td&gt;No tag on that host&lt;/td&gt;
&lt;td&gt;Add the same Measurement ID to that site's HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Smoke page works, real site fails&lt;/td&gt;
&lt;td&gt;Real site CSP stricter than smoke host&lt;/td&gt;
&lt;td&gt;Align custom headers; compare &lt;code&gt;curl -sI&lt;/code&gt; on both&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  12. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://analytics.google.com/" rel="noopener noreferrer"&gt;Google Analytics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.google.com/analytics/answer/9304153" rel="noopener noreferrer"&gt;Set up the Google tag (gtag.js)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/custom-headers.html" rel="noopener noreferrer"&gt;AWS Amplify custom headers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy" rel="noopener noreferrer"&gt;MDN Content-Security-Policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jajera.github.io/aws-icons" rel="noopener noreferrer"&gt;AWS Icons&lt;/a&gt; / &lt;a href="https://jajera.github.io/gcp-icons/" rel="noopener noreferrer"&gt;GCP Icons&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googleanalytics</category>
      <category>ga4</category>
      <category>aws</category>
      <category>amplify</category>
    </item>
    <item>
      <title>EKS Metrics: Amazon Managed Prometheus vs Self-Managed Prometheus</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sat, 23 May 2026 23:58:14 +0000</pubDate>
      <link>https://dev.to/jajera/eks-metrics-amazon-managed-prometheus-vs-self-managed-prometheus-1ihp</link>
      <guid>https://dev.to/jajera/eks-metrics-amazon-managed-prometheus-vs-self-managed-prometheus-1ihp</guid>
      <description>&lt;h2&gt;
  
  
  EKS Metrics: Amazon Managed Prometheus vs Self-Managed Prometheus
&lt;/h2&gt;

&lt;p&gt;Once your cluster is running workloads, you need a &lt;strong&gt;metrics backend&lt;/strong&gt;: something that &lt;strong&gt;scrapes&lt;/strong&gt; (or receives) time series, &lt;strong&gt;stores&lt;/strong&gt; them, and powers &lt;strong&gt;dashboards&lt;/strong&gt; and &lt;strong&gt;alerts&lt;/strong&gt;. On AWS the fork is usually &lt;strong&gt;Amazon Managed Service for Prometheus (AMP)&lt;/strong&gt;—a managed, Prometheus-compatible store—or &lt;strong&gt;self-managed Prometheus&lt;/strong&gt; in the cluster (Helm chart, operator, or agent + remote storage).&lt;/p&gt;

&lt;p&gt;This article is a &lt;strong&gt;practical decision guide&lt;/strong&gt; for that choice on &lt;strong&gt;EKS&lt;/strong&gt;, especially when you are &lt;strong&gt;not&lt;/strong&gt; migrating a decade of PromQL dashboards and recording rules. It covers what each path optimizes for, how &lt;strong&gt;cost&lt;/strong&gt; shows up on the invoice versus in engineer time, how &lt;strong&gt;alerting&lt;/strong&gt; differs, and a short &lt;strong&gt;rubric&lt;/strong&gt; for defaulting to AMP, self-managed, or a &lt;strong&gt;hybrid&lt;/strong&gt; (&lt;code&gt;remote_write&lt;/code&gt;).&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;p&gt;This guide helps you decide, for a &lt;strong&gt;new&lt;/strong&gt; or &lt;strong&gt;early&lt;/strong&gt; EKS observability stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What &lt;strong&gt;AMP&lt;/strong&gt; and &lt;strong&gt;self-managed Prometheus&lt;/strong&gt; each own (ingest, storage, query, alerting)&lt;/li&gt;
&lt;li&gt;How to compare &lt;strong&gt;cost&lt;/strong&gt; at a high level (ingestion, storage, queries, and cluster resources)&lt;/li&gt;
&lt;li&gt;How to &lt;strong&gt;measure or estimate&lt;/strong&gt; active series and ingestion before you pick a tier&lt;/li&gt;
&lt;li&gt;What you still run in-cluster either way (&lt;strong&gt;exporters&lt;/strong&gt;, &lt;strong&gt;scrape config&lt;/strong&gt;, &lt;strong&gt;ADOT&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;When a &lt;strong&gt;hybrid&lt;/strong&gt; (Prometheus in-cluster → AMP via &lt;code&gt;remote_write&lt;/code&gt;) is the least painful path&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Familiarity with &lt;strong&gt;Prometheus concepts&lt;/strong&gt;: scrape targets, labels, TSDB retention, PromQL&lt;/li&gt;
&lt;li&gt;An EKS cluster (Standard or Auto Mode) with a plan for &lt;strong&gt;who operates&lt;/strong&gt; platform add-ons&lt;/li&gt;
&lt;li&gt;Current &lt;strong&gt;&lt;a href="https://aws.amazon.com/prometheus/pricing/" rel="noopener noreferrer"&gt;Amazon Managed Service for Prometheus pricing&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/prometheus/latest/userguide/what-is-Amazon-Managed-Service-Prometheus.html" rel="noopener noreferrer"&gt;AMP documentation&lt;/a&gt;&lt;/strong&gt;—limits and pricing change over time&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Name the two starting paths
&lt;/h3&gt;

&lt;p&gt;Both paths speak &lt;strong&gt;Prometheus&lt;/strong&gt; (PromQL, exposition format, alert rule semantics). The split is &lt;strong&gt;who runs the TSDB and query API&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Side-by-side
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Amazon Managed Prometheus (AMP)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Self-managed Prometheus (in-cluster)&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS runs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ingestion pipeline, durable TSDB, query plane (serverless-style ops)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;You run&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Collectors/agents, scrape or receive config, IAM, workspace wiring&lt;/td&gt;
&lt;td&gt;Prometheus server (often StatefulSet), PVCs, upgrades, HA, backups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typical ingest&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://aws-otel.github.io/docs/getting-started/container-insights" rel="noopener noreferrer"&gt;AWS Distro for OpenTelemetry (ADOT)&lt;/a&gt; collector, Prometheus &lt;strong&gt;agent&lt;/strong&gt; mode, or &lt;code&gt;remote_write&lt;/code&gt; from an in-cluster server&lt;/td&gt;
&lt;td&gt;In-cluster scrape of ServiceMonitors, Pod annotations, static targets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alerting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://docs.aws.amazon.com/prometheus/latest/userguide/AMP-alertmanager.html" rel="noopener noreferrer"&gt;AMP managed Alertmanager&lt;/a&gt; and/or route to SNS; Grafana alerting is a separate choice&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Alertmanager&lt;/strong&gt; you deploy (often same Helm release or a sibling chart)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dashboards&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Often &lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/grafana/latest/userguide/what-is-Amazon-Managed-Service-Grafana.html" rel="noopener noreferrer"&gt;Amazon Managed Grafana&lt;/a&gt;&lt;/strong&gt; or self-hosted Grafana with AMP as datasource&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Grafana&lt;/strong&gt; (or similar) pointing at in-cluster Prometheus Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-cluster&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Natural fit: &lt;strong&gt;one workspace per env/region&lt;/strong&gt; or federation patterns with less TSDB ops&lt;/td&gt;
&lt;td&gt;Per-cluster Prometheus + optional &lt;strong&gt;Thanos/Mimir&lt;/strong&gt; if you outgrow one server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  AMP — responsibility flow
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------------------------+
| EKS: workloads + exporters    |
| (node-exporter, kube-state,   |
|  app /metrics, ADOT collector)|
+-------------------------------+
              |
              | remote_write / ADOT pipeline
              v
+-------------------------------+
| AWS: AMP workspace (TSDB +      |
| PromQL query API)             |
+-------------------------------+
              |
              v
+-------------------------------+
| You: Grafana / AMP Alertmgr / |
| SNS routes, dashboards, SLOs  |
+-------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Self-managed — responsibility flow
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------------------------+
| EKS: Prometheus server        |
| (scrape + TSDB on PVC/emptyDir)|
+-------------------------------+
              |
              v
+-------------------------------+
| EKS: Alertmanager (optional)  |
| receivers → Slack/PagerDuty   |
+-------------------------------+
              |
              v
+-------------------------------+
| You: Grafana, rule lifecycle, |
| upgrades, capacity, backups   |
+-------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Same for both:&lt;/strong&gt; You still own &lt;strong&gt;what&lt;/strong&gt; gets scraped, &lt;strong&gt;label cardinality&lt;/strong&gt;, &lt;strong&gt;RBAC&lt;/strong&gt; for the UI, and &lt;strong&gt;runbooks&lt;/strong&gt; when alerts fire. Picking AMP does not remove the need for good metric and alert design.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  4. Cost at a glance
&lt;/h3&gt;

&lt;p&gt;Pricing moves; verify against &lt;a href="https://aws.amazon.com/prometheus/pricing/" rel="noopener noreferrer"&gt;AMP pricing&lt;/a&gt; and your &lt;strong&gt;EC2/EBS&lt;/strong&gt; bill for self-managed. Think in three buckets: &lt;strong&gt;ingestion&lt;/strong&gt;, &lt;strong&gt;storage/retention&lt;/strong&gt;, &lt;strong&gt;queries&lt;/strong&gt; (AMP) versus &lt;strong&gt;compute + disk + people&lt;/strong&gt; (self-managed).&lt;/p&gt;

&lt;h4&gt;
  
  
  AMP (managed)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cost driver&lt;/th&gt;
&lt;th&gt;What drives it up&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ingested samples&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Short scrape intervals, high-cardinality labels, many targets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Long retention, high churn, many series&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Queries&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Heavy Grafana dashboards, ad-hoc PromQL, recording rules evaluated in AMP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;AMP can be &lt;strong&gt;cheap at small scale&lt;/strong&gt; and &lt;strong&gt;expensive&lt;/strong&gt; when cardinality explodes (unbounded &lt;code&gt;pod&lt;/code&gt; labels, high-cardinality HTTP paths, per-UUID labels). &lt;strong&gt;Cost guardrails&lt;/strong&gt; (sampling, relabel drops, allowed metric lists) matter more than on a single dev cluster where you only notice disk growth.&lt;/p&gt;

&lt;h4&gt;
  
  
  Self-managed (in-cluster)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cost driver&lt;/th&gt;
&lt;th&gt;What drives it up&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EC2 / Karpenter nodes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CPU/memory for Prometheus replicas and rule evaluation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EBS (or equivalent)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TSDB size × retention; compactions and WAL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Engineer time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chart upgrades, PVC expansion, HA drills, backup/restore testing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A &lt;strong&gt;minimal HA&lt;/strong&gt; self-managed stack (two Prometheus replicas, anti-affinity, 20–50 Gi PVCs each, small Alertmanager) is often &lt;strong&gt;tens of dollars per month&lt;/strong&gt; in AWS resources for a small cluster—&lt;strong&gt;before&lt;/strong&gt; counting on-call and upgrade work. The invoice line is predictable; the &lt;strong&gt;hidden cost&lt;/strong&gt; is operational.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; AMP trades &lt;strong&gt;variable, usage-based&lt;/strong&gt; spend for &lt;strong&gt;less TSDB operations&lt;/strong&gt;. Self-managed trades &lt;strong&gt;fixed-ish infra cost&lt;/strong&gt; for &lt;strong&gt;more control and more chores&lt;/strong&gt;. Neither removes the need to design metrics carefully.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  5. How to check and estimate your usage
&lt;/h3&gt;

&lt;p&gt;Cost conversations are easier when you separate &lt;strong&gt;active series&lt;/strong&gt; (how many distinct time series the TSDB holds) from &lt;strong&gt;ingestion rate&lt;/strong&gt; (how many &lt;strong&gt;samples per second&lt;/strong&gt; you push—what AMP bills most directly).&lt;/p&gt;

&lt;h4&gt;
  
  
  What counts as one series
&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;time series&lt;/strong&gt; is one &lt;strong&gt;metric name&lt;/strong&gt; plus one unique &lt;strong&gt;label set&lt;/strong&gt;. Example: &lt;code&gt;http_requests_total{method="GET",status="200",pod="abc"}&lt;/code&gt; is one series. More pods, paths, or IDs in labels → more series.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rough scale tiers (planning only)
&lt;/h4&gt;

&lt;p&gt;These are &lt;strong&gt;order-of-magnitude&lt;/strong&gt; guides, not limits:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Active series (ballpark)&lt;/th&gt;
&lt;th&gt;Typical EKS picture&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Small&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~1k–5k&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minimal scrape (little kube-state-metrics, no full cAdvisor), or aggressive &lt;code&gt;metric_relabel_configs&lt;/code&gt; drops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Medium&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~10k–30k&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;node-exporter + kube-state-metrics + kubelet/cAdvisor + apiserver on a &lt;strong&gt;small&lt;/strong&gt; cluster (handful of nodes, tens of pods)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Large&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;50k+&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full default chart scrape, many microservices, per-path HTTP metrics, or duplicate exporters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A &lt;strong&gt;standard&lt;/strong&gt; platform scrape (kube-state-metrics, &lt;strong&gt;kubernetes-nodes-cadvisor&lt;/strong&gt;, apiserver, node-exporter) on a small cluster often lands &lt;strong&gt;above 10k&lt;/strong&gt; even when it still feels “small” operationally—&lt;strong&gt;measure&lt;/strong&gt; rather than assuming the &lt;strong&gt;1k&lt;/strong&gt; tier.&lt;/p&gt;

&lt;h4&gt;
  
  
  Turn series into ingestion (AMP-style math)
&lt;/h4&gt;

&lt;p&gt;At a steady scrape interval, each active series produces about &lt;strong&gt;one sample per scrape&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;samples_per_second ≈ active_series ÷ scrape_interval_seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples at &lt;strong&gt;30s&lt;/strong&gt; scrape:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Active series&lt;/th&gt;
&lt;th&gt;~samples/s&lt;/th&gt;
&lt;th&gt;~samples/month (30 days)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;~33&lt;/td&gt;
&lt;td&gt;~86M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;~333&lt;/td&gt;
&lt;td&gt;~864M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25,000&lt;/td&gt;
&lt;td&gt;~833&lt;/td&gt;
&lt;td&gt;~2.2B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50,000&lt;/td&gt;
&lt;td&gt;~1,667&lt;/td&gt;
&lt;td&gt;~4.3B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use &lt;strong&gt;86,400 × 30&lt;/strong&gt; seconds per month for back-of-envelope planning. If jobs use different intervals, sum per job or use a weighted average.&lt;/p&gt;

&lt;h4&gt;
  
  
  Back-of-envelope before deploy
&lt;/h4&gt;

&lt;p&gt;List scrape jobs and estimate series per target:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;How to estimate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;node-exporter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~400–1,000 series × &lt;strong&gt;node count&lt;/strong&gt; (disks and NICs add labels)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kube-state-metrics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~20–40 series × &lt;strong&gt;pod count&lt;/strong&gt;, plus Deployments, PVCs, HPA, PDB, …&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;kubelet / cAdvisor&lt;/strong&gt; (&lt;code&gt;kubernetes-nodes-cadvisor&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Often the &lt;strong&gt;largest&lt;/strong&gt; bucket—scales with &lt;strong&gt;containers&lt;/strong&gt;, not just nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;apiserver&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Often &lt;strong&gt;thousands&lt;/strong&gt; of series (histogram buckets and verb/resource labels)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Application &lt;code&gt;/metrics&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Highly variable—histograms and high-cardinality labels dominate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;active_series ≈ Σ (targets × series_per_target)   # plus a little for Prometheus self-metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Churn&lt;/strong&gt; (pods created and destroyed) affects AMP &lt;strong&gt;storage&lt;/strong&gt; more than steady active series; dynamic fleets need headroom.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to check (self-managed Prometheus already running)
&lt;/h4&gt;

&lt;p&gt;Port-forward to the Prometheus server (adjust namespace and service name to match your install):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; prometheus port-forward svc/prometheus-server 9090:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Headline count&lt;/strong&gt; — TSDB stats API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://127.0.0.1:9090/api/v1/status/tsdb | jq &lt;span class="s1"&gt;'.data.numSeries'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same value in the UI: &lt;strong&gt;Status → TSDB Stats&lt;/strong&gt;. The metric &lt;code&gt;prometheus_tsdb_head_series&lt;/code&gt; tracks it over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Top metrics by cardinality&lt;/strong&gt; (can be expensive on very large TSDBs—use in non-prod or off-peak):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sG&lt;/span&gt; &lt;span class="s1"&gt;'http://127.0.0.1:9090/api/v1/query'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'query=topk(20, count by (__name__) ({__name__=~".+"}))'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="s1"&gt;'.data.result[] | {metric: .metric.__name__, series: .value[1]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Top scrape jobs&lt;/strong&gt; (find what to trim):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sG&lt;/span&gt; &lt;span class="s1"&gt;'http://127.0.0.1:9090/api/v1/query'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'query=topk(10, count by (job) ({__name__=~".+"}))'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="s1"&gt;'.data.result[] | {job: .metric.job, series: .value[1]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the UI: &lt;strong&gt;Status → Targets&lt;/strong&gt; shows per-target health and last scrape size—useful when a single job spikes.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to check (AMP)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; metrics on the AMP workspace (ingestion and active series—see &lt;a href="https://docs.aws.amazon.com/prometheus/latest/userguide/AMP-monitoring.html" rel="noopener noreferrer"&gt;Monitor AMP&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS console&lt;/strong&gt; → AMP workspace usage views for growth over time.&lt;/li&gt;
&lt;li&gt;If you use &lt;strong&gt;ADOT&lt;/strong&gt; or &lt;strong&gt;remote_write&lt;/strong&gt;, the &lt;strong&gt;sender&lt;/strong&gt; (collector or in-cluster Prometheus) still exposes scrape stats—debug cardinality at the source before it hits the workspace.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  What usually blows up series count
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;High-cardinality labels&lt;/strong&gt; — &lt;code&gt;pod&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;trace_id&lt;/code&gt;, unbounded &lt;code&gt;user_id&lt;/code&gt; on high-volume metrics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate scrape&lt;/strong&gt; — EKS &lt;strong&gt;addon&lt;/strong&gt; node-exporter &lt;strong&gt;and&lt;/strong&gt; Helm node-exporter, or two Prometheus replicas each scraping everything without needing two full TSDBs for AMP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Histograms&lt;/strong&gt; — each bucket, plus &lt;code&gt;_sum&lt;/code&gt; and &lt;code&gt;_count&lt;/code&gt;, is multiple series per logical metric.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full cAdvisor / apiserver&lt;/strong&gt; defaults with no relabel drops.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After you have &lt;code&gt;numSeries&lt;/code&gt; and the top two &lt;code&gt;job&lt;/code&gt; values, you can map yourself to the &lt;strong&gt;small / medium / large&lt;/strong&gt; table above and plug numbers into the &lt;strong&gt;samples per month&lt;/strong&gt; formula for AMP pricing.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Who runs what—and what the first year feels like
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Where the work lands
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;AMP&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Self-managed&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TSDB durability &amp;amp; scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;td&gt;You (PVC size, retention flags, compaction behavior)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prometheus version upgrades&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed compatibility window&lt;/td&gt;
&lt;td&gt;You (Helm chart / operator upgrades, CRD drift)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scrape discovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You (collector config, EKS receivers, ServiceMonitor CRDs)&lt;/td&gt;
&lt;td&gt;You (same; often more familiar with &lt;code&gt;prometheus.yml&lt;/code&gt; in-cluster)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Recording / alerting rules&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AMP rule groups or in-cluster evaluation + &lt;code&gt;remote_write&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Native &lt;code&gt;serverFiles&lt;/code&gt; / PrometheusRule CRDs in Git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Long-term retention / global view&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AMP + optional export; or Mimir/Thanos later&lt;/td&gt;
&lt;td&gt;Add Thanos/Mimir/Cortex when one server is not enough&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UI for debugging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Grafana → AMP; limited “SSH into Prometheus”&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;kubectl port-forward&lt;/code&gt; to &lt;code&gt;:9090&lt;/code&gt;—fast for platform engineers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  AMP in practice
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Less toil&lt;/strong&gt; on TSDB HA, backups, and “disk full” pages for the metrics store&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strong&lt;/strong&gt; fit for &lt;strong&gt;org-wide&lt;/strong&gt; standards and &lt;strong&gt;IAM&lt;/strong&gt;-bound workspaces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch&lt;/strong&gt; ingestion billing and &lt;strong&gt;cardinality&lt;/strong&gt;; use ADOT/Prometheus relabeling deliberately&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alertmanager&lt;/strong&gt; behavior is &lt;strong&gt;AWS-shaped&lt;/strong&gt;—read AMP Alertmanager docs before assuming OSS Alertmanager feature parity&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Self-managed in practice
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full control&lt;/strong&gt; of scrape config, rule files, federation, and “break glass” PromQL on localhost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Familiar&lt;/strong&gt; path: &lt;a href="https://github.com/prometheus-community/helm-charts/tree/main/charts/prometheus" rel="noopener noreferrer"&gt;prometheus-community/prometheus&lt;/a&gt; Helm chart, GitOps overlays per cluster&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recurring work:&lt;/strong&gt; chart bumps with Kubernetes upgrades, PVC growth, proving &lt;strong&gt;Alertmanager HA&lt;/strong&gt;, securing the Prometheus UI (it has &lt;strong&gt;no built-in auth&lt;/strong&gt;—use network policy, private ingress, or an auth proxy)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EKS add-ons&lt;/strong&gt; can cover &lt;strong&gt;node-exporter&lt;/strong&gt; (managed addon) while you keep a &lt;strong&gt;narrow&lt;/strong&gt; Prometheus release for the server and rules—avoid duplicating the same metrics twice&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  7. EKS-specific wiring (both paths)
&lt;/h3&gt;

&lt;p&gt;Neither option removes in-cluster &lt;strong&gt;collection&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Typical role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metrics server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HPA CPU/memory—not a Prometheus replacement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;prometheus-node-exporter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Node/host metrics (DaemonSet or &lt;strong&gt;EKS managed addon&lt;/strong&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kube-state-metrics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kubernetes object metrics (Deployment, Pod, PVC, …)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CoreDNS / kubelet / apiserver&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cluster health; scrape config or ADOT receivers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Application &lt;code&gt;/metrics&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Your SLOs and business metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;AMP path:&lt;/strong&gt; deploy &lt;strong&gt;ADOT&lt;/strong&gt; (or Prometheus in &lt;strong&gt;agent&lt;/strong&gt; mode) with &lt;code&gt;remote_write&lt;/code&gt; to the AMP workspace endpoint; use &lt;strong&gt;EKS Pod Identity&lt;/strong&gt; or IRSA for SigV4 authentication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-managed path:&lt;/strong&gt; enable targets in Helm values or the &lt;strong&gt;Prometheus Operator&lt;/strong&gt;; pin &lt;strong&gt;&lt;code&gt;kube-state-metrics&lt;/code&gt;&lt;/strong&gt; and exporters intentionally so you do not scrape the same series twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hybrid (common):&lt;/strong&gt; run a &lt;strong&gt;small&lt;/strong&gt; in-cluster Prometheus for &lt;strong&gt;fast local debugging&lt;/strong&gt; and &lt;strong&gt;federation-style&lt;/strong&gt; rules, &lt;code&gt;remote_write&lt;/code&gt; &lt;strong&gt;aggregates or critical&lt;/strong&gt; series to AMP for org dashboards and long retention. You pay complexity once, not double storage for every raw sample.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. Alerting and Grafana
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;AMP&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Self-managed&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rule evaluation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AMP rule groups and/or in-cluster Prometheus firing → AMP&lt;/td&gt;
&lt;td&gt;Prometheus &lt;code&gt;alerting_rules.yml&lt;/code&gt; / PrometheusRule CRDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alert routing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AMP Alertmanager, SNS, EventBridge; fewer “random webhook” examples in AWS docs&lt;/td&gt;
&lt;td&gt;Alertmanager receivers (Slack, PagerDuty, Opsgenie) in Git—with secrets via External Secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Grafana&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed Grafana with AMP datasource is the path of least resistance&lt;/td&gt;
&lt;td&gt;Platform Grafana in-cluster; datasource = Prometheus Service DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Double paging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Risk if &lt;strong&gt;both&lt;/strong&gt; AMP rules and Grafana unified alerting fire on the same metric&lt;/td&gt;
&lt;td&gt;Risk if &lt;strong&gt;both&lt;/strong&gt; Prometheus and Grafana own the same alerts—pick &lt;strong&gt;one owner&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  9. Decision rubric (greenfield)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Lean toward AMP&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want &lt;strong&gt;no&lt;/strong&gt; StatefulSet TSDB to babysit and are fine with &lt;strong&gt;usage-based&lt;/strong&gt; metrics cost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized&lt;/strong&gt; observability across many accounts/clusters matters soon&lt;/li&gt;
&lt;li&gt;You will standardize on &lt;strong&gt;ADOT&lt;/strong&gt; / AWS observability patterns and &lt;strong&gt;Managed Grafana&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The team is &lt;strong&gt;small&lt;/strong&gt; and should not own Prometheus compaction, PVC resize, and version skew&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lean toward self-managed Prometheus&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need &lt;strong&gt;maximum control&lt;/strong&gt; (custom scrape hooks, exotic service discovery, air-gapped patterns)&lt;/li&gt;
&lt;li&gt;Most engineers live in &lt;strong&gt;port-forward PromQL&lt;/strong&gt; and Git-managed &lt;code&gt;prometheus.yml&lt;/code&gt; / rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable&lt;/strong&gt; infra cost matters more than eliminating ops (small cluster, disciplined cardinality)&lt;/li&gt;
&lt;li&gt;You already run &lt;strong&gt;kube-prometheus-stack&lt;/strong&gt; patterns and want one chart to own rules + Alertmanager&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lean toward hybrid&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need &lt;strong&gt;in-cluster&lt;/strong&gt; debugging and &lt;strong&gt;org-wide&lt;/strong&gt; long retention or dashboards in AMP&lt;/li&gt;
&lt;li&gt;You are &lt;strong&gt;migrating&lt;/strong&gt;: start self-managed, &lt;code&gt;remote_write&lt;/code&gt; to AMP, cut over Grafana datasources, then shrink in-cluster retention&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Default suggestion for a greenfield EKS platform team:&lt;/strong&gt; if nobody wants to own &lt;strong&gt;TSDB operations&lt;/strong&gt;, start with &lt;strong&gt;AMP + ADOT&lt;/strong&gt; and Managed Grafana; if the team is building &lt;strong&gt;GitOps muscle&lt;/strong&gt; on platform charts and wants the &lt;strong&gt;fastest&lt;/strong&gt; “see &lt;code&gt;/targets&lt;/code&gt; and &lt;code&gt;/rules&lt;/code&gt; locally” loop, &lt;strong&gt;self-managed&lt;/strong&gt; with a &lt;strong&gt;narrow&lt;/strong&gt; chart (server + optional Alertmanager, exporters wired deliberately) is a solid teaching path. Revisit when &lt;strong&gt;cardinality&lt;/strong&gt;, &lt;strong&gt;retention&lt;/strong&gt;, or &lt;strong&gt;multi-cluster&lt;/strong&gt; pain appears.&lt;/p&gt;




&lt;h3&gt;
  
  
  10. Troubleshooting: common misconceptions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;“AMP means we don’t run anything in the cluster.”&lt;/strong&gt; You still run &lt;strong&gt;collectors/exporters&lt;/strong&gt; and own &lt;strong&gt;cardinality&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“Self-managed is always cheaper.”&lt;/strong&gt; &lt;strong&gt;EBS + replicas&lt;/strong&gt; can be modest; &lt;strong&gt;engineer time&lt;/strong&gt; and &lt;strong&gt;incident&lt;/strong&gt; cost often dominate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“We installed the prometheus-node-exporter EKS addon, so we have Prometheus.”&lt;/strong&gt; The addon is &lt;strong&gt;node metrics&lt;/strong&gt;, not the &lt;strong&gt;TSDB server&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“Grafana alerting replaces Prometheus/Alertmanager.”&lt;/strong&gt; It can—but &lt;strong&gt;two owners&lt;/strong&gt; for the same alert is how you get &lt;strong&gt;double pages&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“remote_write is free duplication.”&lt;/strong&gt; It is not; you pay &lt;strong&gt;network, ingest, and often double evaluation&lt;/strong&gt; unless you design what gets forwarded.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  11. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/prometheus/latest/userguide/what-is-Amazon-Managed-Service-Prometheus.html" rel="noopener noreferrer"&gt;What is Amazon Managed Service for Prometheus?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/prometheus/pricing/" rel="noopener noreferrer"&gt;Amazon Managed Service for Prometheus pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/prometheus/latest/userguide/AMP-onboard-ingest-metrics-eks.html" rel="noopener noreferrer"&gt;Set up ingestion from EKS clusters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/prometheus/latest/userguide/AMP-alertmanager.html" rel="noopener noreferrer"&gt;AMP Alertmanager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws-otel.github.io/docs/getting-started/container-insights" rel="noopener noreferrer"&gt;AWS Distro for OpenTelemetry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://prometheus.io/docs/introduction/overview/" rel="noopener noreferrer"&gt;Prometheus documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/prometheus-community/helm-charts/tree/main/charts/prometheus" rel="noopener noreferrer"&gt;prometheus-community/prometheus Helm chart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/grafana/latest/userguide/what-is-Amazon-Managed-Service-Grafana.html" rel="noopener noreferrer"&gt;Amazon Managed Grafana&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/eks-add-ons.html" rel="noopener noreferrer"&gt;EKS managed add-ons (including prometheus-node-exporter)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/prometheus/latest/userguide/AMP-monitoring.html" rel="noopener noreferrer"&gt;Monitor Amazon Managed Service for Prometheus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>prometheus</category>
      <category>eks</category>
      <category>observability</category>
      <category>amp</category>
    </item>
    <item>
      <title>Why does my AWS SSO session die so fast? (And how to change it in the console)</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Fri, 17 Apr 2026 21:13:08 +0000</pubDate>
      <link>https://dev.to/jajera/why-does-my-aws-sso-session-die-so-fast-and-how-to-change-it-in-the-console-455k</link>
      <guid>https://dev.to/jajera/why-does-my-aws-sso-session-die-so-fast-and-how-to-change-it-in-the-console-455k</guid>
      <description>&lt;h2&gt;
  
  
  Why does my AWS SSO session die so fast? (And how to change it in the console)
&lt;/h2&gt;

&lt;p&gt;Ever open the &lt;strong&gt;AWS access portal&lt;/strong&gt;, click into an account, get a few things done, grab coffee, come back—and &lt;strong&gt;your session is gone&lt;/strong&gt;? You sign in again through &lt;strong&gt;IAM Identity Center&lt;/strong&gt; (what people still call &lt;strong&gt;SSO&lt;/strong&gt;), mutter something about timeouts, and move on.&lt;/p&gt;

&lt;p&gt;I used to assume AWS was just being &lt;strong&gt;strict&lt;/strong&gt; for security. Partly true. The other part is boring: &lt;strong&gt;the permission set is probably still on the default session length&lt;/strong&gt;, which is often &lt;strong&gt;one hour&lt;/strong&gt;. Once you know that, fixing it is a few clicks in the &lt;strong&gt;console&lt;/strong&gt;. No &lt;strong&gt;Terraform&lt;/strong&gt;, no &lt;strong&gt;CLI&lt;/strong&gt;—just the UI.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What you are changing:&lt;/strong&gt; &lt;strong&gt;Session duration&lt;/strong&gt; on an &lt;strong&gt;IAM Identity Center permission set&lt;/strong&gt; (how long &lt;strong&gt;console&lt;/strong&gt; and &lt;strong&gt;CLI&lt;/strong&gt; sessions issued through that set stay valid before you must re-authenticate through the portal).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it felt short:&lt;/strong&gt; &lt;strong&gt;Default&lt;/strong&gt; duration for a new permission set is commonly &lt;strong&gt;1 hour&lt;/strong&gt; unless someone already raised it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What this article is not:&lt;/strong&gt; &lt;strong&gt;IAM roles&lt;/strong&gt; in accounts, &lt;strong&gt;temporary credentials&lt;/strong&gt; from &lt;code&gt;sts:AssumeRole&lt;/code&gt; in automation, or &lt;strong&gt;Control Tower&lt;/strong&gt; landing zone knobs—only &lt;strong&gt;Identity Center permission sets&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You can sign in to the &lt;strong&gt;AWS Management Console&lt;/strong&gt; for the &lt;strong&gt;organization&lt;/strong&gt; that owns &lt;strong&gt;IAM Identity Center&lt;/strong&gt; (often the &lt;strong&gt;management&lt;/strong&gt; or &lt;strong&gt;delegated admin&lt;/strong&gt; account where the directory lives).&lt;/li&gt;
&lt;li&gt;Your user (or role) has permission to &lt;strong&gt;edit permission sets&lt;/strong&gt; (for example an &lt;strong&gt;IAM Identity Center&lt;/strong&gt; administrator).&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Open the permission set
&lt;/h3&gt;

&lt;h4&gt;
  
  
  3.1 Go to IAM Identity Center
&lt;/h4&gt;

&lt;p&gt;In the console, open &lt;strong&gt;&lt;a href="https://console.aws.amazon.com/singlesignon/" rel="noopener noreferrer"&gt;IAM Identity Center&lt;/a&gt;&lt;/strong&gt; (search &lt;strong&gt;IAM Identity Center&lt;/strong&gt; in the top search bar if the console layout moves again).&lt;/p&gt;

&lt;h4&gt;
  
  
  3.2 Open Permission sets
&lt;/h4&gt;

&lt;p&gt;Choose &lt;strong&gt;Permission sets&lt;/strong&gt; in the left navigation. Select the &lt;strong&gt;permission set&lt;/strong&gt; you actually use for day-to-day access (for example &lt;strong&gt;AdministratorAccess&lt;/strong&gt;, &lt;strong&gt;PowerUser&lt;/strong&gt;, or a custom name your org created).&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Edit session duration
&lt;/h3&gt;

&lt;h4&gt;
  
  
  4.1 Edit settings
&lt;/h4&gt;

&lt;p&gt;On the permission set details page, choose &lt;strong&gt;Edit&lt;/strong&gt; (sometimes shown as &lt;strong&gt;Edit configuration&lt;/strong&gt; depending on the console version).&lt;/p&gt;

&lt;h4&gt;
  
  
  4.2 Find session duration
&lt;/h4&gt;

&lt;p&gt;Look for &lt;strong&gt;Session duration&lt;/strong&gt; (wording may be &lt;strong&gt;Session length&lt;/strong&gt; or similar). You will see a value in &lt;strong&gt;hours&lt;/strong&gt; (or a dropdown bounded by what &lt;strong&gt;Identity Center&lt;/strong&gt; allows for that permission set).&lt;/p&gt;

&lt;p&gt;Pick something that matches how you work—for example &lt;strong&gt;8 hours&lt;/strong&gt; for a normal workday—without ignoring your org’s security policy. &lt;strong&gt;Shorter&lt;/strong&gt; is generally safer for &lt;strong&gt;high-privilege&lt;/strong&gt; sets; &lt;strong&gt;longer&lt;/strong&gt; is more convenient and widens the window if a session is misused.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.3 Save
&lt;/h4&gt;

&lt;p&gt;Save the change. &lt;strong&gt;Existing&lt;/strong&gt; sessions do not always pick up the new limit immediately; people may need to &lt;strong&gt;sign out&lt;/strong&gt; of the access portal and &lt;strong&gt;sign in again&lt;/strong&gt; (or wait for the current session to expire) before the new duration applies.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Summary
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Short timeouts&lt;/strong&gt; after portal login are often just the &lt;strong&gt;permission set default&lt;/strong&gt; (commonly &lt;strong&gt;one hour&lt;/strong&gt;), not a mystery AWS punishment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Identity Center&lt;/strong&gt; → &lt;strong&gt;Permission sets&lt;/strong&gt; → your set → &lt;strong&gt;Edit&lt;/strong&gt; → &lt;strong&gt;Session duration&lt;/strong&gt; → save. &lt;strong&gt;Re-login&lt;/strong&gt; if you do not see the new behavior yet.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  6. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/howtosessionduration.html" rel="noopener noreferrer"&gt;Configure session duration for AWS IAM Identity Center&lt;/a&gt; (AWS Documentation)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>iam</category>
      <category>identitycenter</category>
      <category>sso</category>
    </item>
    <item>
      <title>Unenroll a Control Tower sandbox member with the AWS CLI</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Thu, 16 Apr 2026 09:28:40 +0000</pubDate>
      <link>https://dev.to/jajera/unenroll-a-control-tower-sandbox-member-with-the-aws-cli-22ij</link>
      <guid>https://dev.to/jajera/unenroll-a-control-tower-sandbox-member-with-the-aws-cli-22ij</guid>
      <description>&lt;h2&gt;
  
  
  Unenroll a Control Tower sandbox member with the AWS CLI
&lt;/h2&gt;

&lt;p&gt;You enrolled a &lt;strong&gt;member account&lt;/strong&gt; into &lt;strong&gt;AWS Control Tower&lt;/strong&gt; (for example a &lt;strong&gt;sandbox&lt;/strong&gt; workload account) and later want it &lt;strong&gt;not&lt;/strong&gt; governed by the landing zone anymore. The console path is &lt;strong&gt;Organization&lt;/strong&gt; then &lt;strong&gt;Unmanage&lt;/strong&gt;. This article is the &lt;strong&gt;CLI&lt;/strong&gt; path: terminate the &lt;strong&gt;Control Tower&lt;/strong&gt; &lt;strong&gt;Service Catalog&lt;/strong&gt; provisioned product that represents &lt;strong&gt;account enrollment&lt;/strong&gt;, without closing the AWS account.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Goal:&lt;/strong&gt; &lt;strong&gt;Unenroll&lt;/strong&gt; (unmanage) one &lt;strong&gt;member account&lt;/strong&gt; so &lt;strong&gt;Control Tower&lt;/strong&gt; removes its controls and &lt;strong&gt;Service Catalog&lt;/strong&gt; enrollment, while &lt;strong&gt;keeping&lt;/strong&gt; the account open in &lt;strong&gt;AWS Organizations&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mechanism:&lt;/strong&gt; &lt;strong&gt;Terminate&lt;/strong&gt; the &lt;strong&gt;&lt;code&gt;CONTROL_TOWER_ACCOUNT&lt;/code&gt;&lt;/strong&gt; provisioned product whose name looks like &lt;strong&gt;&lt;code&gt;Enroll-Account-&amp;lt;12-digit-account-id&amp;gt;&lt;/code&gt;&lt;/strong&gt;, with &lt;strong&gt;&lt;code&gt;--retain-physical-resources&lt;/code&gt;&lt;/strong&gt; so the &lt;strong&gt;account&lt;/strong&gt; itself is not deleted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where it runs:&lt;/strong&gt; &lt;strong&gt;Management account&lt;/strong&gt; credentials with permission to &lt;strong&gt;Service Catalog&lt;/strong&gt; APIs (and typically &lt;strong&gt;Organizations&lt;/strong&gt; read if you verify parent OU afterward).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Region:&lt;/strong&gt; Use your &lt;strong&gt;Control Tower home region&lt;/strong&gt; (often where &lt;strong&gt;Account Factory&lt;/strong&gt; and enrollment products live). Examples below use &lt;strong&gt;&lt;code&gt;ap-southeast-2&lt;/code&gt;&lt;/strong&gt;; substitute yours if different.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Management account&lt;/strong&gt; access (for example &lt;strong&gt;IAM Identity Center&lt;/strong&gt; admin role on the org payer).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS CLI&lt;/strong&gt; v2 configured for that account (&lt;code&gt;aws sts get-caller-identity&lt;/code&gt; shows the management id).&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;member account id&lt;/strong&gt; you are unmanaging (12 digits).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Patience:&lt;/strong&gt; termination is &lt;strong&gt;asynchronous&lt;/strong&gt;; poll &lt;strong&gt;&lt;code&gt;describe-record&lt;/code&gt;&lt;/strong&gt; until &lt;strong&gt;&lt;code&gt;SUCCEEDED&lt;/code&gt;&lt;/strong&gt; or diagnose &lt;strong&gt;&lt;code&gt;FAILED&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Confirm identity and region
&lt;/h3&gt;

&lt;h4&gt;
  
  
  3.1 Caller identity
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-management-profile

aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm &lt;strong&gt;&lt;code&gt;Account&lt;/code&gt;&lt;/strong&gt; is the &lt;strong&gt;organization management&lt;/strong&gt; account.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.2 Control Tower home region
&lt;/h4&gt;

&lt;p&gt;If you are unsure which region holds enrollment products, try the region where you normally open &lt;strong&gt;Control Tower&lt;/strong&gt; and &lt;strong&gt;Account Factory&lt;/strong&gt;, then continue in &lt;strong&gt;Section 4&lt;/strong&gt; using that &lt;strong&gt;&lt;code&gt;--region&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Find the enrollment provisioned product
&lt;/h3&gt;

&lt;h4&gt;
  
  
  4.1 Scan provisioned products
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;scan-provisioned-products&lt;/code&gt;&lt;/strong&gt; returns up to &lt;strong&gt;20&lt;/strong&gt; items per call (use &lt;strong&gt;&lt;code&gt;PageToken&lt;/code&gt;&lt;/strong&gt; if you have many). Filter by eye or script for &lt;strong&gt;&lt;code&gt;Enroll-Account-&amp;lt;your-member-id&amp;gt;&lt;/code&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ap-southeast-2

aws servicecatalog scan-provisioned-products &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--page-size&lt;/span&gt; 20 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.2 Identify the fields you need
&lt;/h4&gt;

&lt;p&gt;From the matching object, note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Id&lt;/code&gt;&lt;/strong&gt; — the &lt;strong&gt;provisioned product id&lt;/strong&gt; (example shape: &lt;strong&gt;&lt;code&gt;pp-xxxxxxxxxxxxx&lt;/code&gt;&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Name&lt;/code&gt;&lt;/strong&gt; — often &lt;strong&gt;&lt;code&gt;Enroll-Account-405087531484&lt;/code&gt;&lt;/strong&gt; style.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Type&lt;/code&gt;&lt;/strong&gt; — should include &lt;strong&gt;&lt;code&gt;CONTROL_TOWER_ACCOUNT&lt;/code&gt;&lt;/strong&gt; for the enrollment product you want to end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If nothing appears in the first page, paginate with &lt;strong&gt;&lt;code&gt;--page-token&lt;/code&gt;&lt;/strong&gt; from the response, or confirm &lt;strong&gt;region&lt;/strong&gt; and that the account was enrolled through &lt;strong&gt;Control Tower&lt;/strong&gt; (not only moved under an OU in &lt;strong&gt;Organizations&lt;/strong&gt; without enrollment).&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Terminate the provisioned product (unenroll)
&lt;/h3&gt;

&lt;p&gt;Use a &lt;strong&gt;unique&lt;/strong&gt; &lt;strong&gt;&lt;code&gt;--terminate-token&lt;/code&gt;&lt;/strong&gt; per attempt (idempotency). &lt;strong&gt;&lt;code&gt;--retain-physical-resources&lt;/code&gt;&lt;/strong&gt; keeps the &lt;strong&gt;AWS account&lt;/strong&gt;; Control Tower still tears down &lt;strong&gt;CT-managed&lt;/strong&gt; resources it owns for that enrollment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ap-southeast-2
&lt;span class="nv"&gt;PP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pp-jxuf4ldvkwxxc

aws servicecatalog terminate-provisioned-product &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provisioned-product-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--terminate-token&lt;/span&gt; &lt;span class="s2"&gt;"unenroll-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--retain-physical-resources&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response includes &lt;strong&gt;&lt;code&gt;RecordDetail.RecordId&lt;/code&gt;&lt;/strong&gt; (example shape: &lt;strong&gt;&lt;code&gt;rec-xxxxxxxxxxxxx&lt;/code&gt;&lt;/strong&gt;).&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Wait for the record to finish
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;describe-record&lt;/code&gt;&lt;/strong&gt; uses &lt;strong&gt;&lt;code&gt;--id&lt;/code&gt;&lt;/strong&gt; set to the &lt;strong&gt;record id&lt;/strong&gt; (not &lt;strong&gt;&lt;code&gt;--record-id&lt;/code&gt;&lt;/strong&gt; on the CLI).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ap-southeast-2
&lt;span class="nv"&gt;RECORD_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rec-tupynci5numpi

aws servicecatalog describe-record &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RECORD_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'RecordDetail.{Status:Status,Errors:RecordErrors}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Poll until &lt;strong&gt;&lt;code&gt;Status&lt;/code&gt;&lt;/strong&gt; is &lt;strong&gt;&lt;code&gt;SUCCEEDED&lt;/code&gt;&lt;/strong&gt;. If &lt;strong&gt;&lt;code&gt;FAILED&lt;/code&gt;&lt;/strong&gt;, read &lt;strong&gt;&lt;code&gt;RecordErrors&lt;/code&gt;&lt;/strong&gt; and fix permissions or open an &lt;strong&gt;AWS Support&lt;/strong&gt; case for &lt;strong&gt;Service Catalog&lt;/strong&gt; / &lt;strong&gt;Control Tower&lt;/strong&gt; as appropriate.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. Verify organization placement
&lt;/h3&gt;

&lt;p&gt;After a successful &lt;strong&gt;unmanage&lt;/strong&gt;, the account usually moves under the &lt;strong&gt;organization root&lt;/strong&gt; (no longer under a &lt;strong&gt;Control Tower&lt;/strong&gt;-registered OU for that enrollment). Confirm with &lt;strong&gt;Organizations&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;MEMBER_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;405087531484

aws organizations list-parents &lt;span class="nt"&gt;--child-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MEMBER_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expect &lt;strong&gt;&lt;code&gt;Type&lt;/code&gt;&lt;/strong&gt; &lt;strong&gt;&lt;code&gt;ROOT&lt;/code&gt;&lt;/strong&gt; for the parent you care about when the account has been moved out of &lt;strong&gt;Control Tower&lt;/strong&gt;-managed OUs per product behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Also expect:&lt;/strong&gt; &lt;strong&gt;Control Tower&lt;/strong&gt; removes &lt;strong&gt;&lt;code&gt;AWSControlTowerExecution&lt;/code&gt;&lt;/strong&gt; from the member when it cleans up. If you manage that role elsewhere (for example &lt;strong&gt;Terraform&lt;/strong&gt; in a factory repo), run your pipeline again so intent matches reality.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. Console alternative
&lt;/h3&gt;

&lt;p&gt;If you prefer the UI: &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/unmanage-account.html" rel="noopener noreferrer"&gt;Unenroll an account&lt;/a&gt; — &lt;strong&gt;Control Tower&lt;/strong&gt; console, &lt;strong&gt;Organization&lt;/strong&gt;, expand the OU, select the account, &lt;strong&gt;Unmanage&lt;/strong&gt;, wait until status shows &lt;strong&gt;Not enrolled&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  9. Summary: Copy-paste
&lt;/h3&gt;

&lt;p&gt;Replace &lt;strong&gt;&lt;code&gt;REGION&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;PP_ID&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;AWS_PROFILE&lt;/code&gt;&lt;/strong&gt;, and optionally &lt;strong&gt;&lt;code&gt;MEMBER_ID&lt;/code&gt;&lt;/strong&gt; for checks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-management-profile
&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ap-southeast-2

aws sts get-caller-identity

aws servicecatalog scan-provisioned-products &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--page-size&lt;/span&gt; 20 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table

&lt;span class="nv"&gt;PP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pp-REPLACE_ME
aws servicecatalog terminate-provisioned-product &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provisioned-product-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--terminate-token&lt;/span&gt; &lt;span class="s2"&gt;"unenroll-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--retain-physical-resources&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; json

&lt;span class="nv"&gt;RECORD_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rec-REPLACE_ME
aws servicecatalog describe-record &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RECORD_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; json

&lt;span class="nv"&gt;MEMBER_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;405087531484
aws organizations list-parents &lt;span class="nt"&gt;--child-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MEMBER_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  10. Troubleshooting
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;What to try&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;scan-provisioned-products&lt;/code&gt;&lt;/strong&gt; returns no &lt;strong&gt;&lt;code&gt;Enroll-Account-…&lt;/code&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Wrong &lt;strong&gt;&lt;code&gt;REGION&lt;/code&gt;&lt;/strong&gt;; use &lt;strong&gt;Control Tower home&lt;/strong&gt; region. Or the account was never &lt;strong&gt;enrolled&lt;/strong&gt; via &lt;strong&gt;Control Tower&lt;/strong&gt; (only placed in an OU with &lt;strong&gt;Organizations&lt;/strong&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;terminate-provisioned-product&lt;/code&gt;&lt;/strong&gt; access denied&lt;/td&gt;
&lt;td&gt;Principal lacks &lt;strong&gt;Service Catalog&lt;/strong&gt; terminate permissions in the management account.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;describe-record&lt;/code&gt;&lt;/strong&gt; &lt;strong&gt;&lt;code&gt;FAILED&lt;/code&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Read &lt;strong&gt;&lt;code&gt;RecordErrors&lt;/code&gt;&lt;/strong&gt;; may be &lt;strong&gt;Control Tower&lt;/strong&gt; internal constraint, &lt;strong&gt;SCP&lt;/strong&gt;, or &lt;strong&gt;account state&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;ParamValidation&lt;/code&gt;&lt;/strong&gt; on &lt;strong&gt;&lt;code&gt;describe-record&lt;/code&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Use &lt;strong&gt;&lt;code&gt;--id&lt;/code&gt;&lt;/strong&gt;, not &lt;strong&gt;&lt;code&gt;--record-id&lt;/code&gt;&lt;/strong&gt;, for &lt;strong&gt;&lt;code&gt;record-id&lt;/code&gt;&lt;/strong&gt; style values.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Account should stay in a specific OU after unenroll&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Unmanage&lt;/strong&gt; moves the account to &lt;strong&gt;root&lt;/strong&gt; per &lt;strong&gt;AWS&lt;/strong&gt; docs; move it again with &lt;strong&gt;&lt;code&gt;aws organizations move-account&lt;/code&gt;&lt;/strong&gt; if your org design requires a different OU.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  11. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/unmanage-account.html" rel="noopener noreferrer"&gt;Unenroll an account&lt;/a&gt; (Control Tower User Guide)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/unenroll-with-sc.html" rel="noopener noreferrer"&gt;Unenroll an account in Service Catalog&lt;/a&gt; (terminate provisioned product)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/servicecatalog/latest/dg/API_TerminateProvisionedProduct.html" rel="noopener noreferrer"&gt;TerminateProvisionedProduct&lt;/a&gt; (API reference)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/servicecatalog/latest/dg/API_DescribeRecord.html" rel="noopener noreferrer"&gt;DescribeRecord&lt;/a&gt; (API reference)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/organizations/latest/APIReference/API_MoveAccount.html" rel="noopener noreferrer"&gt;MoveAccount&lt;/a&gt; (Organizations API, if you relocate after unenroll)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>controltower</category>
      <category>organizations</category>
      <category>cli</category>
    </item>
    <item>
      <title>IAM Identity Center account assignments for Terraform member accounts</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Wed, 15 Apr 2026 19:27:14 +0000</pubDate>
      <link>https://dev.to/jajera/iam-identity-center-account-assignments-for-terraform-member-accounts-4p30</link>
      <guid>https://dev.to/jajera/iam-identity-center-account-assignments-for-terraform-member-accounts-4p30</guid>
      <description>&lt;h2&gt;
  
  
  IAM Identity Center account assignments for Terraform member accounts
&lt;/h2&gt;

&lt;p&gt;Once &lt;strong&gt;sandbox&lt;/strong&gt; and &lt;strong&gt;development&lt;/strong&gt; &lt;strong&gt;member accounts&lt;/strong&gt; exist in &lt;strong&gt;AWS Organizations&lt;/strong&gt; (for example from Terraform as in a &lt;strong&gt;companion article&lt;/strong&gt; on that layout), they do &lt;strong&gt;not&lt;/strong&gt; automatically appear in the &lt;strong&gt;AWS access portal&lt;/strong&gt;. &lt;strong&gt;IAM Identity Center&lt;/strong&gt; (successor to AWS SSO) shows an account only when there is an &lt;strong&gt;account assignment&lt;/strong&gt;: a &lt;strong&gt;user or group&lt;/strong&gt; mapped to a &lt;strong&gt;permission set&lt;/strong&gt; in that &lt;strong&gt;account&lt;/strong&gt;. This guide explains that model and shows a compact &lt;strong&gt;Terraform&lt;/strong&gt; pattern: look up the &lt;strong&gt;SSO instance&lt;/strong&gt;, resolve an &lt;strong&gt;existing permission set&lt;/strong&gt; and &lt;strong&gt;group&lt;/strong&gt; by name, and create &lt;strong&gt;&lt;code&gt;aws_ssoadmin_account_assignment&lt;/code&gt;&lt;/strong&gt; for each member account. It assumes a &lt;strong&gt;Control Tower–friendly&lt;/strong&gt; landing zone where Identity Center is already enabled in the &lt;strong&gt;organization management&lt;/strong&gt; account; it does &lt;strong&gt;not&lt;/strong&gt; rehash &lt;strong&gt;Organizations&lt;/strong&gt; account creation itself.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; New &lt;strong&gt;member accounts&lt;/strong&gt; have &lt;strong&gt;root&lt;/strong&gt; users and &lt;strong&gt;OrganizationAccountAccessRole&lt;/strong&gt;, but &lt;strong&gt;directory users&lt;/strong&gt; do not see the accounts in the &lt;strong&gt;access portal&lt;/strong&gt; until &lt;strong&gt;assignments&lt;/strong&gt; exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approach:&lt;/strong&gt; Terraform calls &lt;strong&gt;SSO Admin&lt;/strong&gt; and &lt;strong&gt;Identity Store&lt;/strong&gt; in the &lt;strong&gt;region where Identity Center is enabled&lt;/strong&gt;, then creates one &lt;strong&gt;assignment per account&lt;/strong&gt; (sandbox and dev) for the same &lt;strong&gt;group&lt;/strong&gt; and &lt;strong&gt;permission set&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How this article reads:&lt;/strong&gt; The &lt;strong&gt;Terraform&lt;/strong&gt; below uses &lt;strong&gt;fixed illustration values&lt;/strong&gt; (region, permission set &lt;strong&gt;name&lt;/strong&gt;, group &lt;strong&gt;display name&lt;/strong&gt;) and &lt;strong&gt;no&lt;/strong&gt; &lt;code&gt;count&lt;/code&gt;, toggles, or &lt;code&gt;precondition&lt;/code&gt; blocks so the flow is easy to scan. In a real repo you usually add &lt;strong&gt;variables&lt;/strong&gt;, optional &lt;strong&gt;&lt;code&gt;count&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;guardrails&lt;/strong&gt;; a short note after the snippet calls that out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope:&lt;/strong&gt; &lt;strong&gt;Group&lt;/strong&gt; principals and an &lt;strong&gt;existing&lt;/strong&gt; permission set &lt;strong&gt;by name&lt;/strong&gt;; creating permission sets or SCIM groups is out of scope here.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAM Identity Center&lt;/strong&gt; enabled in the org (&lt;strong&gt;management&lt;/strong&gt; account or documented &lt;strong&gt;delegated admin&lt;/strong&gt;); you know the &lt;strong&gt;region&lt;/strong&gt; where the portal was turned on (often the home region you use for admin work).&lt;/li&gt;
&lt;li&gt;At least one &lt;strong&gt;permission set&lt;/strong&gt; already defined (for example &lt;strong&gt;&lt;code&gt;AdministratorAccess&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;code&gt;AWSPowerUserAccess&lt;/code&gt;&lt;/strong&gt;) and one &lt;strong&gt;group&lt;/strong&gt; with a known &lt;strong&gt;display name&lt;/strong&gt; in the Identity Center directory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform&lt;/strong&gt; &lt;strong&gt;1.5+&lt;/strong&gt; and &lt;strong&gt;hashicorp/aws&lt;/strong&gt; provider &lt;strong&gt;5.x&lt;/strong&gt; (examples below use patterns compatible with that stack).&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;IAM principal&lt;/strong&gt; that runs Terraform must be allowed &lt;strong&gt;SSO Admin&lt;/strong&gt; and &lt;strong&gt;Identity Store&lt;/strong&gt; actions used by the provider (see &lt;strong&gt;§6&lt;/strong&gt;). The same principal typically already has &lt;strong&gt;Organizations&lt;/strong&gt; access if this module also manages accounts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Member account ids&lt;/strong&gt; from Terraform outputs (or the console) for &lt;strong&gt;sandbox&lt;/strong&gt; and &lt;strong&gt;dev&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Background: &lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html" rel="noopener noreferrer"&gt;IAM Identity Center&lt;/a&gt; and Terraform &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_account_assignment" rel="noopener noreferrer"&gt;&lt;code&gt;aws_ssoadmin_account_assignment&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. How the portal decides what to list
&lt;/h3&gt;

&lt;p&gt;Flow (ASCII):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  User opens access portal URL
           |
           v
  IAM Identity Center  ---- evaluates  ----&amp;gt;  Account assignments
  (directory + permission sets)              (group X + permission set P + account A)
           |
           v
  User sees account tiles only where an assignment exists for that user or their groups
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Account assignment&lt;/strong&gt; ties three things together: &lt;strong&gt;principal&lt;/strong&gt; (here a &lt;strong&gt;group&lt;/strong&gt;), &lt;strong&gt;permission set&lt;/strong&gt; (IAM role shape in the target account), and &lt;strong&gt;target&lt;/strong&gt; (&lt;strong&gt;AWS account&lt;/strong&gt; id). Without that row, the account stays invisible in the portal for directory users even if the account is healthy in Organizations.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Terraform shape
&lt;/h3&gt;

&lt;p&gt;SSO Admin and Identity Store APIs are invoked in the &lt;strong&gt;region where IAM Identity Center is enabled&lt;/strong&gt;. The snippet uses an &lt;strong&gt;aliased&lt;/strong&gt; provider with a &lt;strong&gt;literal region&lt;/strong&gt; so it is obvious what to change first if your portal runs elsewhere (for example &lt;strong&gt;&lt;code&gt;us-east-1&lt;/code&gt;&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Replace for your org:&lt;/strong&gt; &lt;code&gt;ap-southeast-2&lt;/code&gt;, &lt;strong&gt;&lt;code&gt;AdministratorAccess&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;AWSControlTowerAdmins&lt;/code&gt;&lt;/strong&gt;, and ensure &lt;strong&gt;&lt;code&gt;aws_organizations_account.sandbox_1&lt;/code&gt;&lt;/strong&gt; / &lt;strong&gt;&lt;code&gt;dev_1&lt;/code&gt;&lt;/strong&gt; match your module (or substitute &lt;strong&gt;12-digit account ids&lt;/strong&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;alias&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"identity_center"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-southeast-2"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssoadmin_instances"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity_center&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssoadmin_permission_set"&lt;/span&gt; &lt;span class="s2"&gt;"assignment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity_center&lt;/span&gt;

  &lt;span class="nx"&gt;instance_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ssoadmin_instances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AdministratorAccess"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_identitystore_group"&lt;/span&gt; &lt;span class="s2"&gt;"assignment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity_center&lt;/span&gt;

  &lt;span class="nx"&gt;identity_store_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ssoadmin_instances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity_store_ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;alternate_identifier&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;unique_attribute&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;attribute_path&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"DisplayName"&lt;/span&gt;
      &lt;span class="nx"&gt;attribute_value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWSControlTowerAdmins"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssoadmin_account_assignment"&lt;/span&gt; &lt;span class="s2"&gt;"sandbox_1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity_center&lt;/span&gt;

  &lt;span class="nx"&gt;instance_arn&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ssoadmin_instances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;permission_set_arn&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ssoadmin_permission_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;principal_id&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_identitystore_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;group_id&lt;/span&gt;
  &lt;span class="nx"&gt;principal_type&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GROUP"&lt;/span&gt;
  &lt;span class="nx"&gt;target_id&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_organizations_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sandbox_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;target_type&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS_ACCOUNT"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssoadmin_account_assignment"&lt;/span&gt; &lt;span class="s2"&gt;"dev_1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity_center&lt;/span&gt;

  &lt;span class="nx"&gt;instance_arn&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ssoadmin_instances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;permission_set_arn&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ssoadmin_permission_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;principal_id&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_identitystore_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;group_id&lt;/span&gt;
  &lt;span class="nx"&gt;principal_type&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GROUP"&lt;/span&gt;
  &lt;span class="nx"&gt;target_id&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_organizations_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dev_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;target_type&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS_ACCOUNT"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;principal_type&lt;/code&gt;&lt;/strong&gt; can be &lt;strong&gt;&lt;code&gt;USER&lt;/code&gt;&lt;/strong&gt; if you resolve a user and pass that &lt;strong&gt;principal id&lt;/strong&gt; instead; &lt;strong&gt;groups&lt;/strong&gt; are a common default for &lt;strong&gt;team&lt;/strong&gt; access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production note:&lt;/strong&gt; Real modules usually &lt;strong&gt;parameterize&lt;/strong&gt; the region, permission set &lt;strong&gt;name&lt;/strong&gt;, and group &lt;strong&gt;display name&lt;/strong&gt; (for example with &lt;strong&gt;variables&lt;/strong&gt;), and sometimes wrap assignments in &lt;strong&gt;&lt;code&gt;count&lt;/code&gt;&lt;/strong&gt; or use &lt;strong&gt;&lt;code&gt;lifecycle.precondition&lt;/code&gt;&lt;/strong&gt; so you never plan with an &lt;strong&gt;empty&lt;/strong&gt; group name or enable SSO changes by mistake. The literal version above is only for &lt;strong&gt;reading&lt;/strong&gt; the happy path.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. IAM permissions for the Terraform principal
&lt;/h3&gt;

&lt;p&gt;Your role or user needs API access for &lt;strong&gt;read&lt;/strong&gt; paths (instance, permission set, group lookup) and &lt;strong&gt;write&lt;/strong&gt; paths (&lt;strong&gt;CreateAccountAssignment&lt;/strong&gt;, &lt;strong&gt;DeleteAccountAssignment&lt;/strong&gt; on destroy). Typical action families include &lt;strong&gt;SSO Admin&lt;/strong&gt; (&lt;code&gt;sso:ListInstances&lt;/code&gt;, &lt;code&gt;sso:DescribePermissionSet&lt;/code&gt;, &lt;code&gt;sso:CreateAccountAssignment&lt;/code&gt;, …) and &lt;strong&gt;Identity Store&lt;/strong&gt; (&lt;code&gt;identitystore:GetGroupId&lt;/code&gt;, &lt;code&gt;identitystore:DescribeGroup&lt;/code&gt;, …). &lt;strong&gt;Tighten&lt;/strong&gt; ARNs and actions in a real policy; start from the &lt;strong&gt;Terraform plan&lt;/strong&gt; error messages if something is missing.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Summary: Copy-paste
&lt;/h3&gt;

&lt;p&gt;After pasting the resources into your root module and fixing the &lt;strong&gt;illustration&lt;/strong&gt; strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YourManagementProfile
terraform plan
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm in &lt;strong&gt;IAM Identity Center → Multi-account permissions → AWS accounts&lt;/strong&gt; (wording varies by console revision) that assignments exist for &lt;strong&gt;sandbox&lt;/strong&gt; and &lt;strong&gt;dev&lt;/strong&gt;. Ask a member of the &lt;strong&gt;group&lt;/strong&gt; to open the &lt;strong&gt;access portal&lt;/strong&gt; and verify &lt;strong&gt;two&lt;/strong&gt; new account tiles (after &lt;strong&gt;propagation&lt;/strong&gt;, which can take a short time).&lt;/p&gt;




&lt;h3&gt;
  
  
  7. Troubleshooting
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AccessDenied&lt;/code&gt; on &lt;code&gt;sso:ListInstances&lt;/code&gt;:&lt;/strong&gt; The caller’s policy omits &lt;strong&gt;SSO Admin&lt;/strong&gt; permissions, or Terraform is using the &lt;strong&gt;wrong region&lt;/strong&gt; for the &lt;strong&gt;aliased&lt;/strong&gt; provider.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GetGroupId&lt;/code&gt; validation / empty attribute:&lt;/strong&gt; &lt;strong&gt;&lt;code&gt;attribute_value&lt;/code&gt;&lt;/strong&gt; for the group was empty or wrong; set it to the exact &lt;strong&gt;display name&lt;/strong&gt; from &lt;strong&gt;IAM Identity Center → Groups&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permission set not found:&lt;/strong&gt; The &lt;strong&gt;&lt;code&gt;name&lt;/code&gt;&lt;/strong&gt; in &lt;strong&gt;&lt;code&gt;aws_ssoadmin_permission_set&lt;/code&gt;&lt;/strong&gt; does not match any permission set &lt;strong&gt;name&lt;/strong&gt; in your directory (names are not always identical to &lt;strong&gt;AWS managed&lt;/strong&gt; policy names in the console).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Group not found / ambiguous display name:&lt;/strong&gt; &lt;strong&gt;DisplayName&lt;/strong&gt; must be unique enough for the lookup; prefer the exact string from &lt;strong&gt;Groups&lt;/strong&gt; in the console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account still missing in portal:&lt;/strong&gt; Assignment targets the wrong &lt;strong&gt;account id&lt;/strong&gt;; user is not in the assigned &lt;strong&gt;group&lt;/strong&gt;; or &lt;strong&gt;propagation&lt;/strong&gt; not finished yet.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  8. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html" rel="noopener noreferrer"&gt;What is IAM Identity Center?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/howtocreatepermissionset.html" rel="noopener noreferrer"&gt;Manage IAM Identity Center permission sets and assignments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_account_assignment" rel="noopener noreferrer"&gt;Terraform aws_ssoadmin_account_assignment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_group" rel="noopener noreferrer"&gt;Terraform data aws_identitystore_group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssoadmin_instances" rel="noopener noreferrer"&gt;Terraform data aws_ssoadmin_instances&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terraform</category>
      <category>aws</category>
      <category>sso</category>
      <category>organizations</category>
    </item>
    <item>
      <title>Terraform member accounts for a Control Tower sandbox OU and a custom dev OU</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Wed, 15 Apr 2026 19:06:36 +0000</pubDate>
      <link>https://dev.to/jajera/terraform-member-accounts-for-a-control-tower-sandbox-ou-and-a-custom-dev-ou-3ff2</link>
      <guid>https://dev.to/jajera/terraform-member-accounts-for-a-control-tower-sandbox-ou-and-a-custom-dev-ou-3ff2</guid>
      <description>&lt;h2&gt;
  
  
  Terraform member accounts for a Control Tower sandbox OU and a custom dev OU
&lt;/h2&gt;

&lt;p&gt;If you already run a landing zone where the &lt;strong&gt;sandbox&lt;/strong&gt; organizational unit (OU) exists under &lt;strong&gt;AWS Control Tower&lt;/strong&gt;, you may still want &lt;strong&gt;additional member accounts&lt;/strong&gt; created and updated with &lt;strong&gt;Infrastructure as Code&lt;/strong&gt; instead of only the console. This guide walks through a small Terraform root module pattern: one account in the &lt;strong&gt;existing&lt;/strong&gt; sandbox OU (by id) and one in a &lt;strong&gt;Terraform-managed&lt;/strong&gt; &lt;strong&gt;Development&lt;/strong&gt; OU, with &lt;strong&gt;remote state in S3&lt;/strong&gt;. The real repository may add optional guards or integrations beyond Organizations account creation; this post focuses on that core path. &lt;strong&gt;Continuous delivery&lt;/strong&gt; (for example GitHub Actions and OIDC) is left for a separate article.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Goal:&lt;/strong&gt; Repeatable &lt;strong&gt;AWS Organizations&lt;/strong&gt; member accounts for &lt;strong&gt;sandbox&lt;/strong&gt; and &lt;strong&gt;development&lt;/strong&gt;, tagged and placed under the right OUs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where it runs:&lt;/strong&gt; The &lt;strong&gt;management account&lt;/strong&gt; (or another principal with &lt;strong&gt;Organizations&lt;/strong&gt; APIs and trust to create accounts).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State:&lt;/strong&gt; Encrypted &lt;strong&gt;S3&lt;/strong&gt; backend with a &lt;strong&gt;lock&lt;/strong&gt; compatible with modern Terraform S3 native state locking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credentials:&lt;/strong&gt; Use whatever your team standardizes on for interactive or &lt;strong&gt;manual&lt;/strong&gt; applies (for example an &lt;strong&gt;IAM role&lt;/strong&gt; in the management account via &lt;strong&gt;SSO&lt;/strong&gt; or &lt;strong&gt;assume role&lt;/strong&gt;). The snippets below do not depend on a specific client tool beyond the Terraform CLI.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;AWS Organization&lt;/strong&gt; with permission to &lt;strong&gt;create accounts&lt;/strong&gt; and &lt;strong&gt;OUs&lt;/strong&gt; from the account where you run Terraform.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;organizational unit id&lt;/strong&gt; for the sandbox OU where the first account should live (in a Control Tower setup this OU already exists; you copy its id from the console or CLI).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two unique root user email addresses&lt;/strong&gt; that are &lt;strong&gt;not&lt;/strong&gt; already used as the root email of any AWS account (plus-addressing on a single mailbox is fine).&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;S3 bucket&lt;/strong&gt; for Terraform state (create it out of band or in another stack); the &lt;strong&gt;IAM principal&lt;/strong&gt; you use for &lt;code&gt;terraform apply&lt;/code&gt; needs &lt;strong&gt;read/write&lt;/strong&gt; to that bucket and key (and any &lt;strong&gt;KMS&lt;/strong&gt; key policy if you use SSE-KMS).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform&lt;/strong&gt; &lt;strong&gt;1.5+&lt;/strong&gt; and the &lt;strong&gt;hashicorp/aws&lt;/strong&gt; provider (&lt;strong&gt;5.x&lt;/strong&gt; in the example below).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful background: &lt;a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_introduction.html" rel="noopener noreferrer"&gt;AWS Organizations User Guide&lt;/a&gt; and the Terraform resources &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/organizations_account" rel="noopener noreferrer"&gt;&lt;code&gt;aws_organizations_account&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/organizations_organizational_unit" rel="noopener noreferrer"&gt;&lt;code&gt;aws_organizations_organizational_unit&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Architecture
&lt;/h3&gt;

&lt;p&gt;Terraform runs in the &lt;strong&gt;management account&lt;/strong&gt; (from your workstation or any runner with the right credentials). It reads the organization root, creates (or refreshes) a &lt;strong&gt;Development&lt;/strong&gt; OU, creates two &lt;strong&gt;member accounts&lt;/strong&gt;, and persists state in &lt;strong&gt;S3&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Flow (ASCII):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Operator / runner                    Management account              AWS Organizations
  ----------------                     ------------------              -----------------

  Terraform CLI  ------------------&amp;gt;  Terraform run  -------------&amp;gt;  S3 (remote state)
  (plan / apply)                           |
                                           (Organizations API)
                                                |
                                                v
                                         Organization root
                                              |
                      +-----------------------+-----------------------+
                      |                                               |
                Sandbox OU                                     Development OU
              (already exists,                         (created by Terraform)
               e.g. Control Tower)
                      |                                               |
                      v                                               v
              Sandbox member account                         Dev member account
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Terraform building blocks
&lt;/h3&gt;

&lt;h4&gt;
  
  
  4.1 Provider and versions
&lt;/h4&gt;

&lt;p&gt;Pin Terraform and the AWS provider; set the &lt;strong&gt;region&lt;/strong&gt; your team uses for the provider (Organizations APIs are global in behavior, but the provider still needs a region).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.5.0"&lt;/span&gt;

  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.98.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.2 Data sources
&lt;/h4&gt;

&lt;p&gt;Read the &lt;strong&gt;current account&lt;/strong&gt; and the &lt;strong&gt;organization&lt;/strong&gt; so you can anchor the &lt;strong&gt;root&lt;/strong&gt; id for new OUs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_caller_identity"&lt;/span&gt; &lt;span class="s2"&gt;"current"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_organizations_organization"&lt;/span&gt; &lt;span class="s2"&gt;"current"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.3 Locals: sandbox OU id and tags
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;sandbox&lt;/strong&gt; account is placed under an OU that &lt;strong&gt;already exists&lt;/strong&gt; (for example one created by &lt;strong&gt;Control Tower&lt;/strong&gt;). That OU’s id is &lt;strong&gt;environment-specific&lt;/strong&gt;; store it in &lt;strong&gt;locals&lt;/strong&gt; (or a variable) instead of hard-coding in multiple resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;# Example: existing sandbox OU — replace with your OU id from Organizations or Control Tower.&lt;/span&gt;
  &lt;span class="nx"&gt;sandbox_ou_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ou-xxxx-xxxxxxxx"&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;owner&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt;
    &lt;span class="nx"&gt;service&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"core"&lt;/span&gt;
    &lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;Development&lt;/strong&gt; OU, by contrast, is &lt;strong&gt;created by Terraform&lt;/strong&gt; in the next subsection.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.3.1 Reusing the Control Tower sandbox OU (tradeoffs)
&lt;/h4&gt;

&lt;p&gt;There is &lt;strong&gt;no universal right answer&lt;/strong&gt;; it is a &lt;strong&gt;tradeoff&lt;/strong&gt; your team agrees on.&lt;/p&gt;

&lt;p&gt;When &lt;strong&gt;reusing&lt;/strong&gt; the existing sandbox OU tends to make sense:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alignment with landing zone:&lt;/strong&gt; Control Tower (or your design) already defined a &lt;strong&gt;sandbox&lt;/strong&gt; OU for &lt;strong&gt;experimental&lt;/strong&gt; workloads; putting Terraform-created members there keeps the &lt;strong&gt;same guardrails and enrollment&lt;/strong&gt; model as other sandbox accounts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less OU sprawl:&lt;/strong&gt; You avoid a parallel “sandbox” tree that confuses auditors or operators.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed:&lt;/strong&gt; The OU &lt;strong&gt;already exists&lt;/strong&gt;; Terraform only needs a &lt;strong&gt;stable id&lt;/strong&gt; (&lt;code&gt;locals.sandbox_ou_id&lt;/code&gt;), not another OU resource.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a &lt;strong&gt;different&lt;/strong&gt; OU might be a better fit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolation:&lt;/strong&gt; You want a &lt;strong&gt;dedicated OU&lt;/strong&gt; for &lt;strong&gt;platform&lt;/strong&gt; or &lt;strong&gt;CI-owned&lt;/strong&gt; sandboxes so &lt;strong&gt;Service Catalog / Account Factory&lt;/strong&gt; sandboxes stay separate from &lt;strong&gt;Terraform-vended&lt;/strong&gt; ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Different SCPs:&lt;/strong&gt; The Control Tower sandbox OU carries &lt;strong&gt;service control policies&lt;/strong&gt; you do not want on these accounts; a &lt;strong&gt;separate OU&lt;/strong&gt; can carry &lt;strong&gt;different&lt;/strong&gt; SCPs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational clarity:&lt;/strong&gt; You prefer &lt;strong&gt;one automation path per OU&lt;/strong&gt; so it is obvious &lt;strong&gt;how&lt;/strong&gt; an account was created.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither choice is inherently wrong. The &lt;strong&gt;existing sandbox OU&lt;/strong&gt; is a &lt;strong&gt;reasonable default&lt;/strong&gt; for &lt;strong&gt;consistency&lt;/strong&gt; and &lt;strong&gt;shared&lt;/strong&gt; sandbox policy; another OU is equally valid when &lt;strong&gt;separation&lt;/strong&gt; or &lt;strong&gt;policy&lt;/strong&gt; needs differ. Document the choice in your &lt;strong&gt;runbook&lt;/strong&gt; so future readers know &lt;strong&gt;why&lt;/strong&gt; the id is what it is.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.4 Development organizational unit
&lt;/h4&gt;

&lt;p&gt;Create an OU under the organization &lt;strong&gt;root&lt;/strong&gt; (first element of &lt;code&gt;roots&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_organizations_organizational_unit"&lt;/span&gt; &lt;span class="s2"&gt;"development"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Development"&lt;/span&gt;
  &lt;span class="nx"&gt;parent_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_organizations_organization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roots&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Development"&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.5 Member accounts
&lt;/h4&gt;

&lt;p&gt;Each member account needs a &lt;strong&gt;unique root email&lt;/strong&gt;, the standard &lt;strong&gt;&lt;code&gt;OrganizationAccountAccessRole&lt;/code&gt;&lt;/strong&gt; name (so administrators can assume into the account from the org), a &lt;strong&gt;parent OU&lt;/strong&gt;, and &lt;strong&gt;tags&lt;/strong&gt;. Ignore &lt;strong&gt;iam user access to billing&lt;/strong&gt; if your org policy manages that outside Terraform.&lt;/p&gt;

&lt;p&gt;Below, &lt;strong&gt;&lt;code&gt;sandbox_1&lt;/code&gt;&lt;/strong&gt; uses &lt;strong&gt;&lt;code&gt;local.sandbox_ou_id&lt;/code&gt;&lt;/strong&gt;; &lt;strong&gt;&lt;code&gt;dev_1&lt;/code&gt;&lt;/strong&gt; uses the &lt;strong&gt;Development&lt;/strong&gt; OU resource id. The lifecycle block here is the &lt;strong&gt;minimal&lt;/strong&gt; pattern for teaching; a production repo might add other &lt;code&gt;lifecycle&lt;/code&gt; rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_organizations_account"&lt;/span&gt; &lt;span class="s2"&gt;"sandbox_1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example-sandbox-1"&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sandbox_account_email&lt;/span&gt;
  &lt;span class="nx"&gt;role_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"OrganizationAccountAccessRole"&lt;/span&gt;
  &lt;span class="nx"&gt;parent_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sandbox_ou_id&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example-sandbox-1"&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sandbox"&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ignore_changes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iam_user_access_to_billing&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_organizations_account"&lt;/span&gt; &lt;span class="s2"&gt;"dev_1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example-dev-1"&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dev_account_email&lt;/span&gt;
  &lt;span class="nx"&gt;role_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"OrganizationAccountAccessRole"&lt;/span&gt;
  &lt;span class="nx"&gt;parent_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_organizations_organizational_unit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;development&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example-dev-1"&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ignore_changes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iam_user_access_to_billing&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.6 Remote state backend
&lt;/h4&gt;

&lt;p&gt;Point the backend at your bucket, key, and region. &lt;strong&gt;&lt;code&gt;use_lockfile&lt;/code&gt;&lt;/strong&gt; enables S3-native locking in supported Terraform versions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"your-tf-state-bucket"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"account-factory/terraform.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-southeast-2"&lt;/span&gt;
    &lt;span class="nx"&gt;encrypt&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;use_lockfile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.7 Variables and tfvars
&lt;/h4&gt;

&lt;p&gt;At minimum, pass the two &lt;strong&gt;root emails&lt;/strong&gt;. Optionally override &lt;strong&gt;region&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS region for the provider."&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-southeast-2"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"sandbox_account_email"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Root email address for the sandbox AWS account."&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"dev_account_email"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Root email address for the development AWS account."&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example &lt;strong&gt;&lt;code&gt;terraform.tfvars&lt;/code&gt;&lt;/strong&gt; (keep out of version control if it is sensitive):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;sandbox_account_email&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"you+sandbox@example.com"&lt;/span&gt;
&lt;span class="nx"&gt;dev_account_email&lt;/span&gt;     &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"you+dev@example.com"&lt;/span&gt;
&lt;span class="c1"&gt;# region = "ap-southeast-2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.8 Outputs
&lt;/h4&gt;

&lt;p&gt;Expose &lt;strong&gt;organization&lt;/strong&gt; metadata, &lt;strong&gt;OU&lt;/strong&gt; ids, and &lt;strong&gt;account&lt;/strong&gt; ids and ARNs for downstream stacks or runbooks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"organization_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_organizations_organization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"development_ou_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_organizations_organizational_unit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;development&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"sandbox_account_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_organizations_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sandbox_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"dev_account_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_organizations_account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dev_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Summary: Copy-paste
&lt;/h3&gt;

&lt;p&gt;From the module root, with credentials for the &lt;strong&gt;management account&lt;/strong&gt; (for example &lt;strong&gt;&lt;code&gt;AWS_PROFILE&lt;/code&gt;&lt;/strong&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YourManagementProfile
terraform init
terraform plan
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep &lt;strong&gt;root emails&lt;/strong&gt; stable; changing an account’s root email later is a manual process. Review &lt;strong&gt;&lt;code&gt;terraform plan&lt;/code&gt;&lt;/strong&gt; output before &lt;strong&gt;&lt;code&gt;apply&lt;/code&gt;&lt;/strong&gt;, and use your team’s normal change control for production organizations.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Troubleshooting
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;EmailAlreadyExists&lt;/code&gt; / account creation fails:&lt;/strong&gt; The &lt;strong&gt;root email&lt;/strong&gt; is already tied to another AWS account. Pick a &lt;strong&gt;new&lt;/strong&gt; unique address (plus-addressing helps).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AccessDenied&lt;/code&gt; on Organizations:&lt;/strong&gt; The caller is not in the &lt;strong&gt;management account&lt;/strong&gt; or the principal lacks &lt;strong&gt;Organizations&lt;/strong&gt; permissions for the operations in your plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 backend errors:&lt;/strong&gt; Bucket name, &lt;strong&gt;key&lt;/strong&gt;, &lt;strong&gt;region&lt;/strong&gt;, or &lt;strong&gt;KMS&lt;/strong&gt; policy mismatch; the principal needs &lt;strong&gt;list/get/put&lt;/strong&gt; on the state &lt;strong&gt;prefix&lt;/strong&gt; and lock object if used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong sandbox OU:&lt;/strong&gt; The &lt;strong&gt;sandbox&lt;/strong&gt; account lands in the wrong OU when &lt;strong&gt;&lt;code&gt;local.sandbox_ou_id&lt;/code&gt;&lt;/strong&gt; is incorrect; re-read it from &lt;strong&gt;Organizations&lt;/strong&gt; or your landing zone console.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  7. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_introduction.html" rel="noopener noreferrer"&gt;AWS Organizations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/organizations_account" rel="noopener noreferrer"&gt;Terraform aws_organizations_account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/organizations_organizational_unit" rel="noopener noreferrer"&gt;Terraform aws_organizations_organizational_unit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/language/settings/backends/s3" rel="noopener noreferrer"&gt;Terraform S3 backend&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terraform</category>
      <category>aws</category>
      <category>organizations</category>
      <category>iac</category>
    </item>
    <item>
      <title>GitHub Webhook Secrets and the Terraform Public Registry</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Tue, 14 Apr 2026 20:15:29 +0000</pubDate>
      <link>https://dev.to/jajera/github-webhook-secrets-and-the-terraform-public-registry-5ef1</link>
      <guid>https://dev.to/jajera/github-webhook-secrets-and-the-terraform-public-registry-5ef1</guid>
      <description>&lt;h2&gt;
  
  
  GitHub Webhook Secrets and the Terraform Public Registry
&lt;/h2&gt;

&lt;p&gt;New &lt;strong&gt;semver tags&lt;/strong&gt; in GitHub can fail to appear on &lt;a href="https://registry.terraform.io/" rel="noopener noreferrer"&gt;registry.terraform.io&lt;/a&gt; when the &lt;strong&gt;Terraform Registry&lt;/strong&gt; webhook’s &lt;strong&gt;Secret&lt;/strong&gt; in GitHub no longer matches what the registry uses to verify deliveries. GitHub signs each delivery with that secret; the registry must validate the signature with the same value. This article states that model, what breaks when the two sides diverge, and how to recover using HashiCorp’s documented steps.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; GitHub computes an HMAC signature for webhook payloads (see &lt;a href="https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries" rel="noopener noreferrer"&gt;Validating webhook deliveries&lt;/a&gt;). The registry endpoint must verify it using the &lt;strong&gt;same&lt;/strong&gt; secret tied to your module’s integration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure mode:&lt;/strong&gt; Updating &lt;strong&gt;Secret&lt;/strong&gt; only under &lt;strong&gt;GitHub → Settings → Webhooks&lt;/strong&gt; changes what GitHub signs with. The registry does not read that field from your repo; it keeps the secret from the integration that created or last reconciled the hook. Signatures then fail and new versions do not publish.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recovery:&lt;/strong&gt; Keep a &lt;strong&gt;single&lt;/strong&gt; webhook to &lt;strong&gt;&lt;code&gt;https://registry.terraform.io/hooks/github&lt;/code&gt;&lt;/strong&gt;, run &lt;strong&gt;Resync Module&lt;/strong&gt; from the registry UI, then confirm deliveries: push a &lt;strong&gt;new&lt;/strong&gt; semver tag &lt;strong&gt;or&lt;/strong&gt; use GitHub’s &lt;strong&gt;Redeliver&lt;/strong&gt; on a &lt;strong&gt;failed&lt;/strong&gt; delivery for the tag push you still want published (same payload, signed again with the &lt;strong&gt;current&lt;/strong&gt; secret). If publishing still fails, use &lt;a href="https://developer.hashicorp.com/terraform/registry#getting-help" rel="noopener noreferrer"&gt;registry support&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide is for the &lt;strong&gt;public&lt;/strong&gt; Terraform Registry and &lt;strong&gt;public&lt;/strong&gt; GitHub modules. &lt;strong&gt;Terraform Enterprise&lt;/strong&gt; and other private registries use different hosts and procedures.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Module already published on the &lt;a href="https://registry.terraform.io/" rel="noopener noreferrer"&gt;Terraform Registry&lt;/a&gt; from a &lt;strong&gt;public&lt;/strong&gt; GitHub repository&lt;/li&gt;
&lt;li&gt;Permission to use &lt;strong&gt;Manage Module&lt;/strong&gt; on the module page and to edit &lt;strong&gt;Webhooks&lt;/strong&gt; on the GitHub repository (&lt;strong&gt;Settings → Webhooks&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;semver&lt;/strong&gt; tag to test with (push a &lt;strong&gt;new&lt;/strong&gt; one, or reuse an existing tag whose registry publish failed and still needs to succeed), per &lt;a href="https://developer.hashicorp.com/terraform/registry/modules/publish" rel="noopener noreferrer"&gt;module publishing&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. What not to do
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Do not&lt;/strong&gt; paste a new random value into the webhook &lt;strong&gt;Secret&lt;/strong&gt; in GitHub alone, expecting the public registry to pick it up. There is no documented self-serve screen on the registry where you paste the same value to pair it. Unilateral edits in GitHub desynchronize signing and verification.&lt;/p&gt;

&lt;p&gt;If you already did that, treat the next sections as recovery, not prevention.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Recovery: one webhook, then Resync
&lt;/h3&gt;

&lt;p&gt;Per the &lt;a href="https://developer.hashicorp.com/terraform/registry/faq" rel="noopener noreferrer"&gt;Terraform registry FAQ&lt;/a&gt;, &lt;strong&gt;Resync&lt;/strong&gt; for a module makes the registry &lt;strong&gt;ensure the repository has a webhook&lt;/strong&gt; for automatic publishing and reconciles version gaps. The FAQ also recommends fixing webhook problems before relying on resync alone.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Settings → Webhooks&lt;/strong&gt; on the GitHub repository.&lt;/li&gt;
&lt;li&gt;Leave &lt;strong&gt;exactly one&lt;/strong&gt; active webhook whose payload URL is &lt;strong&gt;&lt;code&gt;https://registry.terraform.io/hooks/github&lt;/code&gt;&lt;/strong&gt;. If several point there, &lt;strong&gt;delete the extras&lt;/strong&gt; (the FAQ calls out duplicate hooks as a cause of publishing issues).&lt;/li&gt;
&lt;li&gt;On &lt;a href="https://registry.terraform.io/" rel="noopener noreferrer"&gt;registry.terraform.io&lt;/a&gt;, open the module → &lt;strong&gt;Manage Module&lt;/strong&gt; → &lt;strong&gt;Resync Module&lt;/strong&gt;. Wait for completion; avoid starting overlapping resyncs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirm the hook:&lt;/strong&gt; Open the webhook → &lt;strong&gt;Recent Deliveries&lt;/strong&gt; on GitHub.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Push a new semver tag&lt;/strong&gt; and check that the new delivery succeeds (typically &lt;strong&gt;HTTP 200&lt;/strong&gt;), then confirm the version on the module page, &lt;strong&gt;or&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redeliver&lt;/strong&gt; a &lt;strong&gt;failed&lt;/strong&gt; delivery whose payload is still the &lt;strong&gt;tag push&lt;/strong&gt; for the semver version you want. GitHub sends the &lt;strong&gt;same&lt;/strong&gt; event body again; signatures use the webhook &lt;strong&gt;Secret&lt;/strong&gt; as it is &lt;strong&gt;after&lt;/strong&gt; your fixes, so this can publish that version without another tag. Use the delivery row that matches the correct tag ref.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Redeliver instead of a new tag
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Yes — that is an acceptable approach&lt;/strong&gt; when the semver tag &lt;strong&gt;already exists&lt;/strong&gt; and the only problem was the webhook (for example signature failure after a bad secret edit). &lt;strong&gt;Redeliver&lt;/strong&gt; is a normal GitHub feature for retrying a delivery; you are replaying the same &lt;strong&gt;push&lt;/strong&gt; event the registry would have seen on a successful first attempt, not inventing a new release artifact in Git.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefer a new tag&lt;/strong&gt; when you need a &lt;strong&gt;new&lt;/strong&gt; module version anyway, when there is &lt;strong&gt;no&lt;/strong&gt; failed delivery to replay (nothing reached GitHub’s hook list), or when you want a straight-line test from “tag push” to “delivery” without hunting history.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefer redeliver&lt;/strong&gt; when bumping the version number would be &lt;strong&gt;artificial&lt;/strong&gt; (the tag is already the release you mean) and you only need the registry to &lt;strong&gt;accept the same event&lt;/strong&gt; again after fixing the hook.&lt;/p&gt;

&lt;p&gt;If a single hook and resync do not restore publishing, &lt;a href="https://developer.hashicorp.com/terraform/registry#getting-help" rel="noopener noreferrer"&gt;contact registry support&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Configuration checks
&lt;/h3&gt;

&lt;p&gt;Use these when deliveries succeed but versions still look wrong, or as routine verification:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Payload URL:&lt;/strong&gt; &lt;strong&gt;&lt;code&gt;https://registry.terraform.io/hooks/github&lt;/code&gt;&lt;/strong&gt; (HTTPS, that host, that path).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tags:&lt;/strong&gt; &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;Semantic version&lt;/a&gt; identifiers only, optional leading &lt;strong&gt;&lt;code&gt;v&lt;/code&gt;&lt;/strong&gt;. Other ref names do not create registry versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Events:&lt;/strong&gt; The hook must receive events for the activity that publishes versions (for example &lt;strong&gt;push&lt;/strong&gt; events that include tag pushes). Overly narrow event filters can skip the tag you care about.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  6. Summary: Copy-Paste
&lt;/h3&gt;

&lt;p&gt;Push a new version tag when you prefer a fresh event (adjust remote and tag):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag v1.0.1
git push origin v1.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open repository webhooks (replace &lt;code&gt;OWNER&lt;/code&gt; and &lt;code&gt;REPO&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://github.com/OWNER/REPO/settings/hooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After opening the Terraform Registry webhook there, use &lt;strong&gt;Recent Deliveries&lt;/strong&gt; → a failed row → &lt;strong&gt;Redeliver&lt;/strong&gt; when you have already fixed the hook and want to retry the &lt;strong&gt;same&lt;/strong&gt; tag event without pushing another tag.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. Troubleshooting
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;What to try&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No new registry version after a tag push&lt;/td&gt;
&lt;td&gt;Missing or failing webhook, duplicate hooks, or signature failure after a secret-only GitHub edit&lt;/td&gt;
&lt;td&gt;One hook to &lt;code&gt;registry.terraform.io/hooks/github&lt;/code&gt;, &lt;strong&gt;Resync Module&lt;/strong&gt;, then push a &lt;strong&gt;new&lt;/strong&gt; semver tag &lt;strong&gt;or&lt;/strong&gt; &lt;strong&gt;Redeliver&lt;/strong&gt; the failed delivery for that tag; re-check &lt;strong&gt;Recent Deliveries&lt;/strong&gt; and the module page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Recent Deliveries&lt;/strong&gt; show errors after changing &lt;strong&gt;Secret&lt;/strong&gt; in GitHub&lt;/td&gt;
&lt;td&gt;GitHub and registry no longer share the same secret&lt;/td&gt;
&lt;td&gt;Dedupe hooks, &lt;strong&gt;Resync Module&lt;/strong&gt;; if still failing, &lt;strong&gt;registry support&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delivery succeeds but no version&lt;/td&gt;
&lt;td&gt;Invalid tag shape or delay&lt;/td&gt;
&lt;td&gt;Fix tag to semver; wait; &lt;strong&gt;Resync&lt;/strong&gt; if needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple registry webhooks&lt;/td&gt;
&lt;td&gt;Stale or conflicting registrations&lt;/td&gt;
&lt;td&gt;Delete duplicates, &lt;strong&gt;Resync&lt;/strong&gt; per &lt;a href="https://developer.hashicorp.com/terraform/registry/faq" rel="noopener noreferrer"&gt;FAQ&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  8. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.hashicorp.com/terraform/registry/modules/publish" rel="noopener noreferrer"&gt;Publish modules&lt;/a&gt; — naming, tags, &lt;strong&gt;Resync Module&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.hashicorp.com/terraform/registry/faq" rel="noopener noreferrer"&gt;Terraform registry FAQ&lt;/a&gt; — &lt;strong&gt;Resync&lt;/strong&gt;, duplicate webhooks, versions not appearing&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries" rel="noopener noreferrer"&gt;Validating webhook deliveries&lt;/a&gt; — GitHub signatures and the webhook secret&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/rest/repos/webhooks?apiVersion=2022-11-28#redeliver-a-delivery-for-a-repository-webhook" rel="noopener noreferrer"&gt;Redeliver a delivery for a repository webhook&lt;/a&gt; — API equivalent of the &lt;strong&gt;Redeliver&lt;/strong&gt; control in the delivery UI&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terraform</category>
      <category>github</category>
      <category>webhooks</category>
      <category>registry</category>
    </item>
    <item>
      <title>AWS IAM Identity Center: Custom Access Portal URL</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Tue, 14 Apr 2026 12:11:29 +0000</pubDate>
      <link>https://dev.to/jajera/aws-iam-identity-center-custom-access-portal-url-963</link>
      <guid>https://dev.to/jajera/aws-iam-identity-center-custom-access-portal-url-963</guid>
      <description>&lt;h2&gt;
  
  
  AWS IAM Identity Center: Custom Access Portal URL
&lt;/h2&gt;

&lt;p&gt;After you enable &lt;strong&gt;IAM Identity Center&lt;/strong&gt;, the default &lt;strong&gt;AWS access portal&lt;/strong&gt; URL uses an opaque subdomain under &lt;code&gt;awsapps.com&lt;/code&gt;. You can replace that prefix once with a &lt;strong&gt;custom access portal URL&lt;/strong&gt; so sign-in links are easier to recognize and communicate. This guide walks through what that means, how to set it in the console, and what to verify afterward.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;p&gt;This article covers how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand the difference between the &lt;strong&gt;default&lt;/strong&gt; and &lt;strong&gt;custom&lt;/strong&gt; access portal URL (&lt;code&gt;https://…awsapps.com/start&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Customize the subdomain &lt;strong&gt;once&lt;/strong&gt; from the IAM Identity Center console (no separate AWS charge for this step)&lt;/li&gt;
&lt;li&gt;Confirm sign-in still works and refresh &lt;strong&gt;bookmarks, runbooks, and onboarding docs&lt;/strong&gt; that referenced the old URL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; cover bringing your own DNS name (for example &lt;code&gt;sso.example.com&lt;/code&gt;); the portal stays on &lt;strong&gt;&lt;code&gt;*.awsapps.com&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Management account&lt;/strong&gt; (or delegated admin where your org’s Identity Center instance lives) with permission to change Identity Center settings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Identity Center enabled&lt;/strong&gt; for the organization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Console access&lt;/strong&gt; in the &lt;strong&gt;Identity Center Region&lt;/strong&gt; (the region shown as primary for your instance; for example &lt;code&gt;ap-southeast-2&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;stable subdomain label&lt;/strong&gt; you are willing to keep: AWS documents that &lt;strong&gt;you cannot edit the access portal URL after you customize it&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. What changes when you customize the URL
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Default vs custom
&lt;/h4&gt;

&lt;p&gt;By default, the portal looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://xxxxxxxxxx.awsapps.com/start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After customization it becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://your-subdomain.awsapps.com/start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the &lt;strong&gt;first label&lt;/strong&gt; (the part before &lt;code&gt;.awsapps.com&lt;/code&gt;) changes. The path &lt;code&gt;/start&lt;/code&gt; and the fact that traffic uses &lt;strong&gt;HTTPS to &lt;code&gt;awsapps.com&lt;/code&gt;&lt;/strong&gt; stay the same.&lt;/p&gt;

&lt;h4&gt;
  
  
  One-time operation
&lt;/h4&gt;

&lt;p&gt;AWS states clearly: &lt;strong&gt;if you change the AWS access portal URL, you cannot edit it later.&lt;/strong&gt; If the &lt;strong&gt;Customize&lt;/strong&gt; control does not appear under the portal URL in the dashboard, the URL has already been customized. Treat the choice like a &lt;strong&gt;permanent hostname&lt;/strong&gt; for your organization.&lt;/p&gt;

&lt;h4&gt;
  
  
  Not the same as “Instance name”
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;Instance name&lt;/strong&gt; in &lt;strong&gt;Settings summary&lt;/strong&gt; is a console-friendly label. The &lt;strong&gt;access portal URL&lt;/strong&gt; is what users type or bookmark. You can set both; they serve different purposes.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Customize the access portal URL (console)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://console.aws.amazon.com/singlesignon/" rel="noopener noreferrer"&gt;IAM Identity Center console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Region&lt;/strong&gt; where your Identity Center instance is registered if the console prompts you (must match your instance’s primary region).&lt;/li&gt;
&lt;li&gt;In the navigation pane, open &lt;strong&gt;Dashboard&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;Settings summary&lt;/strong&gt;, find the &lt;strong&gt;AWS access portal URL&lt;/strong&gt; and choose &lt;strong&gt;Customize&lt;/strong&gt; (only shown if customization is still available).&lt;/li&gt;
&lt;li&gt;Enter your desired &lt;strong&gt;subdomain&lt;/strong&gt; and save.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When the operation completes, use the new URL to open the access portal and confirm the sign-in page loads.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. After you save
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tell your users&lt;/strong&gt; the new portal URL and ask them to &lt;strong&gt;update bookmarks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update internal documentation&lt;/strong&gt;, wiki pages, and &lt;strong&gt;new-hire instructions&lt;/strong&gt; that still point at the old hostname.&lt;/li&gt;
&lt;li&gt;If you use &lt;strong&gt;CLI or IDE profiles&lt;/strong&gt; that reference the portal URL (for example AWS CLI &lt;code&gt;aws configure sso&lt;/code&gt; / &lt;code&gt;sso_start_url&lt;/code&gt;), align those configs with the &lt;strong&gt;new&lt;/strong&gt; URL on each machine.&lt;/li&gt;
&lt;li&gt;If anything in your &lt;strong&gt;IdP or application configuration&lt;/strong&gt; hard-coded the old portal URL, plan a coordinated update (uncommon for the bare portal hostname, but easy to miss in custom integrations).&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  6. Summary: Copy-Paste
&lt;/h3&gt;

&lt;p&gt;Customizing the subdomain is a &lt;strong&gt;console workflow&lt;/strong&gt;; there is no documented AWS CLI parameter to set the access portal hostname after the fact. You can still &lt;strong&gt;list&lt;/strong&gt; your instance from the CLI (replace the region with your Identity Center region):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sso-admin list-instances &lt;span class="nt"&gt;--region&lt;/span&gt; ap-southeast-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example sign-in URL pattern after customization (replace &lt;code&gt;your-subdomain&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://your-subdomain.awsapps.com/start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  7. Troubleshooting
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;What to try&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Customize&lt;/strong&gt; does not appear&lt;/td&gt;
&lt;td&gt;The portal URL may already be customized. AWS does not offer a console option to change it again.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Console shows the wrong account or empty Identity Center&lt;/td&gt;
&lt;td&gt;Use the &lt;strong&gt;organization management account&lt;/strong&gt; (or the account where the instance was created) and the correct &lt;strong&gt;region&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Users see errors after the change&lt;/td&gt;
&lt;td&gt;Confirm they use the &lt;strong&gt;new&lt;/strong&gt; &lt;code&gt;https://…awsapps.com/start&lt;/code&gt; URL, clear old bookmarks, and refresh SSO/CLI &lt;code&gt;sso_start_url&lt;/code&gt; values.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  8. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/howtochangeURL.html" rel="noopener noreferrer"&gt;Customizing the AWS access portal URL&lt;/a&gt; (IAM Identity Center User Guide)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/singlesignon/latest/userguide/using-the-portal.html" rel="noopener noreferrer"&gt;Setting up and using the AWS access portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/iam/identity-center/pricing/" rel="noopener noreferrer"&gt;IAM Identity Center pricing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>iam</category>
      <category>identitycenter</category>
      <category>security</category>
    </item>
    <item>
      <title>Configuring AWS Business Support+</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Mon, 13 Apr 2026 23:21:19 +0000</pubDate>
      <link>https://dev.to/jajera/configuring-aws-business-support-3kdb</link>
      <guid>https://dev.to/jajera/configuring-aws-business-support-3kdb</guid>
      <description>&lt;h2&gt;
  
  
  Configuring AWS Business Support+
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/premiumsupport/plans/business-plus/" rel="noopener noreferrer"&gt;AWS Business Support+&lt;/a&gt; is AWS’s paid support tier that combines &lt;strong&gt;24/7 expert access&lt;/strong&gt;, &lt;strong&gt;AI-assisted troubleshooting&lt;/strong&gt;, &lt;strong&gt;Trusted Advisor&lt;/strong&gt; and health-oriented guidance, and &lt;strong&gt;targeted initial response&lt;/strong&gt; commitments for business-critical issues (see &lt;a href="https://aws.amazon.com/premiumsupport/plans/business-plus/" rel="noopener noreferrer"&gt;official plan page&lt;/a&gt; for current feature wording and pricing). This guide focuses on &lt;strong&gt;how to turn it on and who can do it&lt;/strong&gt;: console enrollment, &lt;strong&gt;organization-wide versus single-account&lt;/strong&gt; subscription, &lt;strong&gt;IAM&lt;/strong&gt; for the Support Plans console, and &lt;strong&gt;Organizations SCP&lt;/strong&gt; pitfalls when regions are restricted.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Open the &lt;strong&gt;&lt;a href="https://console.aws.amazon.com/support/plans/home" rel="noopener noreferrer"&gt;AWS Support Plans console&lt;/a&gt;&lt;/strong&gt; and upgrade from &lt;strong&gt;Basic&lt;/strong&gt; to &lt;strong&gt;Business Support+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;organization-wide&lt;/strong&gt; (management account, Organizations all features) or &lt;strong&gt;account-only&lt;/strong&gt; enrollment&lt;/li&gt;
&lt;li&gt;Grant &lt;strong&gt;IAM&lt;/strong&gt; access to change plans (&lt;code&gt;supportplans:*&lt;/code&gt; or AWS managed policies) and fix &lt;strong&gt;SCP&lt;/strong&gt; denies if Support Plans fails in member accounts&lt;/li&gt;
&lt;li&gt;Know how to &lt;strong&gt;downgrade&lt;/strong&gt; (console path) and where to &lt;strong&gt;compare pricing&lt;/strong&gt; before you confirm&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Root user&lt;/strong&gt; or an IAM principal allowed to change support plans (see &lt;a href="https://docs.aws.amazon.com/awssupport/latest/user/security-support-plans.html" rel="noopener noreferrer"&gt;Manage access to AWS Support Plans&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;organization-level&lt;/strong&gt; Business Support+: the account must be the &lt;strong&gt;Organizations management account&lt;/strong&gt;, with &lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_org_support-all-features.html" rel="noopener noreferrer"&gt;all features enabled&lt;/a&gt;&lt;/strong&gt; for the organization&lt;/li&gt;
&lt;li&gt;Billing in good standing; paid support has a &lt;strong&gt;minimum one-month&lt;/strong&gt; commitment (&lt;a href="https://aws.amazon.com/premiumsupport/faqs/" rel="noopener noreferrer"&gt;Support FAQs&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Subscribe from the Support Plans console
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Open the console
&lt;/h4&gt;

&lt;p&gt;Sign in and go to &lt;a href="https://console.aws.amazon.com/support/plans/home" rel="noopener noreferrer"&gt;AWS Support Plans&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Optional but recommended before you subscribe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose &lt;strong&gt;Compare all Support plans and features&lt;/strong&gt; to line up Business Support+ against other tiers&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Pricing calculator&lt;/strong&gt; (in the console) to estimate support charges from expected monthly AWS usage&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Upgrade from Basic to Business Support+
&lt;/h4&gt;

&lt;p&gt;Steps follow &lt;a href="https://docs.aws.amazon.com/awssupport/latest/user/changing-support-plans.html" rel="noopener noreferrer"&gt;Changing AWS Support plans&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the &lt;strong&gt;AWS Business Support+&lt;/strong&gt; section, choose &lt;strong&gt;Get started&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Choose scope:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;My organization&lt;/strong&gt; — only if you use &lt;strong&gt;AWS Organizations&lt;/strong&gt; with &lt;strong&gt;all-feature mode&lt;/strong&gt;; only the &lt;strong&gt;management account&lt;/strong&gt; can enroll the whole org. New accounts created in the org are &lt;strong&gt;automatically&lt;/strong&gt; on Business Support+. In the console, &lt;strong&gt;My organization&lt;/strong&gt; is &lt;strong&gt;disabled&lt;/strong&gt; when it does not apply (for example, you are signed in to a &lt;strong&gt;member account&lt;/strong&gt;, Organizations is not in use, or &lt;strong&gt;all features&lt;/strong&gt; are not enabled for the organization)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My account&lt;/strong&gt; — subscribe &lt;strong&gt;this account&lt;/strong&gt; only&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Review plan details and pricing (&lt;a href="https://aws.amazon.com/premiumsupport/pricing/" rel="noopener noreferrer"&gt;AWS Support pricing&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Accept the subscription terms (checkbox)&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Confirm upgrade&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;After enrollment:&lt;/strong&gt; confirm the tier in &lt;strong&gt;&lt;a href="https://console.aws.amazon.com/support/home" rel="noopener noreferrer"&gt;AWS Support Center&lt;/a&gt;&lt;/strong&gt; (the console shows your current plan) and verify you can open a &lt;strong&gt;support case&lt;/strong&gt; with the severity options you expect.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. IAM: who may view or change the plan
&lt;/h3&gt;

&lt;p&gt;Users need &lt;code&gt;supportplans&lt;/code&gt; API permissions. Common actions (&lt;a href="https://docs.aws.amazon.com/awssupport/latest/user/security-support-plans.html" rel="noopener noreferrer"&gt;docs&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;supportplans:GetSupportPlan&lt;/code&gt; — view current plan&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;supportplans:StartSupportPlanUpdate&lt;/code&gt; — start an upgrade or downgrade request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;supportplans:GetSupportPlanUpdateStatus&lt;/code&gt; — poll update status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS managed policies (names may evolve; confirm in IAM):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AWSSupportPlansFullAccess&lt;/code&gt;&lt;/strong&gt; — change plans&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AWSSupportPlansReadOnlyAccess&lt;/code&gt;&lt;/strong&gt; — view only&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example &lt;strong&gt;read-only&lt;/strong&gt; custom policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"supportplans:Get*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"supportplans:List*"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example &lt;strong&gt;full&lt;/strong&gt; access (delegate carefully):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"supportplans:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Organizations: SCPs and global services
&lt;/h3&gt;

&lt;p&gt;If member accounts see errors &lt;strong&gt;even with correct IAM&lt;/strong&gt;, an &lt;strong&gt;SCP&lt;/strong&gt; that denies by &lt;strong&gt;Region&lt;/strong&gt; can block &lt;strong&gt;Support Plans&lt;/strong&gt; because it is a &lt;strong&gt;global&lt;/strong&gt; console/API.&lt;/p&gt;

&lt;p&gt;Per &lt;a href="https://docs.aws.amazon.com/awssupport/latest/user/security-support-plans.html" rel="noopener noreferrer"&gt;Manage access to AWS Support Plans&lt;/a&gt;, add &lt;strong&gt;&lt;code&gt;supportplans:*&lt;/code&gt;&lt;/strong&gt; to the &lt;strong&gt;&lt;code&gt;NotAction&lt;/code&gt;&lt;/strong&gt; list on any broad Region-deny SCP (exact structure depends on your org’s policy layout). If you use &lt;strong&gt;AWS Control Tower&lt;/strong&gt; Region deny controls, read AWS guidance on &lt;strong&gt;drift&lt;/strong&gt; before hand-editing SCPs, because repairs can revert custom exceptions.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Downgrades and higher tiers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Downgrade from Business Support+:&lt;/strong&gt; on &lt;a href="https://console.aws.amazon.com/support/plans/home" rel="noopener noreferrer"&gt;Manage Support Plans&lt;/a&gt;, use &lt;strong&gt;Review downgrade&lt;/strong&gt; in the &lt;strong&gt;Basic Support&lt;/strong&gt; section (&lt;a href="https://docs.aws.amazon.com/awssupport/latest/user/changing-support-plans.html" rel="noopener noreferrer"&gt;Changing AWS Support plans&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise Support or Unified Operations:&lt;/strong&gt; use &lt;strong&gt;Contact sales&lt;/strong&gt; in the console; Enterprise downgrades go through your &lt;strong&gt;TAM&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account leaves the org&lt;/strong&gt; after org-level Business Support+: AWS documents that the account &lt;strong&gt;drops to Basic&lt;/strong&gt; support&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  7. Optional follow-on configuration
&lt;/h3&gt;

&lt;p&gt;These are not strictly “subscription” steps but are how teams &lt;strong&gt;use&lt;/strong&gt; paid support day to day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/awssupport/latest/user/aws-support-app-for-slack.html" rel="noopener noreferrer"&gt;AWS Support App in Slack&lt;/a&gt;&lt;/strong&gt; — create and update cases from Slack&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/awssupport/latest/user/about-support-api.html" rel="noopener noreferrer"&gt;AWS Support API&lt;/a&gt;&lt;/strong&gt; — automate case operations (separate IAM/service permissions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trusted Advisor&lt;/strong&gt; and &lt;strong&gt;AWS Health&lt;/strong&gt; — visible in console once the plan includes them; use &lt;a href="https://aws.amazon.com/premiumsupport/technology/trusted-advisor/" rel="noopener noreferrer"&gt;Trusted Advisor&lt;/a&gt; and &lt;a href="https://aws.amazon.com/premiumsupport/technology/aws-health/" rel="noopener noreferrer"&gt;AWS Health&lt;/a&gt; docs for check types and alerts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Promotional &lt;strong&gt;trial&lt;/strong&gt; or &lt;strong&gt;pricing&lt;/strong&gt; offers change over time; use the &lt;a href="https://aws.amazon.com/premiumsupport/plans/business-plus/" rel="noopener noreferrer"&gt;Business Support+ plan page&lt;/a&gt; and in-console &lt;strong&gt;offer&lt;/strong&gt; links for current eligibility and refund rules.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. Summary: Copy-Paste
&lt;/h3&gt;

&lt;p&gt;Console entry points (sign in first):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://console.aws.amazon.com/support/plans/home
https://console.aws.amazon.com/support/home
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IAM: attach &lt;strong&gt;&lt;code&gt;AWSSupportPlansFullAccess&lt;/code&gt;&lt;/strong&gt; to a named role used only for billing/support changes, or merge the JSON examples in section 4 into your least-privilege model.&lt;/p&gt;

&lt;p&gt;Organizations: if Region-deny SCPs exist, ensure &lt;strong&gt;&lt;code&gt;supportplans:*&lt;/code&gt;&lt;/strong&gt; is excluded from the deny per AWS documentation.&lt;/p&gt;




&lt;h3&gt;
  
  
  9. Troubleshooting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Access Denied on Support Plans:&lt;/strong&gt; Attach &lt;strong&gt;&lt;code&gt;AWSSupportPlansFullAccess&lt;/code&gt;&lt;/strong&gt; (or equivalent &lt;code&gt;supportplans:*&lt;/code&gt;) to the role or user; confirm you are not using a &lt;strong&gt;member account&lt;/strong&gt; when only the &lt;strong&gt;management account&lt;/strong&gt; may enroll the org.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Member account cannot open the page:&lt;/strong&gt; Check &lt;strong&gt;SCP&lt;/strong&gt; Region denies and add &lt;strong&gt;&lt;code&gt;supportplans:*&lt;/code&gt;&lt;/strong&gt; to &lt;strong&gt;&lt;code&gt;NotAction&lt;/code&gt;&lt;/strong&gt; as documented; verify &lt;strong&gt;Control Tower&lt;/strong&gt; drift if that platform manages SCPs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wrong scope after the fact:&lt;/strong&gt; Org-wide subscription is a &lt;strong&gt;management-account&lt;/strong&gt; decision; single-account subscription does not extend to siblings. Adjust scope only by following AWS &lt;strong&gt;change plan&lt;/strong&gt; / &lt;strong&gt;downgrade&lt;/strong&gt; flows and org enrollment rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Charges surprise you:&lt;/strong&gt; Revisit the console &lt;strong&gt;Pricing calculator&lt;/strong&gt; and &lt;a href="https://aws.amazon.com/premiumsupport/pricing/" rel="noopener noreferrer"&gt;Support pricing&lt;/a&gt;; support fees are &lt;strong&gt;separate&lt;/strong&gt; from service usage on the bill.&lt;/p&gt;




&lt;h3&gt;
  
  
  10. References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/awssupport/latest/user/changing-support-plans.html" rel="noopener noreferrer"&gt;Change AWS Support plans&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/awssupport/latest/user/aws-support-plans.html" rel="noopener noreferrer"&gt;AWS Support plans (User Guide)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/awssupport/latest/user/security-support-plans.html" rel="noopener noreferrer"&gt;Manage access to AWS Support Plans&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/awssupport/latest/user/managed-policies-aws-support-plans.html" rel="noopener noreferrer"&gt;AWS managed policies for AWS Support Plans&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_org_support-all-features.html" rel="noopener noreferrer"&gt;Enabling all features for an organization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/premiumsupport/plans/business-plus/" rel="noopener noreferrer"&gt;AWS Business Support+&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/premiumsupport/faqs/" rel="noopener noreferrer"&gt;AWS Support FAQs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>support</category>
      <category>iam</category>
      <category>organizations</category>
    </item>
  </channel>
</rss>
