<?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>Forem: John  Ajera</title>
    <description>The latest articles on Forem by John  Ajera (@jajera).</description>
    <link>https://forem.com/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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2461160%2Fefda9ea8-49f8-4d07-b358-445b8e7d5a20.png</url>
      <title>Forem: John  Ajera</title>
      <link>https://forem.com/jajera</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jajera"/>
    <language>en</language>
    <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://forem.com/jajera/iam-identity-center-account-assignments-for-terraform-member-accounts-4p30</link>
      <guid>https://forem.com/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://forem.com/jajera/terraform-member-accounts-for-a-control-tower-sandbox-ou-and-a-custom-dev-ou-3ff2</link>
      <guid>https://forem.com/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://forem.com/jajera/github-webhook-secrets-and-the-terraform-public-registry-5ef1</link>
      <guid>https://forem.com/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://forem.com/jajera/aws-iam-identity-center-custom-access-portal-url-963</link>
      <guid>https://forem.com/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://forem.com/jajera/configuring-aws-business-support-3kdb</link>
      <guid>https://forem.com/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>
    <item>
      <title>Greenfield EKS: Choosing Standard EKS vs EKS Auto Mode Without Legacy Baggage</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Tue, 07 Apr 2026 23:35:59 +0000</pubDate>
      <link>https://forem.com/jajera/greenfield-eks-choosing-standard-eks-vs-eks-auto-mode-without-legacy-baggage-39f3</link>
      <guid>https://forem.com/jajera/greenfield-eks-choosing-standard-eks-vs-eks-auto-mode-without-legacy-baggage-39f3</guid>
      <description>&lt;h2&gt;
  
  
  Greenfield EKS: Choosing Standard EKS vs EKS Auto Mode Without Legacy Baggage
&lt;/h2&gt;

&lt;p&gt;If you are standing up &lt;strong&gt;your first&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html" rel="noopener noreferrer"&gt;Amazon EKS&lt;/a&gt; cluster and you are &lt;strong&gt;not&lt;/strong&gt; dragging along years of Terraform modules, custom AMIs, or a mandated node strategy from another team, the choice between &lt;strong&gt;Standard EKS&lt;/strong&gt; (you own how nodes are provisioned and wired) and &lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/automode.html" rel="noopener noreferrer"&gt;EKS Auto Mode&lt;/a&gt;&lt;/strong&gt; (AWS runs more of that for you) is mostly about &lt;strong&gt;defaults&lt;/strong&gt;: speed and delegated operations versus transparency and fine-grained control. This article is a practical decision guide for that &lt;strong&gt;greenfield&lt;/strong&gt; scenario—what differs, how to think about &lt;strong&gt;cost&lt;/strong&gt; and &lt;strong&gt;maintenance&lt;/strong&gt;, and how to separate &lt;strong&gt;the EKS operating model&lt;/strong&gt; from &lt;strong&gt;optional add-ons&lt;/strong&gt; you install on top.&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.amazonaws.com%2Fuploads%2Farticles%2F5k4027o7x1ulwq0tee4s.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.amazonaws.com%2Fuploads%2Farticles%2F5k4027o7x1ulwq0tee4s.png" alt="Diagram of Amazon EKS: Kubernetes control plane managed by AWS and worker nodes running in your VPC" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source: &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html" rel="noopener noreferrer"&gt;What is Amazon EKS?&lt;/a&gt; (AWS Documentation).&lt;/em&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; cluster with &lt;strong&gt;no legacy constraints&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What &lt;strong&gt;Standard EKS&lt;/strong&gt; and &lt;strong&gt;EKS Auto Mode&lt;/strong&gt; each optimize for in the first weeks and months&lt;/li&gt;
&lt;li&gt;How to compare &lt;strong&gt;cost&lt;/strong&gt; at a high level (control plane, compute, Auto Mode management fees, and people time)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who runs what&lt;/strong&gt; (nodes, scaling, ingress/LB), including an &lt;strong&gt;AWS-documented checklist&lt;/strong&gt; of what Auto Mode manages as built-in infrastructure&lt;/li&gt;
&lt;li&gt;A short &lt;strong&gt;rubric&lt;/strong&gt; for “default to Auto Mode” versus “start explicit with managed node groups”&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;containers&lt;/strong&gt; and the idea of a &lt;strong&gt;Kubernetes control plane&lt;/strong&gt; versus &lt;strong&gt;worker nodes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Access to current &lt;strong&gt;&lt;a href="https://aws.amazon.com/eks/pricing" rel="noopener noreferrer"&gt;Amazon EKS pricing&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/automode.html" rel="noopener noreferrer"&gt;EKS Auto Mode documentation&lt;/a&gt;&lt;/strong&gt; for numbers and feature details that change over time&lt;/li&gt;
&lt;li&gt;No assumption that you already run &lt;strong&gt;Karpenter&lt;/strong&gt;, &lt;strong&gt;managed node groups&lt;/strong&gt;, or a corporate standard—this article assumes &lt;strong&gt;greenfield&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Same managed control plane; &lt;strong&gt;who owns the node story&lt;/strong&gt; is what splits the paths.&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;Standard EKS&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;EKS Auto Mode&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;You choose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How nodes are created and scaled: &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html" rel="noopener noreferrer"&gt;managed node groups&lt;/a&gt;, self-managed nodes, or later &lt;a href="https://karpenter.sh/" rel="noopener noreferrer"&gt;Karpenter&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Less DIY wiring; AWS runs more of &lt;strong&gt;lifecycle + integration&lt;/strong&gt; (&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/automode.html" rel="noopener noreferrer"&gt;docs&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mental model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;“These are &lt;strong&gt;my&lt;/strong&gt; instances / ASGs, &lt;strong&gt;my&lt;/strong&gt; scaling and upgrade path”&lt;/td&gt;
&lt;td&gt;“&lt;strong&gt;AWS-shaped&lt;/strong&gt; automation; fewer knobs, less assembly”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;APIs you will see&lt;/strong&gt; (examples)&lt;/td&gt;
&lt;td&gt;EC2, MNG, launch templates, plus whatever provisioner you pick&lt;/td&gt;
&lt;td&gt;Platform-oriented CRDs such as &lt;code&gt;NodePool&lt;/code&gt; / &lt;code&gt;NodeClaim&lt;/code&gt;, &lt;code&gt;NodeClass&lt;/code&gt;, &lt;code&gt;CNINode&lt;/code&gt;, LB-related types—&lt;strong&gt;exact set&lt;/strong&gt; varies by version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Standard EKS — responsibility flow
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------------------------+
| AWS: Kubernetes API           |  (managed control plane)
+-------------------------------+
              |
              v
+-------------------------------+
| You: pick node strategy       |
| MNG / self-managed / Karpenter|
+-------------------------------+
              |
              v
+-------------------------------+
| Workloads / Pods              |
+-------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  EKS Auto Mode — responsibility flow
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------------------------+
| AWS: Kubernetes API           |  (managed control plane)
+-------------------------------+
              |
              v
+-------------------------------+
| AWS: node lifecycle +         |
| integrations (opinionated)    |
+-------------------------------+
              |
              v
+-------------------------------+
| Workloads / Pods              |
+-------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Same for both:&lt;/strong&gt; observability, backups, secrets, RBAC, network policy, ingress, and mesh are &lt;strong&gt;still yours&lt;/strong&gt;. Picking a mode ≠ production-ready.&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;Pricing moves; always verify against the &lt;a href="https://aws.amazon.com/eks/pricing/" rel="noopener noreferrer"&gt;Amazon EKS pricing page&lt;/a&gt;. The figures below use &lt;strong&gt;US West (Oregon)&lt;/strong&gt; On-Demand rates at the time of writing and a &lt;strong&gt;small greenfield&lt;/strong&gt; scenario: &lt;strong&gt;one cluster, three worker nodes&lt;/strong&gt; (&lt;code&gt;m5a.xlarge&lt;/code&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  Estimated monthly AWS bill
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line item&lt;/th&gt;
&lt;th&gt;Rate&lt;/th&gt;
&lt;th&gt;Standard EKS&lt;/th&gt;
&lt;th&gt;Auto Mode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Control plane&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.10 / cluster / hr&lt;/td&gt;
&lt;td&gt;~$73&lt;/td&gt;
&lt;td&gt;~$73&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;EC2 instances&lt;/strong&gt; (3 x &lt;code&gt;m5a.xlarge&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;$0.172 / instance / hr&lt;/td&gt;
&lt;td&gt;~$377&lt;/td&gt;
&lt;td&gt;~$377&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auto Mode management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.02064 / instance / hr&lt;/td&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;td&gt;~$45&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS invoice total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$450&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$495&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Source for *&lt;/em&gt;$0.02064 / hr** (Auto Mode) and &lt;strong&gt;$0.172 / hr&lt;/strong&gt; (EC2) for &lt;code&gt;m5a.xlarge&lt;/code&gt;: AWS &lt;strong&gt;Example 4: EKS Auto Mode&lt;/strong&gt; on &lt;a href="https://aws.amazon.com/eks/pricing/" rel="noopener noreferrer"&gt;Amazon EKS pricing&lt;/a&gt; (US West Oregon, On-Demand). Auto Mode fees are &lt;strong&gt;per instance type&lt;/strong&gt;; use that page or the &lt;a href="https://calculator.aws/#/createCalculator/EKS" rel="noopener noreferrer"&gt;AWS Pricing Calculator for EKS&lt;/a&gt; for other shapes.*&lt;/p&gt;

&lt;p&gt;Auto Mode adds roughly &lt;strong&gt;10-12%&lt;/strong&gt; to the AWS bill in this scenario. The management fee is billed per-second (one-minute minimum) and applies regardless of EC2 purchase option (On-Demand, Reserved, Savings Plan, or Spot).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Watch out for extended support.&lt;/strong&gt; If you let a Kubernetes version drift past &lt;strong&gt;standard support&lt;/strong&gt; (14 months), the cluster fee jumps to &lt;strong&gt;$0.60 / hr (~$438 / month)&lt;/strong&gt;. Plan upgrades regardless of mode.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  What the invoice does not show
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hidden cost&lt;/th&gt;
&lt;th&gt;Standard EKS&lt;/th&gt;
&lt;th&gt;Auto Mode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Engineer time&lt;/strong&gt; building node automation, LB controller Helm charts, upgrade runbooks&lt;/td&gt;
&lt;td&gt;Higher -- you build and maintain the glue&lt;/td&gt;
&lt;td&gt;Lower -- AWS ships more of that glue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Incident cost&lt;/strong&gt; when nodes misbehave, AMIs drift, or scaling stalls&lt;/td&gt;
&lt;td&gt;Yours to debug end-to-end&lt;/td&gt;
&lt;td&gt;Shared with AWS; fewer levers but also fewer moving parts you wrote&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Idle capacity&lt;/strong&gt; from over-provisioned groups or generous defaults&lt;/td&gt;
&lt;td&gt;Risk if you set scaling bounds loosely&lt;/td&gt;
&lt;td&gt;Risk if Auto Mode defaults are generous for your workload&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; the ~$45 / month management fee in this example is roughly &lt;strong&gt;one hour of a platform engineer's loaded cost&lt;/strong&gt;. If Auto Mode saves more than that in reduced toil per month, it pays for itself. If your team already has solid automation and rarely touches nodes, Standard keeps the invoice leaner.&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;The choice shows up in &lt;strong&gt;division of labor&lt;/strong&gt;: who keeps &lt;strong&gt;nodes&lt;/strong&gt;, &lt;strong&gt;scaling&lt;/strong&gt;, and &lt;strong&gt;traffic into the cluster&lt;/strong&gt; healthy. That is what fills your calendar in months &lt;strong&gt;six through twelve&lt;/strong&gt;—upgrade planning and Helm charts on one side, AWS platform changes and support cases on the other—not an abstract “mode” badge.&lt;/p&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;Standard EKS (typical greenfield)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;EKS Auto Mode&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nodes and scaling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You pick the mechanism—&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html" rel="noopener noreferrer"&gt;managed node groups&lt;/a&gt;, self-managed nodes, or &lt;a href="https://karpenter.sh/" rel="noopener noreferrer"&gt;Karpenter&lt;/a&gt;—and you own &lt;strong&gt;upgrades&lt;/strong&gt;, &lt;strong&gt;behavior&lt;/strong&gt;, and &lt;strong&gt;capacity tuning&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;AWS delivers a more &lt;strong&gt;integrated&lt;/strong&gt; node and scaling experience; you align with &lt;strong&gt;Kubernetes objects and practices AWS documents for Auto Mode&lt;/strong&gt; instead of assembling the same stack yourself (&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/automode.html" rel="noopener noreferrer"&gt;Auto Mode&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ingress and load balancers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams usually &lt;strong&gt;install and operate&lt;/strong&gt; something like &lt;strong&gt;AWS Load Balancer Controller&lt;/strong&gt;—chart upgrades, compatibility with new cluster versions, incidents when labels or annotations drift&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;More of that integration is AWS-operated&lt;/strong&gt; for Auto Mode, so you typically spend &lt;strong&gt;less&lt;/strong&gt; time babysitting that slice—still read &lt;strong&gt;AWS release notes&lt;/strong&gt; when the platform changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Standard EKS in practice
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clarity and skill-building:&lt;/strong&gt; Obvious ownership (“we chose MNG / Karpenter / …”), strong &lt;strong&gt;learning&lt;/strong&gt; for engineers who will live in AWS and Kubernetes, and a concrete story when &lt;strong&gt;auditors&lt;/strong&gt; ask what creates instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recurring toil:&lt;/strong&gt; &lt;strong&gt;Upgrade choreography&lt;/strong&gt; across the control plane version, &lt;strong&gt;node AMIs&lt;/strong&gt;, and &lt;strong&gt;add-on compatibility&lt;/strong&gt;; deliberate choices for &lt;strong&gt;scaling bounds&lt;/strong&gt; and instance families; ongoing ownership of &lt;strong&gt;ingress/LB&lt;/strong&gt; tooling unless you outsource it elsewhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  EKS Auto Mode in practice
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Less assembly, faster baseline:&lt;/strong&gt; Shorter path to a &lt;strong&gt;working data plane&lt;/strong&gt;, fewer Terraform or CloudFormation resources tied to &lt;strong&gt;node plumbing&lt;/strong&gt;, and less day-to-day ownership of the &lt;strong&gt;LB integration&lt;/strong&gt; layer described above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tradeoff:&lt;/strong&gt; &lt;strong&gt;Fewer levers&lt;/strong&gt; when behavior surprises you; success depends on &lt;strong&gt;solid observability&lt;/strong&gt; (logs, metrics, tracing) and comfort escalating or adapting when &lt;strong&gt;AWS-owned&lt;/strong&gt; behavior changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  What Auto Mode treats as managed infrastructure (AWS checklist)
&lt;/h4&gt;

&lt;p&gt;AWS describes these as &lt;strong&gt;built-in cluster capabilities&lt;/strong&gt;, not as separate EKS console add-ons you install and version yourself. This is the high-level list from &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/automode.html" rel="noopener noreferrer"&gt;Automate cluster infrastructure with EKS Auto Mode&lt;/a&gt; and the &lt;strong&gt;Automated components&lt;/strong&gt; section there—use the docs for the latest detail and limits.&lt;/p&gt;

&lt;h5&gt;
  
  
  Compute and nodes
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Data-plane &lt;strong&gt;EC2 instances&lt;/strong&gt; (Bottlerocket-based variants): AMI selection, locked-down OS (SELinux enforcing, read-only root), &lt;strong&gt;no direct SSH or SSM&lt;/strong&gt; to nodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security and lifecycle:&lt;/strong&gt; patching and upgrades with minimal disruption; &lt;strong&gt;maximum node lifetime&lt;/strong&gt; (default up to &lt;strong&gt;21 days&lt;/strong&gt;) so nodes are replaced regularly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accelerated workloads:&lt;/strong&gt; GPU-related kernel drivers and plugins (for example &lt;strong&gt;NVIDIA&lt;/strong&gt; and &lt;strong&gt;AWS Neuron&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spot:&lt;/strong&gt; handling of &lt;strong&gt;Spot interruption notices&lt;/strong&gt; and EC2 instance health signals&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Scaling
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Karpenter&lt;/strong&gt;-style autoscaling: reacts to unschedulable Pods, adds capacity, and removes or consolidates nodes when demand drops&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Load balancing
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Elastic Load Balancing&lt;/strong&gt; tied to Kubernetes &lt;strong&gt;Service&lt;/strong&gt; and &lt;strong&gt;Ingress&lt;/strong&gt; objects: provisions and manages &lt;strong&gt;ALB&lt;/strong&gt; and &lt;strong&gt;NLB&lt;/strong&gt; resources and scales them with cluster demand&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Networking
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pod and Service networking&lt;/strong&gt;, including &lt;strong&gt;IPv4/IPv6&lt;/strong&gt; and use of &lt;strong&gt;secondary CIDRs&lt;/strong&gt; where needed for Pod IP space&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network policy&lt;/strong&gt; enforcement for Pods&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster DNS&lt;/strong&gt; (local DNS for the cluster)&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Storage
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EBS CSI&lt;/strong&gt;-class block storage as a managed capability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ephemeral storage&lt;/strong&gt; defaults on nodes (volume type, size, encryption, and cleanup behavior on termination—per AWS documentation)&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Identity
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EKS Pod Identity:&lt;/strong&gt; you &lt;strong&gt;do not&lt;/strong&gt; install the &lt;strong&gt;EKS Pod Identity Agent&lt;/strong&gt; yourself on Auto Mode clusters (AWS documents that it is not required in this model)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Neither mode delivers “production in a box.”&lt;/strong&gt; Metrics, secret rotation, mesh, policy engines, backups, and business-specific operators remain &lt;strong&gt;your&lt;/strong&gt; software lifecycle unless you adopt separate managed services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoid the “busy cluster” trap.&lt;/strong&gt; A Standard environment can &lt;em&gt;look&lt;/em&gt; heavier because it has &lt;strong&gt;more add-ons&lt;/strong&gt; (monitoring, GitOps, security tools). That says what &lt;strong&gt;you installed&lt;/strong&gt;, not that &lt;strong&gt;Standard&lt;/strong&gt; is inherently more capable than &lt;strong&gt;Auto Mode&lt;/strong&gt;. Judge the choice on &lt;strong&gt;who operates nodes and built-in integrations&lt;/strong&gt;, not on how crowded the UI feels.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Decision rubric (greenfield, no baggage)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Lean toward EKS Auto Mode&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The team is &lt;strong&gt;small&lt;/strong&gt; and the priority is &lt;strong&gt;shipping&lt;/strong&gt; a standard container platform quickly&lt;/li&gt;
&lt;li&gt;Workloads are &lt;strong&gt;typical&lt;/strong&gt; Linux containers without exotic kernel, sysctl, or custom AMI requirements &lt;strong&gt;today&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You are comfortable adopting &lt;strong&gt;AWS-shaped&lt;/strong&gt; APIs for nodes and integrations for the next phase of growth&lt;/li&gt;
&lt;li&gt;You prefer &lt;strong&gt;fewer&lt;/strong&gt; moving parts &lt;strong&gt;you&lt;/strong&gt; must patch and tune in the first year&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lean toward Standard EKS (often with managed node groups first)&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want &lt;strong&gt;maximum transparency&lt;/strong&gt; into every layer for &lt;strong&gt;training&lt;/strong&gt;, &lt;strong&gt;compliance groundwork&lt;/strong&gt;, or &lt;strong&gt;multi-cloud&lt;/strong&gt; discipline&lt;/li&gt;
&lt;li&gt;You already know you need &lt;strong&gt;specific&lt;/strong&gt; instance families, purchase options, or &lt;strong&gt;strict&lt;/strong&gt; cost caps encoded &lt;strong&gt;explicitly&lt;/strong&gt; from day one&lt;/li&gt;
&lt;li&gt;You expect to &lt;strong&gt;standardize&lt;/strong&gt; on an in-house or community &lt;strong&gt;node provisioning&lt;/strong&gt; story (for example Karpenter you fully control) and want to &lt;strong&gt;learn&lt;/strong&gt; that model without an additional management layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Default suggestion for a true greenfield product team:&lt;/strong&gt; if your main risk is &lt;strong&gt;slow delivery&lt;/strong&gt; and &lt;strong&gt;operational overload&lt;/strong&gt;, &lt;strong&gt;EKS Auto Mode&lt;/strong&gt; is often the better &lt;strong&gt;starting&lt;/strong&gt; default; if your main risk is &lt;strong&gt;understanding and owning&lt;/strong&gt; every dependency for regulatory or career reasons, &lt;strong&gt;Standard EKS&lt;/strong&gt; with &lt;strong&gt;managed node groups&lt;/strong&gt; is a clean teaching path. You can revisit the choice after the team has real production traffic and metrics.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;“Auto Mode is more Kubernetes.”&lt;/strong&gt; It is &lt;strong&gt;more AWS-managed automation&lt;/strong&gt; exposed through Kubernetes APIs, not a superset of every optional addon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“Standard is cheaper.”&lt;/strong&gt; &lt;strong&gt;Invoice lines&lt;/strong&gt; can be lower; &lt;strong&gt;total cost&lt;/strong&gt; may not be once you count &lt;strong&gt;engineering time&lt;/strong&gt; and &lt;strong&gt;incidents&lt;/strong&gt; for a small team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“We picked a mode, so we are secure and observable.”&lt;/strong&gt; &lt;strong&gt;RBAC&lt;/strong&gt;, &lt;strong&gt;network policy&lt;/strong&gt;, &lt;strong&gt;secrets&lt;/strong&gt;, &lt;strong&gt;auditing&lt;/strong&gt;, and &lt;strong&gt;monitoring&lt;/strong&gt; are still &lt;strong&gt;your&lt;/strong&gt; design choices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“Our Standard cluster has more moving parts, so it must be better.”&lt;/strong&gt; Often that is &lt;strong&gt;more optional software you added&lt;/strong&gt;—not proof that Standard beats Auto Mode; see &lt;strong&gt;section 5&lt;/strong&gt;.&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/eks/latest/userguide/what-is-eks.html" rel="noopener noreferrer"&gt;What is Amazon EKS?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/eks/pricing/" rel="noopener noreferrer"&gt;Amazon EKS pricing&lt;/a&gt; (see &lt;strong&gt;Example 4: EKS Auto Mode&lt;/strong&gt; for per-instance-type Auto Mode fees used in section 4)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/automode.html" rel="noopener noreferrer"&gt;EKS Auto Mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/auto-reference.html" rel="noopener noreferrer"&gt;Learn how EKS Auto Mode works&lt;/a&gt; (deeper component topics)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/eks/resources/best-practices/" rel="noopener noreferrer"&gt;Amazon EKS best practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html" rel="noopener noreferrer"&gt;Managed node groups&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://karpenter.sh/" rel="noopener noreferrer"&gt;Karpenter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>eks</category>
      <category>aws</category>
      <category>kubernetes</category>
      <category>automode</category>
    </item>
    <item>
      <title>Argo CD and AWS CodeConnections: The Upside, the Redeploy Pain, and How I Fixed It</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sat, 28 Mar 2026 10:46:16 +0000</pubDate>
      <link>https://forem.com/jajera/argo-cd-and-aws-codeconnections-the-upside-the-redeploy-pain-and-how-i-fixed-it-2k1m</link>
      <guid>https://forem.com/jajera/argo-cd-and-aws-codeconnections-the-upside-the-redeploy-pain-and-how-i-fixed-it-2k1m</guid>
      <description>&lt;h2&gt;
  
  
  Argo CD and AWS CodeConnections: The Upside, the Redeploy Pain, and How I Fixed It
&lt;/h2&gt;

&lt;p&gt;I run &lt;a href="https://argo-cd.readthedocs.io/" rel="noopener noreferrer"&gt;Argo CD&lt;/a&gt; on &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html" rel="noopener noreferrer"&gt;Amazon EKS&lt;/a&gt; using the managed &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/argo-cd.html" rel="noopener noreferrer"&gt;Argo CD capability&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/codeconnections/latest/userguide/welcome.html" rel="noopener noreferrer"&gt;AWS CodeConnections&lt;/a&gt; for Git. CodeConnections has been a clear win for day-to-day operations. Then I had to &lt;strong&gt;recreate&lt;/strong&gt; the connection (new resource, new identity in the URL). Every Application went to &lt;strong&gt;Sync: Unknown&lt;/strong&gt; until I updated URLs in &lt;strong&gt;two&lt;/strong&gt; places—Git &lt;strong&gt;and&lt;/strong&gt; the live cluster—and fixed &lt;strong&gt;ApplicationSets&lt;/strong&gt; so they stopped writing the old URL back. This article leads with &lt;strong&gt;why I still choose CodeConnections&lt;/strong&gt;, then &lt;strong&gt;what breaks on redeploy&lt;/strong&gt;, then &lt;strong&gt;what I did&lt;/strong&gt; when it inevitably happened, in that order.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Why CodeConnections is worth it for Argo CD on EKS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;No SSH keys or personal tokens in the cluster.&lt;/strong&gt; Argo pulls Git using IAM: the capability role is allowed to use the connection (&lt;code&gt;UseConnection&lt;/code&gt;, &lt;code&gt;GetConnection&lt;/code&gt;). You are not copying PATs into Secrets or rotating leaked keys because someone printed &lt;code&gt;kubectl get secret&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One connection, many repos.&lt;/strong&gt; The HTTPS URL includes your account, region, a &lt;strong&gt;connection UUID&lt;/strong&gt;, and then &lt;code&gt;owner/repo&lt;/code&gt;. Same connection, different path segment for each repository. Setup details and Terraform patterns are in &lt;a href="https://dev.to/jajera/argo-cd-on-eks-git-repo-access-with-aws-codeconnections-and-terraform-3ejl"&gt;Argo CD on EKS: Git repo access with AWS CodeConnections and Terraform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fits how enterprises already govern access.&lt;/strong&gt; Connections are AWS resources; approval and auditing live next to the rest of your cloud controls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That does not mean zero tradeoffs.&lt;/strong&gt; Managed Argo CD often applies &lt;strong&gt;one&lt;/strong&gt; Git credential broadly (for example every &lt;code&gt;github.com&lt;/code&gt; fetch through Kustomize can inherit it). If that bites you, vendoring or URL strategy fixes it—see &lt;a href="https://dev.to/jajera/why-your-kustomize-remote-bases-break-on-managed-argo-cd-and-how-to-fix-it-26i6"&gt;Why Your Kustomize Remote Bases Break on Managed Argo CD (and How to Fix It)&lt;/a&gt;. The tradeoff this article focuses on is different: &lt;strong&gt;replacing&lt;/strong&gt; the connection.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. What actually hurts when you update or redeploy the connection
&lt;/h3&gt;

&lt;p&gt;The Git URL Argo uses is not abstract. It embeds a &lt;strong&gt;connection UUID&lt;/strong&gt; in the path. &lt;strong&gt;A new connection is a new UUID.&lt;/strong&gt; Anything that still points at the old path keeps asking AWS to authorize the wrong resource, so repo fetch fails and sync never reconciles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pain point one — Git only is not enough.&lt;/strong&gt; Your GitOps repo is the source of truth, but Kubernetes already has &lt;code&gt;Application&lt;/code&gt; and &lt;code&gt;ApplicationSet&lt;/code&gt; objects applied &lt;strong&gt;yesterday&lt;/strong&gt;. Their &lt;code&gt;spec.source.repoURL&lt;/code&gt; (and generator URLs on sets) stay on the old string until something updates them. Pushing Git does not retroactively patch those CRs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pain point two — ApplicationSets fight you.&lt;/strong&gt; Many sets declare &lt;code&gt;repoURL&lt;/code&gt; twice: on the &lt;strong&gt;git&lt;/strong&gt; generator and again on the &lt;strong&gt;template&lt;/strong&gt;. If you patch child Applications but leave the set on the old URL, the controller reconciles and &lt;strong&gt;puts the old URL back&lt;/strong&gt; on the apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pain point three — Terraform layout.&lt;/strong&gt; If CodeConnections and the EKS cluster share one tangled module and state, &lt;strong&gt;recreating&lt;/strong&gt; the connection can feel like you are planning half the platform when you only wanted a new Git pipe. I now prefer the connection (and its IAM attachment) in something &lt;strong&gt;standalone&lt;/strong&gt;, with outputs the cluster stack consumes—so the next rotation is a smaller blast radius.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this is not.&lt;/strong&gt; If Argo shows &lt;strong&gt;forbidden&lt;/strong&gt; listing some API group (for example heavy CRD surfaces from controllers like ACK), that is usually &lt;strong&gt;cluster RBAC / EKS access policies&lt;/strong&gt; for the Argo identity, not the CodeConnections URL. Fix that on its own; do not confuse it with a UUID swap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not mass-delete Applications&lt;/strong&gt; to “fix” a bad URL. Workloads may still be fine; you risk prune tearing down real resources. Fix the URLs.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kubectl&lt;/code&gt; against the cluster, with permission to read and patch &lt;code&gt;applications&lt;/code&gt; and &lt;code&gt;applicationsets&lt;/code&gt; in the Argo CD namespace (below I use &lt;code&gt;argocd&lt;/code&gt;, the usual default)&lt;/li&gt;
&lt;li&gt;Rights to &lt;strong&gt;commit and push&lt;/strong&gt; every Git repo that hardcodes the CodeConnections URL&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;new&lt;/strong&gt; clone URL or at least the new UUID from the AWS Console or Terraform output&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. If it happens to you: what worked for me
&lt;/h3&gt;

&lt;p&gt;These steps assume you already know the &lt;strong&gt;old&lt;/strong&gt; and &lt;strong&gt;new&lt;/strong&gt; connection UUID (search your shell history, Terraform state, or an old Application YAML). The host pattern is documented in the CodeConnections and EKS guides linked at the end.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step A — Fix Git first, everywhere
&lt;/h4&gt;

&lt;p&gt;Search across &lt;strong&gt;all&lt;/strong&gt; repos that participate in GitOps—not only the “main” infra repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rg &lt;span class="s1"&gt;'codeconnections\.'&lt;/span&gt; &lt;span class="nt"&gt;--glob&lt;/span&gt; &lt;span class="s1"&gt;'*.yaml'&lt;/span&gt; &lt;span class="nt"&gt;--glob&lt;/span&gt; &lt;span class="s1"&gt;'*.yml'&lt;/span&gt; &lt;span class="nt"&gt;--glob&lt;/span&gt; &lt;span class="s1"&gt;'*.tf'&lt;/span&gt; &lt;span class="nt"&gt;--glob&lt;/span&gt; &lt;span class="s1"&gt;'*.md'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update bootstrap &lt;code&gt;Application&lt;/code&gt; manifests, every &lt;code&gt;ApplicationSet&lt;/code&gt; &lt;strong&gt;generator&lt;/strong&gt; and &lt;strong&gt;template&lt;/strong&gt; &lt;code&gt;repoURL&lt;/code&gt;, any child &lt;code&gt;Application&lt;/code&gt; checked in with a literal URL, and docs or scripts that build repository secrets. Commit and push.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step B — Patch Applications in the cluster
&lt;/h4&gt;

&lt;p&gt;Still in a broken state, the cluster cannot always sync from Git, so you patch live objects once. Set shell variables to your real values (namespace if not &lt;code&gt;argocd&lt;/code&gt;, old UUID, new UUID):&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;NS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;argocd
&lt;span class="nv"&gt;OLD_UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
&lt;span class="nv"&gt;NEW_UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ffffffff-eeee-dddd-cccc-bbbbbbbbbbbb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List apps whose single &lt;code&gt;spec.source.repoURL&lt;/code&gt; still contains the old UUID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get applications &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; json | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;--arg&lt;/span&gt; u &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OLD_UUID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'.items[] | select(.spec.source.repoURL // "" | contains($u)) | .metadata.name'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each name, the safest approach is to take the existing URL from the object and swap the UUID in the shell, then merge-patch:&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;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-app
&lt;span class="nv"&gt;oldurl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get application &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.spec.source.repoURL}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;newurl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;oldurl&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="nv"&gt;$OLD_UUID&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$NEW_UUID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
kubectl patch application &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt; merge &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;spec&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;source&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;repoURL&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$newurl&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use &lt;strong&gt;&lt;code&gt;spec.sources&lt;/code&gt;&lt;/strong&gt; (multi-source), repeat the idea per entry that points at CodeConnections.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step C — Patch ApplicationSets (do not skip this)
&lt;/h4&gt;

&lt;p&gt;Confirm the old UUID still appears on &lt;strong&gt;spec&lt;/strong&gt; (ignore &lt;strong&gt;status&lt;/strong&gt; for a moment):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get applicationsets &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; yaml | rg &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OLD_UUID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To replace the UUID everywhere under each set’s JSON (review before you run this in production—I exported one set first and eyeballed it):&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="k"&gt;for &lt;/span&gt;name &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;kubectl get applicationsets &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; json | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;--arg&lt;/span&gt; u &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OLD_UUID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'.items[] | select(.. | strings? | contains($u)) | .metadata.name'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;kubectl get applicationset &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; json | &lt;span class="se"&gt;\&lt;/span&gt;
    jq &lt;span class="nt"&gt;--arg&lt;/span&gt; o &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OLD_UUID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--arg&lt;/span&gt; n &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEW_UUID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s1"&gt;'del(.status) | walk(if type == "string" then gsub($o; $n) else . end)'&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
    kubectl replace &lt;span class="nt"&gt;-f&lt;/span&gt; -
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;walk&lt;/code&gt; replaces &lt;strong&gt;every&lt;/strong&gt; occurrence of the old UUID string in that JSON. Pick a UUID substring unique to the connection so you do not hit unrelated fields.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step D — Refresh and converge
&lt;/h4&gt;

&lt;p&gt;Hard refresh apps in the UI or CLI, then let GitOps catch up. If Git still cannot be fetched until &lt;strong&gt;one&lt;/strong&gt; app is fixed, patch your &lt;strong&gt;bootstrap&lt;/strong&gt; Application (the one that applies the rest of the tree) to the new URL first, then repeat the rest—classic chicken-and-egg.&lt;/p&gt;




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

&lt;h4&gt;
  
  
  Application stuck deleting
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl patch application my-app &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"metadata":{"finalizers":null}}'&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;merge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  I deleted an app and it came back
&lt;/h4&gt;

&lt;p&gt;An &lt;strong&gt;ApplicationSet&lt;/strong&gt; owns it (&lt;code&gt;ownerReferences&lt;/code&gt;). Fix the set and Git; deleting the app alone will not stick.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/codeconnections/latest/userguide/welcome.html" rel="noopener noreferrer"&gt;AWS CodeConnections User Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/argo-cd.html" rel="noopener noreferrer"&gt;Amazon EKS: Argo CD capability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://argo-cd.readthedocs.io/en/stable/user-guide/application-specification/" rel="noopener noreferrer"&gt;Argo CD – Application spec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/" rel="noopener noreferrer"&gt;Argo CD – ApplicationSet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/jajera/argo-cd-on-eks-git-repo-access-with-aws-codeconnections-and-terraform-3ejl"&gt;Argo CD on EKS: Git repo access with AWS CodeConnections and Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/jajera/why-your-kustomize-remote-bases-break-on-managed-argo-cd-and-how-to-fix-it-26i6"&gt;Why Your Kustomize Remote Bases Break on Managed Argo CD (and How to Fix It)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>argocd</category>
      <category>codeconnections</category>
      <category>eks</category>
      <category>gitops</category>
    </item>
    <item>
      <title>Host a Static Site on EC2 with Terraform (VPC, Optional ALB)</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sat, 28 Mar 2026 08:54:20 +0000</pubDate>
      <link>https://forem.com/jajera/host-a-static-site-on-ec2-with-terraform-vpc-optional-alb-1ba9</link>
      <guid>https://forem.com/jajera/host-a-static-site-on-ec2-with-terraform-vpc-optional-alb-1ba9</guid>
      <description>&lt;h2&gt;
  
  
  Host a Static Site on EC2 with Terraform (VPC, Optional ALB, Session Manager)
&lt;/h2&gt;

&lt;p&gt;For static sites, &lt;strong&gt;S3 + CloudFront&lt;/strong&gt; is usually the better default. This post points at a small Terraform demo and pulls a few excerpts from &lt;strong&gt;&lt;code&gt;main.tf&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;iam.tf&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;user_data.tftpl&lt;/code&gt;&lt;/strong&gt;. Full layout: &lt;a href="https://github.com/jdevto/tf-aws-ec2-static-demo" rel="noopener noreferrer"&gt;tf-aws-ec2-static-demo&lt;/a&gt; (local path &lt;code&gt;~/workspace/jdevto/tf-aws-ec2-static-demo&lt;/code&gt; if you keep it beside this blog repo). &lt;strong&gt;S3 + CloudFront&lt;/strong&gt; with Terraform: &lt;a href="https://github.com/jdevto/blog/blob/main/articles/art0018.md" rel="noopener noreferrer"&gt;art0018&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;The demo provisions a VPC, &lt;strong&gt;nginx&lt;/strong&gt; on &lt;strong&gt;Amazon Linux 2023&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/strong&gt; (AZ + private IP from &lt;strong&gt;IMDSv2&lt;/strong&gt;), and &lt;strong&gt;&lt;code&gt;robots.txt&lt;/code&gt;&lt;/strong&gt;. &lt;strong&gt;&lt;code&gt;use_alb=false&lt;/code&gt; (default):&lt;/strong&gt; one instance in one &lt;strong&gt;public&lt;/strong&gt; subnet; clients hit &lt;strong&gt;:80&lt;/strong&gt; on the instance public IP (CIDR from &lt;strong&gt;&lt;code&gt;allowed_http_cidr&lt;/code&gt;&lt;/strong&gt;). &lt;strong&gt;&lt;code&gt;use_alb=true&lt;/code&gt;:&lt;/strong&gt; &lt;strong&gt;internet ALB&lt;/strong&gt; across &lt;strong&gt;&lt;code&gt;az_count&lt;/code&gt; ≥ 2&lt;/strong&gt; public subnets; &lt;strong&gt;&lt;code&gt;az_count&lt;/code&gt;&lt;/strong&gt; instances in &lt;strong&gt;private&lt;/strong&gt; subnets (one per AZ), &lt;strong&gt;NAT&lt;/strong&gt; for egress, no instance public IP; instances register to one target group with &lt;strong&gt;HTTP /&lt;/strong&gt; health check expecting &lt;strong&gt;200&lt;/strong&gt;; instance SG allows &lt;strong&gt;:80&lt;/strong&gt; from the &lt;strong&gt;ALB&lt;/strong&gt; SG and from the &lt;strong&gt;VPC CIDR&lt;/strong&gt;. &lt;strong&gt;&lt;code&gt;enable_ssm=true&lt;/code&gt; (default):&lt;/strong&gt; &lt;strong&gt;Session Manager&lt;/strong&gt; with &lt;strong&gt;AmazonSSMManagedInstanceCore&lt;/strong&gt;—no SSH in the template.&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="nx"&gt;subnet_count&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;use_alb&lt;/span&gt; &lt;span class="err"&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;az_count&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="nx"&gt;instance_count&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;use_alb&lt;/span&gt; &lt;span class="err"&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;az_count&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&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 hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"az_count"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="nx"&gt;validation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;condition&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;az_count&lt;/span&gt; &lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&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;az_count&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&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;use_alb&lt;/span&gt; &lt;span class="err"&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;az_count&lt;/span&gt; &lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;error_message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"az_count must be between 1 and 6, and at least 2 when use_alb is true (ALB requirement)."&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;h3&gt;
  
  
  Why EC2?
&lt;/h3&gt;

&lt;p&gt;Learning stack (Terraform + VPC + optional ELB), policy that prefers VPC-hosted sites, temporary lift-and-shift, or a box that might grow non-static behavior later. None of that makes EC2 the default for a &lt;strong&gt;pure&lt;/strong&gt; static site.&lt;/p&gt;

&lt;h3&gt;
  
  
  VPC
&lt;/h3&gt;

&lt;p&gt;Every instance is in a &lt;strong&gt;VPC&lt;/strong&gt; and a &lt;strong&gt;subnet&lt;/strong&gt; (&lt;a href="https://aws.amazon.com/ec2/faqs/#ec2-classic" rel="noopener noreferrer"&gt;EC2-Classic&lt;/a&gt; is gone for new accounts). The demo creates its own VPC—public subnets always; &lt;strong&gt;private subnets + NAT&lt;/strong&gt; when &lt;strong&gt;&lt;code&gt;use_alb=true&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use_alb = false
  +----------+   :80 (public IP)   +----------------+
  | Clients  | ------------------&amp;gt; | EC2 (nginx)    |
  +----------+                     +----------------+

use_alb = true
  +----------+   :80   +-----+   :80   +----------------+
  | Clients  | ------&amp;gt; | ALB | ------&amp;gt; | EC2 × az_count |
  +----------+         +-----+         | (private)      |
                                      +----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NAT&lt;/strong&gt; is billed when the ALB path is on. Repeated &lt;strong&gt;&lt;code&gt;curl&lt;/code&gt;&lt;/strong&gt; to the ALB can show different &lt;strong&gt;AZ / private IP&lt;/strong&gt; in &lt;strong&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/strong&gt; as backends rotate. &lt;strong&gt;&lt;code&gt;user_data&lt;/code&gt;&lt;/strong&gt; fills those fields via &lt;strong&gt;IMDSv2&lt;/strong&gt; after &lt;strong&gt;nginx&lt;/strong&gt; is up:&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;IMDS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="s2"&gt;"http://169.254.169.254/latest/api/token"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token-ttl-seconds: 21600"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;PRIVATE_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$IMDS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"http://169.254.169.254/latest/meta-data/local-ipv4"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;AZ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$IMDS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"http://169.254.169.254/latest/meta-data/placement/availability-zone"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;main.tf&lt;/code&gt; — placement and bootstrap:&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&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;instance_count&lt;/span&gt;

  &lt;span class="nx"&gt;subnet_id&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;use_alb&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&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;vpc_security_group_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&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;user_data&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;templatefile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"${path.module}/user_data.tftpl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;enable_ssm&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;enable_ssm&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;user_data_replace_on_change&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;iam_instance_profile&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;enable_ssm&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_instance_profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ssm&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;name&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&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;main.tf&lt;/code&gt; — instance ingress (ALB on) and target group health check:&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;dynamic&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&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;use_alb&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="nx"&gt;content&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;"HTTP from ALB security group (forwarded client traffic)"&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;security_groups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alb&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="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;dynamic&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&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;use_alb&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="nx"&gt;content&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;"HTTP from VPC for ALB health checks and internal probes"&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_block&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;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_lb_target_group"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&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;use_alb&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

  &lt;span class="nx"&gt;port&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HTTP"&lt;/span&gt;
  &lt;span class="nx"&gt;protocol_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HTTP1"&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;

  &lt;span class="nx"&gt;health_check&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&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;protocol&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HTTP"&lt;/span&gt;
    &lt;span class="nx"&gt;port&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"traffic-port"&lt;/span&gt;
    &lt;span class="nx"&gt;path&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;
    &lt;span class="nx"&gt;matcher&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"200"&lt;/span&gt;
    &lt;span class="nx"&gt;interval&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="nx"&gt;healthy_threshold&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;unhealthy_threshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&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;h3&gt;
  
  
  Session Manager
&lt;/h3&gt;

&lt;p&gt;Agent + &lt;strong&gt;AmazonSSMManagedInstanceCore&lt;/strong&gt;; &lt;strong&gt;&lt;code&gt;user_data.tftpl&lt;/code&gt;&lt;/strong&gt; via &lt;strong&gt;&lt;code&gt;templatefile&lt;/code&gt;&lt;/strong&gt; so &lt;strong&gt;&lt;code&gt;#!/bin/bash&lt;/code&gt;&lt;/strong&gt; is at column 0 (indented &lt;strong&gt;&lt;code&gt;heredoc&lt;/code&gt;&lt;/strong&gt; in Terraform often breaks the shebang). With &lt;strong&gt;&lt;code&gt;enable_ssm&lt;/code&gt;&lt;/strong&gt;, the template pulls the &lt;strong&gt;SSM Agent RPM from S3&lt;/strong&gt; and starts the service. Use the AMI’s &lt;strong&gt;&lt;code&gt;curl&lt;/code&gt;&lt;/strong&gt;—do not &lt;strong&gt;&lt;code&gt;dnf install curl&lt;/code&gt;&lt;/strong&gt; on AL2023 (conflicts with &lt;strong&gt;&lt;code&gt;curl-minimal&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;set -e&lt;/code&gt;&lt;/strong&gt; aborts before &lt;strong&gt;nginx&lt;/strong&gt;). Egress: &lt;strong&gt;NAT&lt;/strong&gt; or &lt;strong&gt;SSM VPC endpoints&lt;/strong&gt;. Docs: &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/agent-install-al2.html" rel="noopener noreferrer"&gt;agent install&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent-status-and-restart.html" rel="noopener noreferrer"&gt;status&lt;/a&gt;. Your principal needs &lt;strong&gt;&lt;code&gt;ssm:StartSession&lt;/code&gt;&lt;/strong&gt;; &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html" rel="noopener noreferrer"&gt;Session Manager plugin&lt;/a&gt; for CLI. After apply, wait for &lt;strong&gt;Online&lt;/strong&gt; in Fleet Manager; with &lt;strong&gt;&lt;code&gt;use_alb=true&lt;/code&gt;&lt;/strong&gt;, use &lt;strong&gt;&lt;code&gt;instance_ids&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;code&gt;ssm_start_session_command&lt;/code&gt;&lt;/strong&gt; (first instance). &lt;strong&gt;&lt;code&gt;%{ if enable_ssm ~}&lt;/code&gt;&lt;/strong&gt; … &lt;strong&gt;&lt;code&gt;%{ endif ~}&lt;/code&gt;&lt;/strong&gt; wraps the SSM block in the template.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;iam.tf&lt;/code&gt;:&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role_policy_attachment"&lt;/span&gt; &lt;span class="s2"&gt;"ssm_core"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&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;enable_ssm&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ssm&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;name&lt;/span&gt;
  &lt;span class="nx"&gt;policy_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"&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;user_data.tftpl&lt;/code&gt; (SSM path):&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="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Column-0 shebang required: indented Terraform heredocs break #!/bin/bash and cloud-init may skip the script.&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-eux&lt;/span&gt;
&lt;span class="c"&gt;# Do not `dnf install curl` here: AL2023 ships curl-minimal; installing full curl conflicts and aborts the whole script under set -e.&lt;/span&gt;
&lt;span class="nv"&gt;SSM_RPM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
  &lt;/span&gt;x86_64&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;SSM_RPM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  aarch64&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;SSM_RPM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_arm64/amazon-ssm-agent.rpm"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Unsupported arch for SSM agent RPM"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1 &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/amazon-ssm-agent.rpm &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SSM_RPM&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; /tmp/amazon-ssm-agent.rpm
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /tmp/amazon-ssm-agent.rpm
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;amazon-ssm-agent
systemctl restart amazon-ssm-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  robots.txt
&lt;/h3&gt;

&lt;p&gt;Crawler hint file—not security. The demo writes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User-agent: *
Allow: /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;user_data.tftpl&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;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/usr/share/nginx/html/robots.txt &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;ROBOTS&lt;/span&gt;&lt;span class="sh"&gt;'
User-agent: *
Allow: /
&lt;/span&gt;&lt;span class="no"&gt;ROBOTS
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://developers.google.com/search/docs/crawling-indexing/robots/intro" rel="noopener noreferrer"&gt;Google: robots.txt&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run it
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/jdevto/tf-aws-ec2-static-demo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;tf-aws-ec2-static-demo
terraform init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ALB (needs ≥2 AZs; default &lt;strong&gt;&lt;code&gt;az_count&lt;/code&gt;&lt;/strong&gt; is 3):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply &lt;span class="nt"&gt;-var&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"use_alb=true"&lt;/span&gt;
&lt;span class="c"&gt;# terraform apply -var="use_alb=true" -var="az_count=2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait &lt;strong&gt;1–2 minutes&lt;/strong&gt; after first boot for &lt;strong&gt;&lt;code&gt;user_data&lt;/code&gt;&lt;/strong&gt;. &lt;strong&gt;&lt;code&gt;user_data_replace_on_change = true&lt;/code&gt;&lt;/strong&gt; replaces instances when the template changes. Variables: repo &lt;strong&gt;&lt;a href="https://github.com/jdevto/tf-aws-ec2-static-demo/blob/main/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt;&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;"use_alb"&lt;/span&gt; &lt;span class="p"&gt;{&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;bool&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;"allowed_http_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&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;"0.0.0.0/0"&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;"enable_ssm"&lt;/span&gt; &lt;span class="p"&gt;{&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;bool&lt;/span&gt;
  &lt;span class="nx"&gt;default&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform output verify_commands
&lt;span class="c"&gt;# use_alb=false:&lt;/span&gt;
curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"http://&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; instance_public_ip&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/robots.txt"&lt;/span&gt;
&lt;span class="c"&gt;# use_alb=true (no instance public IP):&lt;/span&gt;
&lt;span class="c"&gt;# curl -sS "$(terraform output -raw website_url_alb)/robots.txt"&lt;/span&gt;
terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; ssm_start_session_command
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  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;Check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Timeout / connection refused&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;use_alb=true&lt;/code&gt;:&lt;/strong&gt; use ALB URL, not instance IP. &lt;strong&gt;&lt;code&gt;false&lt;/code&gt;:&lt;/strong&gt; SG, &lt;strong&gt;&lt;code&gt;allowed_http_cidr&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;user_data&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;instance_public_ip&lt;/code&gt;&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALB unhealthy / &lt;strong&gt;502&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;SG &lt;strong&gt;:80&lt;/strong&gt; from ALB + VPC; &lt;strong&gt;&lt;code&gt;/&lt;/code&gt;&lt;/strong&gt; returns &lt;strong&gt;200&lt;/strong&gt;. &lt;strong&gt;&lt;code&gt;cloud-init-output.log&lt;/code&gt;:&lt;/strong&gt; failed &lt;strong&gt;&lt;code&gt;user_data&lt;/code&gt;&lt;/strong&gt; (e.g. &lt;strong&gt;&lt;code&gt;dnf install curl&lt;/code&gt;&lt;/strong&gt; vs &lt;strong&gt;&lt;code&gt;curl-minimal&lt;/code&gt;&lt;/strong&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apply limits&lt;/td&gt;
&lt;td&gt;Other region/account or limit increase.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSM offline / denied&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;enable_ssm&lt;/code&gt;&lt;/strong&gt;, agent time, &lt;strong&gt;&lt;code&gt;ssm:StartSession&lt;/code&gt;&lt;/strong&gt;, outbound to SSM (NAT or endpoints).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jdevto/tf-aws-ec2-static-demo" rel="noopener noreferrer"&gt;tf-aws-ec2-static-demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs" rel="noopener noreferrer"&gt;Terraform AWS provider&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html" rel="noopener noreferrer"&gt;Amazon VPC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html" rel="noopener noreferrer"&gt;Application Load Balancer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html" rel="noopener noreferrer"&gt;Session Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html" rel="noopener noreferrer"&gt;Session Manager plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terraform</category>
      <category>ec2</category>
      <category>vpc</category>
      <category>ssm</category>
    </item>
    <item>
      <title>Using GoAccess to View nginx Logs</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sat, 28 Mar 2026 08:54:19 +0000</pubDate>
      <link>https://forem.com/jajera/using-goaccess-to-view-nginx-logs-1d1c</link>
      <guid>https://forem.com/jajera/using-goaccess-to-view-nginx-logs-1d1c</guid>
      <description>&lt;h2&gt;
  
  
  Using GoAccess to View nginx Logs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://goaccess.io/" rel="noopener noreferrer"&gt;GoAccess&lt;/a&gt; is a fast terminal and HTML log analyzer for nginx access logs (requests/sec, status codes, top URLs, referrers, user agents). This guide covers installation, optional S3 download, and typical run modes.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;Install &lt;strong&gt;GoAccess&lt;/strong&gt; (package manager, source, or &lt;a href="https://github.com/jdevto/cli-tools" rel="noopener noreferrer"&gt;jdevto/cli-tools&lt;/a&gt; script)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional:&lt;/strong&gt; sync logs from &lt;strong&gt;S3&lt;/strong&gt; when they are not on the machine you analyze from&lt;/li&gt;
&lt;li&gt;Run &lt;strong&gt;TUI&lt;/strong&gt;, &lt;strong&gt;HTML report&lt;/strong&gt;, &lt;strong&gt;live tail&lt;/strong&gt;, or &lt;strong&gt;compressed&lt;/strong&gt; input; use &lt;code&gt;COMBINED&lt;/code&gt; or &lt;code&gt;COMMON&lt;/code&gt; to match nginx &lt;code&gt;log_format&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skip §4 if logs are already on disk.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;Readable &lt;strong&gt;nginx access logs&lt;/strong&gt; (usually &lt;strong&gt;combined&lt;/strong&gt; or &lt;strong&gt;common&lt;/strong&gt; format)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sudo&lt;/strong&gt; for distro package installs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 (optional):&lt;/strong&gt; AWS CLI v2 and &lt;code&gt;s3:GetObject&lt;/code&gt; / &lt;code&gt;s3:ListBucket&lt;/code&gt; for the prefix you use&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Install GoAccess
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Fedora / RHEL / CentOS Stream
&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;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; goaccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Debian / Ubuntu
&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;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; goaccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Newer or custom build:&lt;/strong&gt; &lt;a href="https://goaccess.io/download" rel="noopener noreferrer"&gt;official build instructions&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  jdevto/cli-tools installer (optional)
&lt;/h4&gt;

&lt;p&gt;Builds from the &lt;a href="https://tar.goaccess.io" rel="noopener noreferrer"&gt;official tarball&lt;/a&gt;; details and env vars in &lt;a href="https://github.com/jdevto/cli-tools/blob/main/docs/install_goaccess.md" rel="noopener noreferrer"&gt;install_goaccess.md&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/jdevto/cli-tools/main/scripts/install_goaccess.sh&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/jdevto/cli-tools/main/scripts/install_goaccess.sh&lt;span class="o"&gt;)&lt;/span&gt; uninstall
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Optional: Download nginx Logs from S3
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Single file
&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/nginx-logs
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://my-bucket/path/to/access.log ~/nginx-logs/access.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key is &lt;code&gt;.gz&lt;/code&gt; only:&lt;/strong&gt; download, decompress, or stream without extracting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://my-bucket/path/to/access.log.gz ~/nginx-logs/access.log.gz
&lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; ~/nginx-logs/access.log.gz
&lt;span class="c"&gt;# or: zcat ~/nginx-logs/access.log.gz | goaccess - --log-format=COMBINED&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Prefix (many files)
&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/nginx-logs
aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;s3://my-bucket/nginx-logs/ ~/nginx-logs/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace bucket and prefix. If you only have &lt;strong&gt;&lt;code&gt;*.gz&lt;/code&gt;&lt;/strong&gt;, after sync use &lt;code&gt;zcat ~/nginx-logs/*.gz &amp;gt; ~/nginx-logs/merged-access.log&lt;/code&gt; (order across files may not be chronological) or decompress then merge plain &lt;code&gt;*.log&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Merge plain logs (optional)
&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;cat&lt;/span&gt; ~/nginx-logs/&lt;span class="k"&gt;*&lt;/span&gt;.log &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/nginx-logs/merged-access.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Run GoAccess
&lt;/h3&gt;

&lt;p&gt;Match &lt;strong&gt;&lt;code&gt;--log-format&lt;/code&gt;&lt;/strong&gt; to nginx (&lt;code&gt;COMBINED&lt;/code&gt; is the usual default).&lt;/p&gt;

&lt;h4&gt;
  
  
  Terminal UI
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  HTML report
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED &lt;span class="nt"&gt;-o&lt;/span&gt; report.html &lt;span class="nt"&gt;--no-parsing-spinner&lt;/span&gt;
xdg-open report.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Live log
&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;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/nginx/access.log | goaccess - &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Common log format
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMMON
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Custom &lt;code&gt;log_format&lt;/code&gt;: map fields with GoAccess &lt;a href="https://goaccess.io/man" rel="noopener noreferrer"&gt;custom format&lt;/a&gt; tokens.&lt;/p&gt;

&lt;h4&gt;
  
  
  Compressed on disk
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zcat /var/log/nginx/access.log.&lt;span class="k"&gt;*&lt;/span&gt;.gz | goaccess - &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; goaccess
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/nginx-logs &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;s3://YOUR_BUCKET/YOUR_PREFIX/ ~/nginx-logs/   &lt;span class="c"&gt;# optional&lt;/span&gt;
goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED &lt;span class="nt"&gt;-o&lt;/span&gt; report.html &lt;span class="nt"&gt;--no-parsing-spinner&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; xdg-open report.html
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/nginx/access.log | goaccess - &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;&lt;strong&gt;Parsed 0 lines / wrong numbers:&lt;/strong&gt; Log line shape ≠ &lt;code&gt;COMBINED&lt;/code&gt;/&lt;code&gt;COMMON&lt;/code&gt;. Compare &lt;code&gt;head -1&lt;/code&gt; to nginx &lt;code&gt;log_format&lt;/code&gt; and adjust &lt;code&gt;--log-format&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission denied on log:&lt;/strong&gt; &lt;code&gt;sudo goaccess ...&lt;/code&gt;, fix group on log files, or copy the log to your home directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S3 Access Denied:&lt;/strong&gt; IAM on bucket/prefix; check &lt;code&gt;AWS_PROFILE&lt;/code&gt; / role.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live tail idle:&lt;/strong&gt; Confirm nginx &lt;code&gt;access_log&lt;/code&gt; path and read permissions on the active file.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://goaccess.io/" rel="noopener noreferrer"&gt;GoAccess&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://goaccess.io/man" rel="noopener noreferrer"&gt;GoAccess man (log formats)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nginx.org/en/docs/http/ngx_http_log_module.html" rel="noopener noreferrer"&gt;nginx log module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/s3/cp.html" rel="noopener noreferrer"&gt;AWS CLI s3 cp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>goaccess</category>
      <category>nginx</category>
      <category>logs</category>
      <category>aws</category>
    </item>
    <item>
      <title>Using GoAccess to View nginx Logs</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sun, 22 Mar 2026 23:22:42 +0000</pubDate>
      <link>https://forem.com/jajera/using-goaccess-to-view-nginx-logs-1491</link>
      <guid>https://forem.com/jajera/using-goaccess-to-view-nginx-logs-1491</guid>
      <description>&lt;h2&gt;
  
  
  Using GoAccess to View nginx Logs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://goaccess.io/" rel="noopener noreferrer"&gt;GoAccess&lt;/a&gt; is a fast terminal and HTML log analyzer for nginx access logs (requests/sec, status codes, top URLs, referrers, user agents). This guide covers installation, optional S3 download, and typical run modes.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;Install &lt;strong&gt;GoAccess&lt;/strong&gt; (package manager, source, or &lt;a href="https://github.com/jdevto/cli-tools" rel="noopener noreferrer"&gt;jdevto/cli-tools&lt;/a&gt; script)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional:&lt;/strong&gt; sync logs from &lt;strong&gt;S3&lt;/strong&gt; when they are not on the machine you analyze from&lt;/li&gt;
&lt;li&gt;Run &lt;strong&gt;TUI&lt;/strong&gt;, &lt;strong&gt;HTML report&lt;/strong&gt;, &lt;strong&gt;live tail&lt;/strong&gt;, or &lt;strong&gt;compressed&lt;/strong&gt; input; use &lt;code&gt;COMBINED&lt;/code&gt; or &lt;code&gt;COMMON&lt;/code&gt; to match nginx &lt;code&gt;log_format&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skip §4 if logs are already on disk.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;Readable &lt;strong&gt;nginx access logs&lt;/strong&gt; (usually &lt;strong&gt;combined&lt;/strong&gt; or &lt;strong&gt;common&lt;/strong&gt; format)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sudo&lt;/strong&gt; for distro package installs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 (optional):&lt;/strong&gt; AWS CLI v2 and &lt;code&gt;s3:GetObject&lt;/code&gt; / &lt;code&gt;s3:ListBucket&lt;/code&gt; for the prefix you use&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Install GoAccess
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Fedora / RHEL / CentOS Stream
&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;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; goaccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Debian / Ubuntu
&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;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; goaccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Newer or custom build:&lt;/strong&gt; &lt;a href="https://goaccess.io/download" rel="noopener noreferrer"&gt;official build instructions&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  jdevto/cli-tools installer (optional)
&lt;/h4&gt;

&lt;p&gt;Builds from the &lt;a href="https://tar.goaccess.io" rel="noopener noreferrer"&gt;official tarball&lt;/a&gt;; details and env vars in &lt;a href="https://github.com/jdevto/cli-tools/blob/main/docs/install_goaccess.md" rel="noopener noreferrer"&gt;install_goaccess.md&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/jdevto/cli-tools/main/scripts/install_goaccess.sh&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/jdevto/cli-tools/main/scripts/install_goaccess.sh&lt;span class="o"&gt;)&lt;/span&gt; uninstall
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Optional: Download nginx Logs from S3
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Single file
&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/nginx-logs
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://my-bucket/path/to/access.log ~/nginx-logs/access.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key is &lt;code&gt;.gz&lt;/code&gt; only:&lt;/strong&gt; download, decompress, or stream without extracting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://my-bucket/path/to/access.log.gz ~/nginx-logs/access.log.gz
&lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; ~/nginx-logs/access.log.gz
&lt;span class="c"&gt;# or: zcat ~/nginx-logs/access.log.gz | goaccess - --log-format=COMBINED&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Prefix (many files)
&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/nginx-logs
aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;s3://my-bucket/nginx-logs/ ~/nginx-logs/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace bucket and prefix. If you only have &lt;strong&gt;&lt;code&gt;*.gz&lt;/code&gt;&lt;/strong&gt;, after sync use &lt;code&gt;zcat ~/nginx-logs/*.gz &amp;gt; ~/nginx-logs/merged-access.log&lt;/code&gt; (order across files may not be chronological) or decompress then merge plain &lt;code&gt;*.log&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Merge plain logs (optional)
&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;cat&lt;/span&gt; ~/nginx-logs/&lt;span class="k"&gt;*&lt;/span&gt;.log &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/nginx-logs/merged-access.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Run GoAccess
&lt;/h3&gt;

&lt;p&gt;Match &lt;strong&gt;&lt;code&gt;--log-format&lt;/code&gt;&lt;/strong&gt; to nginx (&lt;code&gt;COMBINED&lt;/code&gt; is the usual default).&lt;/p&gt;

&lt;h4&gt;
  
  
  Terminal UI
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  HTML report
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED &lt;span class="nt"&gt;-o&lt;/span&gt; report.html &lt;span class="nt"&gt;--no-parsing-spinner&lt;/span&gt;
xdg-open report.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Live log
&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;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/nginx/access.log | goaccess - &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Common log format
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMMON
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Custom &lt;code&gt;log_format&lt;/code&gt;: map fields with GoAccess &lt;a href="https://goaccess.io/man" rel="noopener noreferrer"&gt;custom format&lt;/a&gt; tokens.&lt;/p&gt;

&lt;h4&gt;
  
  
  Compressed on disk
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zcat /var/log/nginx/access.log.&lt;span class="k"&gt;*&lt;/span&gt;.gz | goaccess - &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; goaccess
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/nginx-logs &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;s3://YOUR_BUCKET/YOUR_PREFIX/ ~/nginx-logs/   &lt;span class="c"&gt;# optional&lt;/span&gt;
goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
goaccess /var/log/nginx/access.log &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED &lt;span class="nt"&gt;-o&lt;/span&gt; report.html &lt;span class="nt"&gt;--no-parsing-spinner&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; xdg-open report.html
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/nginx/access.log | goaccess - &lt;span class="nt"&gt;--log-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;COMBINED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;&lt;strong&gt;Parsed 0 lines / wrong numbers:&lt;/strong&gt; Log line shape ≠ &lt;code&gt;COMBINED&lt;/code&gt;/&lt;code&gt;COMMON&lt;/code&gt;. Compare &lt;code&gt;head -1&lt;/code&gt; to nginx &lt;code&gt;log_format&lt;/code&gt; and adjust &lt;code&gt;--log-format&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission denied on log:&lt;/strong&gt; &lt;code&gt;sudo goaccess ...&lt;/code&gt;, fix group on log files, or copy the log to your home directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S3 Access Denied:&lt;/strong&gt; IAM on bucket/prefix; check &lt;code&gt;AWS_PROFILE&lt;/code&gt; / role.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live tail idle:&lt;/strong&gt; Confirm nginx &lt;code&gt;access_log&lt;/code&gt; path and read permissions on the active file.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://goaccess.io/" rel="noopener noreferrer"&gt;GoAccess&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://goaccess.io/man" rel="noopener noreferrer"&gt;GoAccess man (log formats)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nginx.org/en/docs/http/ngx_http_log_module.html" rel="noopener noreferrer"&gt;nginx log module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/reference/s3/cp.html" rel="noopener noreferrer"&gt;AWS CLI s3 cp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>goaccess</category>
      <category>nginx</category>
      <category>logs</category>
      <category>aws</category>
    </item>
    <item>
      <title>Why Your Kustomize Remote Bases Break on Managed Argo CD (and How to Fix It)</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Mon, 16 Mar 2026 10:50:40 +0000</pubDate>
      <link>https://forem.com/jajera/why-your-kustomize-remote-bases-break-on-managed-argo-cd-and-how-to-fix-it-26i6</link>
      <guid>https://forem.com/jajera/why-your-kustomize-remote-bases-break-on-managed-argo-cd-and-how-to-fix-it-26i6</guid>
      <description>&lt;h2&gt;
  
  
  Why Your Kustomize Remote Bases Break on Managed Argo CD (and How to Fix It)
&lt;/h2&gt;

&lt;p&gt;You switched to managed Argo CD (e.g. EKS Capabilities) and use AWS CodeConnections for Git. Your Applications that pull from &lt;strong&gt;public&lt;/strong&gt; GitHub remotes in Kustomize suddenly fail with errors like "Password authentication is not supported" or "Authentication failed". The same kustomization worked with self-managed Argo CD. This article explains why CodeConnections causes that and how &lt;strong&gt;vendoring&lt;/strong&gt; those remote bases into your repo fixes it.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;What this guide does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explains why Kustomize remote resources (e.g. &lt;code&gt;github.com/open-policy-agent/gatekeeper-library/...&lt;/code&gt;) break when Argo CD uses CodeConnections for Git&lt;/li&gt;
&lt;li&gt;Shows that the credential helper is applied to every &lt;code&gt;github.com&lt;/code&gt; URL, including public repos, and GitHub rejects those credentials&lt;/li&gt;
&lt;li&gt;Describes when to vendor and a minimal how-to: download remote files at a pinned ref, add them to your repo, and point your kustomization at local files instead of remote URLs&lt;/li&gt;
&lt;li&gt;Suggests keeping a README in the vendored directory so future upgrades are a repeatable re-download and commit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it breaks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With managed Argo CD + CodeConnections, one credential is used for all GitHub access. When Kustomize runs &lt;code&gt;git fetch&lt;/code&gt; for a remote base, Argo CD passes that same credential. Public repos don't need auth—but they receive it anyway, and GitHub rejects the format (e.g. "Password authentication is not supported").&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;Managed Argo CD (e.g. &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/argo-cd.html" rel="noopener noreferrer"&gt;EKS Argo CD capability&lt;/a&gt;) with &lt;a href="https://docs.aws.amazon.com/codeconnections/latest/userguide/welcome.html" rel="noopener noreferrer"&gt;AWS CodeConnections&lt;/a&gt; used for Git (see &lt;a href="https://dev.to/jajera/argo-cd-on-eks-git-repo-access-with-aws-codeconnections-and-terraform-3ejl"&gt;Argo CD on EKS: Git repo access with AWS CodeConnections and Terraform&lt;/a&gt; for setup)&lt;/li&gt;
&lt;li&gt;At least one Argo CD Application whose source uses Kustomize with &lt;strong&gt;remote&lt;/strong&gt; &lt;code&gt;resources&lt;/code&gt; or &lt;code&gt;bases&lt;/code&gt; pointing at GitHub (e.g. &lt;code&gt;github.com/org/repo/path?ref=master&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Why remote bases break with CodeConnections
&lt;/h3&gt;

&lt;p&gt;When Argo CD syncs an Application, the repo-server runs &lt;code&gt;kustomize build&lt;/code&gt; over your repo. If your &lt;code&gt;kustomization.yaml&lt;/code&gt; lists remote resources like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;github.com/open-policy-agent/gatekeeper-library/library/general/httpsonly?ref=master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kustomize invokes &lt;code&gt;git fetch&lt;/code&gt; for that URL. Argo CD configures a Git credential helper so that any git operation gets credentials. With CodeConnections, that helper returns the &lt;strong&gt;same&lt;/strong&gt; credential (the one for your connected repos) for &lt;strong&gt;every&lt;/strong&gt; &lt;code&gt;github.com&lt;/code&gt; request—including requests to public repos such as &lt;code&gt;open-policy-agent/gatekeeper-library&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Git sends those credentials to GitHub. GitHub then responds with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fatal: Authentication failed for 'https://github.com/open-policy-agent/gatekeeper-library/'
remote: Invalid username or token. Password authentication is not supported for Git operations.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the build fails even though the remote repo is public and would work with no credentials. With self-managed Argo CD you might have used SSH for your app repo; Kustomize’s HTTPS fetches to public GitHub then didn’t use that credential and succeeded. With CodeConnections, one HTTPS credential is used everywhere, and it gets sent to public URLs where it’s invalid.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. When to vendor
&lt;/h3&gt;

&lt;p&gt;Vendor remote bases when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You use &lt;strong&gt;managed Argo CD with CodeConnections&lt;/strong&gt; and your Kustomize app references &lt;strong&gt;public&lt;/strong&gt; GitHub remotes (e.g. gatekeeper-library, shared bases from other orgs).&lt;/li&gt;
&lt;li&gt;You see authentication or "Password authentication is not supported" errors during manifest generation for those remotes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also vendor remotes you don’t control so that upgrades are explicit and reproducible (pin to a commit, re-download when you want to upgrade).&lt;/p&gt;




&lt;h3&gt;
  
  
  5. How to vendor
&lt;/h3&gt;

&lt;p&gt;Vendoring means copying the remote manifest files into your repo and pointing Kustomize at local files instead of remote URLs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Choose a ref&lt;/strong&gt; — Use a commit SHA or tag from the upstream repo (e.g. &lt;code&gt;master&lt;/code&gt; or a specific SHA) so you can reproduce the same content later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Download the remote files&lt;/strong&gt; — Each remote resource is usually a directory containing one or more YAML files (e.g. &lt;code&gt;template.yaml&lt;/code&gt;). Download those files using the raw GitHub URL pattern:
&lt;code&gt;https://raw.githubusercontent.com/&amp;lt;org&amp;gt;/&amp;lt;repo&amp;gt;/&amp;lt;ref&amp;gt;/&amp;lt;path&amp;gt;/&amp;lt;file&amp;gt;.yaml&lt;/code&gt;
Save them under a directory in your repo (e.g. &lt;code&gt;infrastructure/my-app/vendored/&lt;/code&gt;) with clear names (e.g. &lt;code&gt;httpsonly.yaml&lt;/code&gt;, &lt;code&gt;requiredlabels.yaml&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update your kustomization&lt;/strong&gt; — Replace remote &lt;code&gt;resources&lt;/code&gt; entries with the local file names:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before (remote – fails with CodeConnections)&lt;/span&gt;
&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;github.com/open-policy-agent/gatekeeper-library/library/general/httpsonly?ref=master&lt;/span&gt;

&lt;span class="c1"&gt;# After (vendored)&lt;/span&gt;
&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;httpsonly.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Document the source and upgrade process&lt;/strong&gt; — Add a README in the vendored directory that lists:

&lt;ul&gt;
&lt;li&gt;The upstream repo and the ref (commit SHA) you used&lt;/li&gt;
&lt;li&gt;The mapping from each local file to its upstream path&lt;/li&gt;
&lt;li&gt;How to upgrade: re-download from a new ref, overwrite the files, run &lt;code&gt;kustomize build .&lt;/code&gt; to verify, commit, and update the README with the new ref.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Optionally, add a small script or one-liner (e.g. a shell loop with &lt;code&gt;curl&lt;/code&gt;) that downloads all vendored files given a ref, so upgrades are a single command plus commit.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; Managed Argo CD + CodeConnections sends one GitHub credential to every git URL. Kustomize remote bases to public GitHub repos get that credential; GitHub rejects it and the build fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix:&lt;/strong&gt; Vendor those remote bases: download the manifest files at a pinned ref, put them in your repo, and point &lt;code&gt;kustomization.yaml&lt;/code&gt; at the local files. No remote fetch at build time, so no credential is used for those resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upgrades:&lt;/strong&gt; Re-download from a new ref, overwrite the vendored files, verify with &lt;code&gt;kustomize build .&lt;/code&gt;, commit, and update the README with the new ref.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Checklist when vendoring:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick a ref (e.g. latest &lt;code&gt;master&lt;/code&gt; or a commit SHA) from the upstream repo.&lt;/li&gt;
&lt;li&gt;Download each remote file via &lt;code&gt;https://raw.githubusercontent.com/&amp;lt;org&amp;gt;/&amp;lt;repo&amp;gt;/&amp;lt;ref&amp;gt;/&amp;lt;path&amp;gt;/&amp;lt;file&amp;gt;.yaml&lt;/code&gt; into a directory in your repo.&lt;/li&gt;
&lt;li&gt;Replace remote &lt;code&gt;resources&lt;/code&gt; in &lt;code&gt;kustomization.yaml&lt;/code&gt; with the local file names.&lt;/li&gt;
&lt;li&gt;Add a README in that directory with source ref, file mapping, and upgrade steps (and optionally a download script).&lt;/li&gt;
&lt;/ol&gt;




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

&lt;h4&gt;
  
  
  Manifest generation still fails after vendoring
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Confirm &lt;code&gt;kustomization.yaml&lt;/code&gt; lists only &lt;strong&gt;local&lt;/strong&gt; file names (e.g. &lt;code&gt;httpsonly.yaml&lt;/code&gt;), not &lt;code&gt;github.com/...&lt;/code&gt; URLs.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;kustomize build .&lt;/code&gt; from the directory that contains the kustomization and vendored files; fix any path or resource errors before relying on Argo CD.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Upstream added or removed a file
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Re-download from the ref you want (e.g. new commit on &lt;code&gt;master&lt;/code&gt;). Add or remove the corresponding local file and update the &lt;code&gt;resources&lt;/code&gt; list and README.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://argo-cd.readthedocs.io/en/stable/user-guide/kustomize/" rel="noopener noreferrer"&gt;Argo CD – Kustomize&lt;/a&gt; (including private remote bases)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/jajera/argo-cd-on-eks-git-repo-access-with-aws-codeconnections-and-terraform-3ejl"&gt;Argo CD on EKS: Git repo access with AWS CodeConnections and Terraform&lt;/a&gt; (CodeConnections setup)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/open-policy-agent/gatekeeper-library" rel="noopener noreferrer"&gt;gatekeeper-library&lt;/a&gt; (example upstream that’s often used as a Kustomize remote)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>argocd</category>
      <category>kubernetes</category>
      <category>kustomize</category>
      <category>vendoring</category>
    </item>
    <item>
      <title>Argo CD on EKS: Git repo access with AWS CodeConnections and Terraform</title>
      <dc:creator>John  Ajera</dc:creator>
      <pubDate>Sat, 14 Mar 2026 07:31:29 +0000</pubDate>
      <link>https://forem.com/jajera/argo-cd-on-eks-git-repo-access-with-aws-codeconnections-and-terraform-kck</link>
      <guid>https://forem.com/jajera/argo-cd-on-eks-git-repo-access-with-aws-codeconnections-and-terraform-kck</guid>
      <description>&lt;h2&gt;
  
  
  Argo CD on EKS: Git repo access with AWS CodeConnections and Terraform
&lt;/h2&gt;

&lt;p&gt;Argo CD on EKS Capabilities needs to pull from your Git repos. Instead of storing personal access tokens or SSH keys in the cluster, use &lt;strong&gt;AWS CodeConnections&lt;/strong&gt;: one connection authorizes Argo CD to access GitHub via IAM. This guide gives the minimal Terraform (connection + IAM policy on the Argo CD role) and the one-time Console steps to move the connection from Pending to Available. One connection can serve many repos; you only change the owner/repo in the URL.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;What this guide does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a CodeStar connection (GitHub) and grants the Argo CD capability role &lt;code&gt;codeconnections:UseConnection&lt;/code&gt; and &lt;code&gt;codeconnections:GetConnection&lt;/code&gt; so Argo CD can pull from Git without credentials in the cluster&lt;/li&gt;
&lt;li&gt;Walks through the one-time Console step to complete the connection (Pending → Available), including using the &lt;strong&gt;GitHub App&lt;/strong&gt; (e.g. AWS Connector for GitHub) for org repos&lt;/li&gt;
&lt;li&gt;Shows the CodeConnections repo URL format for Argo CD Applications (connection ID is the UUID only, not the ARN)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why CodeConnections?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No PAT or SSH key in Kubernetes; the Argo CD role gets IAM permission to use the connection&lt;/li&gt;
&lt;li&gt;One connection = multiple GitHub repos (same connection ID, different owner/repo in the URL)&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;EKS cluster with the &lt;strong&gt;Argo CD capability&lt;/strong&gt; enabled (Identity Center configured)&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;name&lt;/strong&gt; of the Argo CD capability IAM role (e.g. from your EKS module: &lt;code&gt;cluster_name-argocd-capability-role&lt;/code&gt;, or from AWS Console → IAM → Roles)&lt;/li&gt;
&lt;li&gt;AWS Console access to complete the connection (GitHub OAuth)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Terraform: connection and IAM
&lt;/h3&gt;

&lt;p&gt;Add the following to your Terraform. Replace &lt;code&gt;argocd_capability_role_name&lt;/code&gt; with the actual role name (the role EKS created for the Argo CD capability).&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;"argocd_capability_role_name"&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;"Name of the IAM role used by the Argo CD EKS Capability"&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_codestarconnections_connection"&lt;/span&gt; &lt;span class="s2"&gt;"github"&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;"github"&lt;/span&gt;
  &lt;span class="nx"&gt;provider_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GitHub"&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_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"argocd_codeconnections"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"codeconnections:UseConnection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"codeconnections:GetConnection"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_codestarconnections_connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&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_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"argocd_codeconnections"&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;"argocd-codeconnections"&lt;/span&gt;
  &lt;span class="nx"&gt;role&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;argocd_capability_role_name&lt;/span&gt;
  &lt;span class="nx"&gt;policy&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_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argocd_codeconnections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After &lt;code&gt;terraform apply&lt;/code&gt;, the connection exists in &lt;strong&gt;Pending&lt;/strong&gt; state. Terraform cannot complete it; you do that once in the Console.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. After apply: complete the connection (Pending → Available)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://console.aws.amazon.com/" rel="noopener noreferrer"&gt;AWS Console&lt;/a&gt; in the same region as your Terraform.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Developer Tools&lt;/strong&gt; → &lt;strong&gt;Connections&lt;/strong&gt; (or search &lt;strong&gt;Connections&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;Find the connection (e.g. &lt;code&gt;github&lt;/code&gt;). Status will be &lt;strong&gt;Pending&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click the connection name → &lt;strong&gt;Update pending connection&lt;/strong&gt; (or &lt;strong&gt;Connect to GitHub&lt;/strong&gt;). You’ll land on the &lt;strong&gt;Connect to GitHub&lt;/strong&gt; page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;GitHub connection settings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connection name&lt;/strong&gt; — Confirm or set the name (e.g. &lt;code&gt;github&lt;/code&gt; to match your Terraform &lt;code&gt;name&lt;/code&gt;). This is how the connection appears in the Console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App Installation (optional)&lt;/strong&gt; — Two choices:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leave blank&lt;/strong&gt; to connect as a &lt;strong&gt;GitHub user&lt;/strong&gt;. That user must have (at least read) access to the repos you use in Argo CD. Fine for personal or single-account repos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use a GitHub App&lt;/strong&gt; (recommended for org repos): e.g. choose &lt;strong&gt;AWS Connector for GitHub&lt;/strong&gt; via “Install a new app”, then select &lt;strong&gt;the org where your repos live&lt;/strong&gt; (e.g. &lt;code&gt;my-org&lt;/code&gt;) as the installation target.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Click the orange &lt;strong&gt;Connect&lt;/strong&gt; button to start the GitHub authorization flow (or complete it after installing the app).&lt;/li&gt;

&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;If you chose the GitHub App&lt;/strong&gt;, you’ll land on GitHub’s &lt;strong&gt;Install &amp;amp; Authorize AWS Connector for GitHub&lt;/strong&gt; page for that org. Choose &lt;strong&gt;Only select repositories&lt;/strong&gt;, click &lt;strong&gt;Select repositories&lt;/strong&gt;, and pick the repo(s) Argo CD will use (e.g. &lt;code&gt;my-org/my-repo&lt;/code&gt;). Review the permissions (read/write access to code, pull requests, etc.), then click &lt;strong&gt;Install &amp;amp; Authorize&lt;/strong&gt;. You’ll be redirected to &lt;code&gt;https://redirect.codestar.aws/return&lt;/code&gt;. Back on the AWS &lt;strong&gt;Connect to GitHub&lt;/strong&gt; page, the &lt;strong&gt;App Installation&lt;/strong&gt; field will show your installation (e.g. an installation ID such as &lt;code&gt;123456789&lt;/code&gt;—yours will differ). Confirm &lt;strong&gt;Connection name&lt;/strong&gt; (e.g. &lt;code&gt;github&lt;/code&gt;), then click the orange &lt;strong&gt;Connect&lt;/strong&gt; button. The connection status should become &lt;strong&gt;Available&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you connected as a GitHub user&lt;/strong&gt;, complete the OAuth prompt, then return to the Console and confirm status is &lt;strong&gt;Available&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  5. Test the connection
&lt;/h3&gt;

&lt;p&gt;Once the connection is &lt;strong&gt;Available&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Register the repo in Argo CD&lt;/strong&gt; (one-time). Create a repository Secret so the repo appears under Settings → Repositories. Use your real &lt;strong&gt;region&lt;/strong&gt;, &lt;strong&gt;account ID&lt;/strong&gt;, &lt;strong&gt;connection UUID&lt;/strong&gt;, and &lt;strong&gt;org/repo&lt;/strong&gt;—do not leave literal &lt;code&gt;OWNER/REPO&lt;/code&gt; in the URL or you’ll get “repository not found”. Example (replace the values if yours differ):
&lt;/li&gt;
&lt;/ol&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;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ap-southeast-2
   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sts get-caller-identity &lt;span class="nt"&gt;--query&lt;/span&gt; Account &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;
   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CONN_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;a1b2c3d4-e5f6-7890-abcd-ef1234567890   &lt;span class="c"&gt;# your connection UUID from Console or Terraform&lt;/span&gt;
   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OWNER_REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-org/my-repo                     &lt;span class="c"&gt;# your org and repo&lt;/span&gt;
   kubectl create secret generic argocd-repo-github &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://codeconnections.&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.amazonaws.com/git-http/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONN_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER_REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.git"&lt;/span&gt;
   kubectl label secret argocd-repo-github &lt;span class="nt"&gt;-n&lt;/span&gt; argocd argocd.argoproj.io/secret-type&lt;span class="o"&gt;=&lt;/span&gt;repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the secret already exists with the wrong URL, delete it first: &lt;code&gt;kubectl delete secret argocd-repo-github -n argocd&lt;/code&gt;, then run the &lt;code&gt;create&lt;/code&gt; and &lt;code&gt;label&lt;/code&gt; commands above.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In &lt;strong&gt;Argo CD&lt;/strong&gt; → &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Repositories&lt;/strong&gt;, click &lt;strong&gt;REFRESH LIST&lt;/strong&gt;. The repo should show with a green check (Successful). If it shows Failed, see the Troubleshooting section.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create an Application&lt;/strong&gt; that uses this repo: set &lt;code&gt;source.repoURL&lt;/code&gt; to the same CodeConnections URL and a path/targetRevision. Sync the application and confirm it syncs without errors.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  6. Using the repo URL in Argo CD
&lt;/h3&gt;

&lt;p&gt;CodeConnections URL format (replace placeholders):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://codeconnections.&amp;lt;region&amp;gt;.amazonaws.com/git-http/&amp;lt;account-id&amp;gt;/&amp;lt;region&amp;gt;/&amp;lt;connection-id&amp;gt;/OWNER/REPO.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;connection-id:&lt;/strong&gt; Must be the &lt;strong&gt;connection ID (UUID only)&lt;/strong&gt;, e.g. &lt;code&gt;9b6c3274-31da-4d1d-8955-e7d5cd9d15e2&lt;/code&gt;. Do &lt;strong&gt;not&lt;/strong&gt; put the full ARN in the URL path—that causes HTTP 400. In Terraform the AWS provider’s &lt;code&gt;aws_codestarconnections_connection.github.id&lt;/code&gt; is the ARN; use the UUID (the segment after the last &lt;code&gt;/&lt;/code&gt; in the ARN) or an output from your module that exposes the UUID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OWNER/REPO:&lt;/strong&gt; Your Git org and repo (e.g. &lt;code&gt;my-org/my-repo&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In your Argo CD Application, set &lt;code&gt;source.repoURL&lt;/code&gt; to that URL. One connection works for many repos—same connection ID, change only owner/repo.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;Terraform:&lt;/strong&gt; Connection + IAM policy (role name from your EKS setup):&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_codestarconnections_connection"&lt;/span&gt; &lt;span class="s2"&gt;"github"&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;"github"&lt;/span&gt;
  &lt;span class="nx"&gt;provider_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GitHub"&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_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"argocd_codeconnections"&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;"argocd-codeconnections"&lt;/span&gt;
  &lt;span class="nx"&gt;role&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;argocd_capability_role_name&lt;/span&gt;  &lt;span class="c1"&gt;# your Argo CD capability role name&lt;/span&gt;
  &lt;span class="nx"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&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;"2012-10-17"&lt;/span&gt;
    &lt;span class="nx"&gt;Statement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="nx"&gt;Effect&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
      &lt;span class="nx"&gt;Action&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"codeconnections:UseConnection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"codeconnections:GetConnection"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_codestarconnections_connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&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;p&gt;&lt;strong&gt;Console:&lt;/strong&gt; Developer Tools → Connections → select connection → Update pending connection → on the Connect to GitHub page set/confirm Connection name, then either leave App Installation blank (user connection) or install &lt;strong&gt;AWS Connector for GitHub&lt;/strong&gt; and select the org that owns your repos (e.g. my-org) → click &lt;strong&gt;Connect&lt;/strong&gt; → complete GitHub auth → status &lt;strong&gt;Available&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Argo CD:&lt;/strong&gt; Use the CodeConnections URL with the connection ID and your &lt;code&gt;OWNER/REPO&lt;/code&gt; as &lt;code&gt;source.repoURL&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. 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 check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Connection stays &lt;strong&gt;Pending&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Complete the connection in the AWS Console (authorize in GitHub or install the app). Check region.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;HTTP 400&lt;/strong&gt; when Argo CD tests repo&lt;/td&gt;
&lt;td&gt;The repo URL path must use the &lt;strong&gt;connection ID (UUID only)&lt;/strong&gt;, not the full ARN. If your URL contains &lt;code&gt;arn:aws:codestar-connections:&lt;/code&gt; in the path, fix the URL to use only the UUID (e.g. from Terraform use a value that outputs the UUID, not &lt;code&gt;connection.id&lt;/code&gt; which is the ARN).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;repository not found&lt;/strong&gt; / &lt;strong&gt;ProviderResourceNotFoundException&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;The URL still has literal &lt;code&gt;OWNER/REPO&lt;/code&gt; instead of your real org and repo (e.g. &lt;code&gt;my-org/my-repo&lt;/code&gt;). Delete the repo Secret and recreate it with the correct URL (see §5).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;authorization failed: Write access to repository not granted&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The GitHub identity (user or app) used by the connection doesn’t have access to that repo. Install the &lt;strong&gt;GitHub App&lt;/strong&gt; (e.g. AWS Connector for GitHub) on the &lt;strong&gt;org that owns the repo&lt;/strong&gt; (e.g. my-org), or ensure the GitHub user that authorized has read access to the repo. Then re-authorize the connection if needed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Argo CD cannot fetch repo&lt;/td&gt;
&lt;td&gt;Argo CD role has &lt;code&gt;codeconnections:UseConnection&lt;/code&gt; and &lt;code&gt;codeconnections:GetConnection&lt;/code&gt; on the connection ARN; connection status &lt;strong&gt;Available&lt;/strong&gt;; &lt;code&gt;source.repoURL&lt;/code&gt; uses the UUID as connection-id and correct owner/repo.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong region&lt;/td&gt;
&lt;td&gt;Connection is regional. Console and Terraform region must match.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/argocd-configure-repositories.html" rel="noopener noreferrer"&gt;Configure repository access (Argo CD, CodeConnections)&lt;/a&gt; – AWS EKS User Guide&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/capabilities.html" rel="noopener noreferrer"&gt;EKS Capabilities&lt;/a&gt; – Argo CD, ACK, KRO&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>argocd</category>
      <category>codeconnections</category>
      <category>eks</category>
      <category>terraform</category>
    </item>
  </channel>
</rss>
