feat: add required field to identities for multi-account Terraform components#2180
feat: add required field to identities for multi-account Terraform components#2180
Conversation
…nents via auth.needs Add `auth.needs` field to component auth config, enabling components to declare which identities they require. All listed identities are authenticated before Terraform runs, with their profiles written to the shared credentials file. The first identity becomes primary (sets AWS_PROFILE). This fixes CI failures when using multiple AWS provider aliases (e.g., hub-spoke networking) with OIDC, where only one profile was previously written to the credentials file. Changes: - Add Needs field to AuthConfig schema with comprehensive documentation - Update resolveTargetIdentityName() to prioritize auth.needs first entry as primary - Add authenticateAdditionalIdentities() for non-primary identity authentication with non-fatal error handling - Call authenticateAdditionalIdentities() from authenticateAndWriteEnv() after primary auth succeeds - Add 7 test cases covering needs list resolution, CLI override, fallback, success auth, skipping primary, non-fatal errors, and empty needs Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Dependency Review✅ No vulnerabilities or license issues found.Scanned FilesNone |
Add the `auth` definition to the Atmos stack manifest JSON schema, including the new `needs` field, along with `realm`, `providers`, `identities`, and `integrations`. Reference it from `terraform_component_manifest` so IDE autocompletion and schema validation recognize the component-level auth config. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Warning Release Documentation RequiredThis PR is labeled
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughImplements required identity authentication for multi-account Terraform scenarios. Adds logic to automatically authenticate multiple identities marked as required, extends schema definitions to support required and additional auth properties, and provides comprehensive documentation and tests for the new capability. Changes
Sequence DiagramsequenceDiagram
participant CLI as CLI Hook
participant AuthMgr as AuthManager
participant Primary as Primary Identity
participant Secondary as Required Identities
participant Env as Environment
CLI->>AuthMgr: Authenticate(default or --identity)
AuthMgr->>Primary: Authenticate primary
Primary-->>AuthMgr: Credentials
AuthMgr->>Env: Set primary credentials
CLI->>AuthMgr: AuthenticateAdditionalIdentities()
loop For each required identity
AuthMgr->>Secondary: Authenticate identity
alt Success
Secondary-->>AuthMgr: Credentials
AuthMgr->>Env: Write to shared credentials
else Failure
Secondary-->>AuthMgr: Error
AuthMgr->>AuthMgr: Log warning (non-fatal)
end
end
Env-->>CLI: All identities available
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Update all three copies of the manifest schema to include auth.needs: - pkg/datafetcher/schema (source, already had auth definition) - website/static/schemas (published, add needs/realm/integrations to component_auth) - tests/fixtures/schemas (test fixture, same as website) Add schema validation tests: - TestManifestSchema_AuthDefinitionExists: verify auth definition in embedded schema - TestManifestSchema_AuthNeedsField: verify needs is array of strings - TestManifestSchema_ValidAuthConfig: validate realistic auth configs against schema - TestAuthConfig_Needs: struct-level tests for Needs field - TestAuthConfig_NeedsWithMapstructure: verify Needs works with full AuthConfig Also changed component_auth additionalProperties from false to true to allow fields like logs, keyring, and realm that exist in the Go struct. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Warning Release Documentation RequiredThis PR is labeled
|
Add announcement blog post for the auth.needs feature enabling concurrent identity authentication for multi-account Terraform components. Update the roadmap with a shipped milestone and add a dedicated documentation page at /cli/configuration/auth/needs with configuration examples and behavior reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2180 +/- ##
==========================================
- Coverage 77.29% 77.28% -0.02%
==========================================
Files 960 960
Lines 91088 91096 +8
==========================================
- Hits 70410 70407 -3
- Misses 16593 16605 +12
+ Partials 4085 4084 -1
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
pkg/schema/schema_auth_test.go (1)
175-195: Test name slightly misleading.
TestAuthConfig_NeedsWithMapstructureconstructs the struct directly rather than usingmapstructure.Decode. The test is valid for verifying field assignment, but consider renaming toTestAuthConfig_NeedsWithFullConfigfor clarity.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/schema/schema_auth_test.go` around lines 175 - 195, Rename the test function TestAuthConfig_NeedsWithMapstructure to a clearer name like TestAuthConfig_NeedsWithFullConfig since the test constructs the AuthConfig struct directly (no mapstructure.Decode); update the function declaration and any references to the test symbol so the test suite continues to run and the name accurately reflects that it verifies a fully populated AuthConfig (reference symbol: TestAuthConfig_NeedsWithMapstructure → TestAuthConfig_NeedsWithFullConfig).pkg/datafetcher/schema_auth_validation_test.go (1)
35-47: Unchecked type assertions could cause test panics.Several type assertions (lines 35, 39, 47) don't use the comma-ok idiom. If the schema structure changes, tests will panic rather than fail gracefully with a helpful message.
Consider using
require.IsTypeor the comma-ok pattern for safer assertions.🔧 Example fix for safer type assertions
- oneOf := tfManifest["oneOf"].([]interface{}) + oneOfRaw, ok := tfManifest["oneOf"].([]interface{}) + require.True(t, ok, "terraform_component_manifest should have oneOf as array") + oneOf := oneOfRaw // Find the object variant (not the !include string variant). var objectVariant map[string]interface{} for _, v := range oneOf { - variant := v.(map[string]interface{}) + variant, ok := v.(map[string]interface{}) + require.True(t, ok, "oneOf entry should be a map") if variant["type"] == "object" {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/datafetcher/schema_auth_validation_test.go` around lines 35 - 47, The test uses unchecked type assertions on tfManifest["oneOf"], each element in oneOf, and objectVariant["properties"] which can cause panics; update the code around tfManifest, oneOf and objectVariant to use safe checks (comma-ok) or require.IsType/require.IsNotNil before casting: assert tfManifest["oneOf"] is a []interface{}, iterate with type-checked assertions for each element to ensure it's a map[string]interface{} when checking variant["type"], confirm objectVariant is not nil before accessing objectVariant["properties"], and ensure properties is a map[string]interface{} via a safe cast so failures produce test errors instead of panics.pkg/auth/hooks.go (1)
91-116: Resolution logic is correct with clear precedence.CLI flag → auth.needs[0] → default identity. The implementation matches the documented behavior.
One observation:
decodeAuthConfigFromStackis called multiple times during the flow (TerraformPreHook, resolveTargetIdentityName, authenticateAdditionalIdentities). Consider passing the decoded config through to avoid redundant decoding, though the current approach works.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/auth/hooks.go` around lines 91 - 116, resolveTargetIdentityName currently calls decodeAuthConfigFromStack again causing redundant decoding; change its signature to accept a pre-decoded authConfig (type returned by decodeAuthConfigFromStack) or a pointer to it and update callers (TerraformPreHook, authenticateAdditionalIdentities) to decode once and pass the result through, then use authConfig.Needs[0] inside resolveTargetIdentityName instead of calling decodeAuthConfigFromStack; ensure nil/empty checks remain and preserve existing error handling around GetDefaultIdentity in resolveTargetIdentityName.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pkg/datafetcher/schema/atmos/manifest/1.0.json`:
- Around line 313-315: The manifest's inline "auth" schema is too
permissive—replace it with the shared, structured auth definitions used by the
other manifest copies so "providers" and "identities" validate consistently;
specifically, update the "auth" entry to reference the common auth definitions
(use the same definitions/auth, definitions/provider, and definitions/identity
shapes used elsewhere) rather than allowing free-form objects, and apply the
same change to the other inline blocks noted (around 1301-1343) so all copies
share the identical auth schema.
In `@website/static/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json`:
- Around line 1573-1597: The component auth section currently routes
"identities" to the existing "#/definitions/auth_identities", whose key pattern
forbids "/" and thus rejects slash-delimited names like "core/network" and
"spoke/production"; update the schema so component auth accepts these names by
widening the key pattern to allow "/" (e.g., modify the regex in
"auth_identities" or create a new definition for component-level identities and
reference it here) and apply the same pattern change to the fixture copy so the
examples validate.
---
Nitpick comments:
In `@pkg/auth/hooks.go`:
- Around line 91-116: resolveTargetIdentityName currently calls
decodeAuthConfigFromStack again causing redundant decoding; change its signature
to accept a pre-decoded authConfig (type returned by decodeAuthConfigFromStack)
or a pointer to it and update callers (TerraformPreHook,
authenticateAdditionalIdentities) to decode once and pass the result through,
then use authConfig.Needs[0] inside resolveTargetIdentityName instead of calling
decodeAuthConfigFromStack; ensure nil/empty checks remain and preserve existing
error handling around GetDefaultIdentity in resolveTargetIdentityName.
In `@pkg/datafetcher/schema_auth_validation_test.go`:
- Around line 35-47: The test uses unchecked type assertions on
tfManifest["oneOf"], each element in oneOf, and objectVariant["properties"]
which can cause panics; update the code around tfManifest, oneOf and
objectVariant to use safe checks (comma-ok) or require.IsType/require.IsNotNil
before casting: assert tfManifest["oneOf"] is a []interface{}, iterate with
type-checked assertions for each element to ensure it's a map[string]interface{}
when checking variant["type"], confirm objectVariant is not nil before accessing
objectVariant["properties"], and ensure properties is a map[string]interface{}
via a safe cast so failures produce test errors instead of panics.
In `@pkg/schema/schema_auth_test.go`:
- Around line 175-195: Rename the test function
TestAuthConfig_NeedsWithMapstructure to a clearer name like
TestAuthConfig_NeedsWithFullConfig since the test constructs the AuthConfig
struct directly (no mapstructure.Decode); update the function declaration and
any references to the test symbol so the test suite continues to run and the
name accurately reflects that it verifies a fully populated AuthConfig
(reference symbol: TestAuthConfig_NeedsWithMapstructure →
TestAuthConfig_NeedsWithFullConfig).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7b16bfd9-f4c9-4cc9-9c01-f109d2030b4e
📒 Files selected for processing (14)
pkg/auth/hooks.gopkg/auth/hooks_needs_test.gopkg/datafetcher/schema/atmos/manifest/1.0.jsonpkg/datafetcher/schema_auth_validation_test.gopkg/schema/schema_auth.gopkg/schema/schema_auth_test.gotests/fixtures/schemas/atmos/atmos-manifest/1.0/atmos-manifest.jsonwebsite/blog/2026-03-12-auth-needs-concurrent-identities.mdxwebsite/docs/cli/configuration/auth/identities.mdxwebsite/docs/cli/configuration/auth/keyring.mdxwebsite/docs/cli/configuration/auth/logs.mdxwebsite/docs/cli/configuration/auth/needs.mdxwebsite/src/data/roadmap.jswebsite/static/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json
… slash-delimited identity names The embedded manifest schema had a loose auth definition with unstructured providers/identities, while the website and fixture schemas used rich structured definitions. This aligns all three copies and widens the identity/provider key pattern to accept slash-delimited names like core/network. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (2)
pkg/datafetcher/schema_auth_validation_test.go (1)
35-49: Consider defensive type assertions for clearer failure messages.The type assertions on lines 35, 39, and 47 could panic with opaque messages if the schema structure changes unexpectedly. While panics still fail tests, using the two-value assertion form produces more actionable output.
That said, this is a minor nit for test code where panics are acceptable.
♻️ Optional: Defensive assertions
- oneOf := tfManifest["oneOf"].([]interface{}) + oneOfRaw, ok := tfManifest["oneOf"] + require.True(t, ok, "terraform_component_manifest should have oneOf") + oneOf, ok := oneOfRaw.([]interface{}) + require.True(t, ok, "oneOf should be an array")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/datafetcher/schema_auth_validation_test.go` around lines 35 - 49, The test uses single-value type assertions that can panic (oneOf := tfManifest["oneOf"].([]interface{}), variant := v.(map[string]interface{}), props := objectVariant["properties"].(map[string]interface{})); change these to the two-value form and assert/require the ok value before use: e.g., get oneOfRaw, ok := tfManifest["oneOf"]; require.True(t, ok) and oneOf, ok := oneOfRaw.([]interface{}); require.True(t, ok), and likewise for variant and props so objectVariant and props are only accessed after confirming their types in the code paths around oneOf, objectVariant and props.pkg/datafetcher/schema/atmos/manifest/1.0.json (1)
375-411: Consider addingauthproperty tohelmfile_component_manifestandpacker_component_manifestfor consistency.The website and fixture schemas include
authin these manifests, but the embedded schema doesn't. This gap looks intentional given the PR's Terraform focus, but aligning them would keep things consistent across all three schema sources.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/datafetcher/schema/atmos/manifest/1.0.json` around lines 375 - 411, Add an "auth" property to the "helmfile_component_manifest" and "packer_component_manifest" object schemas: inside each manifest's "properties" object add an "auth" entry with "$ref": "#/definitions/auth" so the embedded schema matches the website and fixture schemas; keep "additionalProperties": false and do not add "auth" to "required" unless required by other schemas.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@pkg/datafetcher/schema_auth_validation_test.go`:
- Around line 35-49: The test uses single-value type assertions that can panic
(oneOf := tfManifest["oneOf"].([]interface{}), variant :=
v.(map[string]interface{}), props :=
objectVariant["properties"].(map[string]interface{})); change these to the
two-value form and assert/require the ok value before use: e.g., get oneOfRaw,
ok := tfManifest["oneOf"]; require.True(t, ok) and oneOf, ok :=
oneOfRaw.([]interface{}); require.True(t, ok), and likewise for variant and
props so objectVariant and props are only accessed after confirming their types
in the code paths around oneOf, objectVariant and props.
In `@pkg/datafetcher/schema/atmos/manifest/1.0.json`:
- Around line 375-411: Add an "auth" property to the
"helmfile_component_manifest" and "packer_component_manifest" object schemas:
inside each manifest's "properties" object add an "auth" entry with "$ref":
"#/definitions/auth" so the embedded schema matches the website and fixture
schemas; keep "additionalProperties": false and do not add "auth" to "required"
unless required by other schemas.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fdfcc570-0d56-4c4d-9c51-244c9f97803c
📒 Files selected for processing (4)
pkg/datafetcher/schema/atmos/manifest/1.0.jsonpkg/datafetcher/schema_auth_validation_test.gotests/fixtures/schemas/atmos/atmos-manifest/1.0/atmos-manifest.jsonwebsite/static/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json
Previously, the first entry in auth.needs became the primary identity, requiring users to re-list the default. Now the default identity is always primary, and needs only lists additional identities. If no default exists, the first needs entry becomes the primary as a fallback. Precedence: --identity CLI flag > default identity > needs[0] > error Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pkg/auth/hooks.go (1)
129-139:⚠️ Potential issue | 🟠 MajorSnapshot the primary env before secondary auth runs.
AuthenticatemutatesstackInfo.ComponentEnvSectionduring post-auth. With this new ordering,PrepareShellEnvironmentnow starts from the last extra identity's env instead of the primary one. CapturebaseEnvListbeforeauthenticateAdditionalIdentities, or restore the primary env after the loop.Patch sketch
log.Debug("Authentication successful", "identity", whoami.Identity, "expiration", whoami.Expiration) + // Convert ComponentEnvSection to env list for PrepareShellEnvironment. + // This includes any component-specific env vars already set in the stack config. + baseEnvList := componentEnvSectionToList(stackInfo.ComponentEnvSection) + // Authenticate additional needed identities so their profiles exist in the shared credentials file. // This is required for Terraform components that use multiple AWS provider aliases. authenticateAdditionalIdentities(ctx, authManager, identityName, stackInfo) - - // Convert ComponentEnvSection to env list for PrepareShellEnvironment. - // This includes any component-specific env vars already set in the stack config. - baseEnvList := componentEnvSectionToList(stackInfo.ComponentEnvSection)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/auth/hooks.go` around lines 129 - 139, The code builds baseEnvList from stackInfo.ComponentEnvSection after calling authenticateAdditionalIdentities, but authenticateAdditionalIdentities mutates stackInfo.ComponentEnvSection so PrepareShellEnvironment ends up using a secondary identity's env; to fix, snapshot the primary env before calling authenticateAdditionalIdentities by moving the call to componentEnvSectionToList(stackInfo.ComponentEnvSection) above authenticateAdditionalIdentities (or reassign baseEnvList from a saved copy after the loop), ensuring authManager.PrepareShellEnvironment(ctx, identityName, baseEnvList) receives the original primary env; update references to authenticateAdditionalIdentities, componentEnvSectionToList, stackInfo.ComponentEnvSection, and authManager.PrepareShellEnvironment accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pkg/auth/hooks.go`:
- Around line 166-183: The loop in authenticateAdditionalIdentities calls
authManager.Authenticate for each identity which causes Azure's WriteCredentials
to truncate credentials.json so only the last identity remains; fix by changing
the Azure file manager's credential write logic (the method invoked by
SetupFiles()/WriteCredentials()) to read the existing credentials.json, merge in
the new identity's credentials keyed by identity/client-id (preserve existing
entries), and then write the merged JSON atomically, so per-identity calls from
authenticateAdditionalIdentities and authManager.Authenticate no longer
overwrite previous Azure identities.
- Around line 99-113: GetDefaultIdentity(false) is currently treating any
non-nil error as equivalent to "no default" and falls back to
authConfig.Needs[0]; change this to discriminate sentinel errors using
errors.Is(err, errUtils.ErrNoDefaultIdentity) so only ErrNoDefaultIdentity
triggers the fallback path, and return/bubble up any other errors (e.g.,
ErrIdentitySelectionRequiresTTY, ErrUserAborted) immediately from the function;
locate the call to authManager.GetDefaultIdentity(false) and replace the simple
nil-check with an errors.Is check, keeping the existing
decodeAuthConfigFromStack and authConfig.Needs fallback only for the
ErrNoDefaultIdentity case.
---
Outside diff comments:
In `@pkg/auth/hooks.go`:
- Around line 129-139: The code builds baseEnvList from
stackInfo.ComponentEnvSection after calling authenticateAdditionalIdentities,
but authenticateAdditionalIdentities mutates stackInfo.ComponentEnvSection so
PrepareShellEnvironment ends up using a secondary identity's env; to fix,
snapshot the primary env before calling authenticateAdditionalIdentities by
moving the call to componentEnvSectionToList(stackInfo.ComponentEnvSection)
above authenticateAdditionalIdentities (or reassign baseEnvList from a saved
copy after the loop), ensuring authManager.PrepareShellEnvironment(ctx,
identityName, baseEnvList) receives the original primary env; update references
to authenticateAdditionalIdentities, componentEnvSectionToList,
stackInfo.ComponentEnvSection, and authManager.PrepareShellEnvironment
accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 695ad31e-466a-4c30-89c4-ae88eb3261c8
📒 Files selected for processing (6)
pkg/auth/hooks.gopkg/auth/hooks_needs_test.gopkg/schema/schema_auth.gowebsite/blog/2026-03-12-auth-needs-concurrent-identities.mdxwebsite/docs/cli/configuration/auth/needs.mdxwebsite/src/data/roadmap.js
🚧 Files skipped from review as they are similar to previous changes (4)
- pkg/auth/hooks_needs_test.go
- website/src/data/roadmap.js
- website/blog/2026-03-12-auth-needs-concurrent-identities.mdx
- pkg/schema/schema_auth.go
| func authenticateAdditionalIdentities(ctx context.Context, authManager types.AuthManager, | ||
| primaryIdentity string, stackInfo *schema.ConfigAndStacksInfo, | ||
| ) { | ||
| authConfig, err := decodeAuthConfigFromStack(stackInfo) | ||
| if err != nil || len(authConfig.Needs) == 0 { | ||
| return | ||
| } | ||
| for _, identity := range authConfig.Needs { | ||
| if strings.EqualFold(identity, primaryIdentity) { | ||
| continue | ||
| } | ||
| log.Debug("Authenticating additional needed identity", identityKey, identity) | ||
| if _, err := authManager.Authenticate(ctx, identity); err != nil { | ||
| log.Warn("Failed to authenticate additional identity (non-fatal)", | ||
| identityKey, identity, "error", err) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C20 'func .*SetupFiles\(' pkg/auth
rg -n -C4 '\bSetupFiles\(' pkg/auth
rg -n -C4 'WriteFile|os\.Create|os\.OpenFile|Truncate|Remove' pkg/authRepository: cloudposse/atmos
Length of output: 50375
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# AWS file manager WriteCredentials implementation
rg -n -A30 'func.*WriteCredentials.*aws' pkg/auth/cloud/aws/
# GCP file writing in SetupFiles
rg -n -A50 'func SetupFiles' pkg/auth/cloud/gcp/setup.go | head -100
# Check for any read-modify-write pattern
rg -n 'ioutil\.ReadFile|os\.ReadFile|WriteFile.*Append|OpenFile.*Append' pkg/auth/cloud/Repository: cloudposse/atmos
Length of output: 42
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find AWS file manager implementation
fd -t f 'file_manager' pkg/auth/cloud/aws/ -x cat {}
# Look for WriteCredentials in AWS
rg -n 'WriteCredentials' pkg/auth/cloud/aws/
# Check GCP setup.go for file writing patterns
sed -n '32,150p' pkg/auth/cloud/gcp/setup.goRepository: cloudposse/atmos
Length of output: 7003
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find AWS WriteCredentials implementation
sed -n '184,280p' pkg/auth/cloud/aws/files.goRepository: cloudposse/atmos
Length of output: 3811
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check Azure WriteCredentials
rg -n -A40 'func.*WriteCredentials.*azure' pkg/auth/cloud/azure/
# Check GCP WriteADCFile to see if files are per-identity or shared
rg -n -A20 'func WriteADCFile' pkg/auth/cloud/gcp/Repository: cloudposse/atmos
Length of output: 42
🏁 Script executed:
#!/bin/bash
set -euoripefail
# Find all files in cloud/azure
fd . pkg/auth/cloud/azure/ -t f
# Find all files in cloud/gcp
fd . pkg/auth/cloud/gcp/ -t f
# Look for file writing in azure setup
cat pkg/auth/cloud/azure/setup.go
# Look for WriteADCFile in gcp
rg -n 'WriteADCFile' pkg/auth/cloud/gcp/Repository: cloudposse/atmos
Length of output: 734
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find all files in cloud/azure
fd . pkg/auth/cloud/azure/ -t f
# Find all files in cloud/gcp
fd . pkg/auth/cloud/gcp/ -t fRepository: cloudposse/atmos
Length of output: 706
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Look at Azure file writing
cat pkg/auth/cloud/azure/files.go
# Look at GCP file writing
cat pkg/auth/cloud/gcp/files.goRepository: cloudposse/atmos
Length of output: 20561
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Look at authenticateAdditionalIdentities and see how identities are authenticated
sed -n '166,183p' pkg/auth/hooks.go
# Check the Authenticate method signature to understand what it does
rg -n -B5 -A15 'func.*Authenticate\(ctx' pkg/auth/ | head -100Repository: cloudposse/atmos
Length of output: 7949
Azure credentials get overwritten by the loop—only the last identity persists.
The loop calls Authenticate() for each additional identity, which eventually calls SetupFiles() and triggers file writes. AWS merges profiles into a single INI file per provider, and GCP isolates identities into separate directories. Azure, however, truncates its credentials.json on each WriteCredentials() call, leaving only the final identity. The auth.needs feature won't work reliably for Azure unless each identity's credentials are stored or the Azure file manager adopts a merge strategy.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/auth/hooks.go` around lines 166 - 183, The loop in
authenticateAdditionalIdentities calls authManager.Authenticate for each
identity which causes Azure's WriteCredentials to truncate credentials.json so
only the last identity remains; fix by changing the Azure file manager's
credential write logic (the method invoked by SetupFiles()/WriteCredentials())
to read the existing credentials.json, merge in the new identity's credentials
keyed by identity/client-id (preserve existing entries), and then write the
merged JSON atomically, so per-identity calls from
authenticateAdditionalIdentities and authManager.Authenticate no longer
overwrite previous Azure identities.
GetDefaultIdentity can return multiple error types beyond ErrNoDefaultIdentity (e.g., ErrUserAborted, ErrIdentitySelectionRequiresTTY). Previously all errors fell through to the auth.needs fallback, masking real failures. Now only ErrNoDefaultIdentity triggers the fallback; other errors are returned immediately. Also properly surfaces decodeAuthConfigFromStack errors instead of silently ignoring them, and documents the Azure credential overwrite limitation for multi-identity scenarios. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
pkg/auth/hooks.go (1)
182-185: Consider logging decode failures for debugging.If
decodeAuthConfigFromStackfails here, the function silently returns. While non-fatal behavior is intentional, a trace-level log would help operators troubleshoot configuration issues without breaking the flow.🔧 Optional: Add trace log on decode failure
func authenticateAdditionalIdentities(ctx context.Context, authManager types.AuthManager, primaryIdentity string, stackInfo *schema.ConfigAndStacksInfo, ) { authConfig, err := decodeAuthConfigFromStack(stackInfo) - if err != nil || len(authConfig.Needs) == 0 { + if err != nil { + log.Trace("Failed to decode auth config for additional identities", "error", err) + return + } + if len(authConfig.Needs) == 0 { return }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/auth/hooks.go` around lines 182 - 185, The function currently returns silently when decodeAuthConfigFromStack fails; add a trace-level log when err != nil to record the decode error and relevant context (e.g., the stack info or identifier) without changing control flow. Locate the call to decodeAuthConfigFromStack and, before the early return, emit a trace/debug log including err and enough stackInfo to diagnose the failure; keep behavior unchanged when authConfig is present but authConfig.Needs is empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@pkg/auth/hooks.go`:
- Around line 182-185: The function currently returns silently when
decodeAuthConfigFromStack fails; add a trace-level log when err != nil to record
the decode error and relevant context (e.g., the stack info or identifier)
without changing control flow. Locate the call to decodeAuthConfigFromStack and,
before the early return, emit a trace/debug log including err and enough
stackInfo to diagnose the failure; keep behavior unchanged when authConfig is
present but authConfig.Needs is empty.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a7550b68-b592-4a7e-ad9a-980ab0b682f6
📒 Files selected for processing (1)
pkg/auth/hooks.go
|
💥 This pull request now has conflicts. Could you fix it @osterman? 🙏 |
Move the multi-identity declaration from a centralized `auth.needs` string array on AuthConfig to a `required: true` boolean on each Identity. Required identities are automatically authenticated without prompting. The `required` field is orthogonal to `default`: default sets the primary identity, required means auto-authenticate. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve conflict in roadmap.js progress value, keeping 92% from this branch which includes auth.needs milestones. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
pkg/auth/hooks.go (1)
159-180:⚠️ Potential issue | 🟠 Major
required: trueis still broken for Azure multi-identity flows.This isn't just future work—the new loop now calls
Authenticate()for multiple identities, while Azure credentials are still keyed by provider name, so the last required Azure identity wins and earlier ones get overwritten. The TODO is useful, but the feature stays provider-dependent until you either gate this path for Azure or fix Azure's per-identity/merge strategy.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/auth/hooks.go` around lines 159 - 180, The loop in authenticateAdditionalIdentities incorrectly calls authManager.Authenticate for Azure identities which are keyed by provider name and thus overwrite earlier required identities; to fix, detect Azure identities (e.g., inspect identity.Provider / identity.Type or a method on types.AuthManager) and skip authenticating additional Azure identities in authenticateAdditionalIdentities (log a clear warning about the provider limitation), or alternatively implement a per-identity Azure storage/merge strategy inside the Azure-specific auth implementation; update authenticateAdditionalIdentities to only call authManager.Authenticate for non-Azure providers (and add the warning) unless you also change the Azure auth backend to support per-identity keys.
🧹 Nitpick comments (4)
pkg/schema/schema_auth_test.go (1)
128-183: These tests mostly just restate struct literals.
TestIdentity_Required,TestAuthConfig_WithRequiredIdentities, and the newRequiredassertion inTestIdentity_Structureonly read back bools assigned in the same test. A YAML/mapstructure/JSON round-trip would give you real regression coverage for the new field instead of compile-time tautologies.Based on learnings: Test Quality (MANDATORY): Test behavior, not implementation. Avoid tautological tests.
Also applies to: 216-240
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/schema/schema_auth_test.go` around lines 128 - 183, The tests TestIdentity_Required, TestAuthConfig_WithRequiredIdentities and the added Required assertion in TestIdentity_Structure are tautological because they only read back boolean literals; change them to perform a serialization/deserialization round-trip (e.g., marshal to YAML/JSON or build a map[string]interface{} and use mapstructure) and then unmarshal into Identity/AuthConfig to assert the Required and Default fields are preserved after parsing; update TestIdentity_Required to create input bytes (or map) with required:true/false/missing cases and assert the parsed Identity.Required matches expectations, and update TestAuthConfig_WithRequiredIdentities to parse a sample YAML/JSON auth config and assert identities' Required/Default values post-unmarshal so the tests cover mapping/decoding behavior rather than literal assignment.pkg/auth/hooks_required_test.go (1)
30-68: These resolve-target tests don't hit a new path.
resolveTargetIdentityNamenever inspectsGetIdentities(), so the required-identity setup here is dead data and the assertions mostly duplicateTestResolveTargetIdentityNameinpkg/auth/hooks_test.go. I'd keep this file focused onauthenticateAdditionalIdentitiesand fold/remove the duplicate precedence checks.Based on learnings: Test Quality (MANDATORY): Test behavior, not implementation. Avoid tautological tests.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/auth/hooks_required_test.go` around lines 30 - 68, The tests in hooks_required_test.go are exercising resolveTargetIdentityName but pass required identities that are unused because resolveTargetIdentityName never calls GetIdentities; remove or fold the duplicate precedence tests (those using mgr.identities with Required:true) and instead add/keep tests that directly exercise authenticateAdditionalIdentities and any behavior that reads GetIdentities; specifically, delete or merge tests referencing resolveTargetIdentityName that duplicate pkg/auth/hooks_test.go, and replace them with tests that call authenticateAdditionalIdentities (or update existing tests to assert behavior that depends on mgr.GetIdentities), ensuring you reference resolveTargetIdentityName only where it actually inspects input and use authenticateAdditionalIdentities for required-identity logic.pkg/datafetcher/schema_auth_validation_test.go (2)
100-165: Consider adding a negative test case.The
expectErrfield is present but unused (all cases arefalse). Adding a case with invalid config (e.g., identity missingkind) would strengthen the test suite.💡 Example negative test case
{ name: "identity missing required kind field", manifest: map[string]interface{}{ "components": map[string]interface{}{ "terraform": map[string]interface{}{ "bad": map[string]interface{}{ "auth": map[string]interface{}{ "identities": map[string]interface{}{ "missing-kind": map[string]interface{}{ "default": true, // "kind" intentionally omitted }, }, }, }, }, }, }, expectErr: true, },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/datafetcher/schema_auth_validation_test.go` around lines 100 - 165, Add a negative test case to the tests slice that sets expectErr: true and supplies a manifest with an identity missing the required "kind" (e.g., under "components" -> "terraform" -> "bad" -> "auth" -> "identities" include an entry with "default": true but no "kind"), and ensure the test loop that iterates over tests reads the expectErr flag and asserts an error is returned when expectErr is true (reference the tests slice, manifest maps, and the expectErr field in the test runner).
35-49: Type assertions without safety checks may cause cryptic panics.Lines 35, 39, 47 use direct type assertions (
.([]interface{}),.(map[string]interface{})) that will panic if the schema structure differs from expectations. Consider using the two-value form for clearer failure messages.♻️ Safer type assertion example
- oneOf := tfManifest["oneOf"].([]interface{}) + oneOf, ok := tfManifest["oneOf"].([]interface{}) + require.True(t, ok, "terraform_component_manifest should have oneOf array") // Find the object variant (not the !include string variant). var objectVariant map[string]interface{} for _, v := range oneOf { - variant := v.(map[string]interface{}) + variant, ok := v.(map[string]interface{}) + if !ok { + continue + } if variant["type"] == "object" { objectVariant = variant break } } require.NotNil(t, objectVariant, "terraform_component_manifest should have an object variant") - props := objectVariant["properties"].(map[string]interface{}) + props, ok := objectVariant["properties"].(map[string]interface{}) + require.True(t, ok, "object variant should have properties")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/datafetcher/schema_auth_validation_test.go` around lines 35 - 49, The test uses unchecked type assertions on tfManifest["oneOf"], loop variable variant, and objectVariant["properties"] which can panic; change them to the two-value "ok" form and fail the test with clear messages instead of panicking: safely extract oneOf using v, ok := tfManifest["oneOf"].([]interface{}) and require.True/require.NotNil on ok, inside the loop assert variant, ok := v.(map[string]interface{}) and skip or fail if not ok, and read props with props, ok := objectVariant["properties"].(map[string]interface{}) followed by require.True(t, ok, ...) so failures produce readable test errors referencing oneOf, variant, objectVariant and props rather than panics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pkg/datafetcher/schema/atmos/manifest/1.0.json`:
- Around line 1301-1331: The ComponentAuthConfig Go struct is missing the schema
fields "realm" and "integrations", so add a Realm string `Realm string
`json:"realm,omitempty"` to represent the credential isolation realm and add an
Integrations field (e.g., `Integrations map[string]interface{}
`json:"integrations,omitempty"` or `map[string]any`) to hold client-only
credential materializations; ensure both fields are exported, have the correct
json tags, and live alongside the existing Providers and Identities fields so
unmarshalling preserves these values.
In `@website/blog/2026-03-12-auth-required-concurrent-identities.mdx`:
- Around line 26-39: The sample aws/assume-role identities (auth.identities ->
core-network, plat-prod, plat-staging) are incomplete: update each
aws/assume-role entry to either include the required fields "via" and
"principal.assume_role" with realistic example values, or clearly mark the YAML
block as a partial override/snippet and link to the full required schema; ensure
the examples use the exact keys shown (kind: aws/assume-role, default, required,
via, principal.assume_role) so copy-paste consumers get valid config.
In `@website/docs/cli/configuration/auth/required.mdx`:
- Around line 28-42: The examples under the atmos.yaml auth.identities block
(identities: core-network, plat-prod, plat-staging with kind: aws/assume-role)
are missing required fields (via and principal.assume_role) per the
aws/assume-role schema; either update those identity examples to include valid
via and principal.assume_role entries (showing a complete aws/assume-role
identity) or clearly label the code blocks as "partial overrides" and add a
short note that these fragments only override default/required flags and rely on
pre-defined global identities for the full assume-role configuration; apply the
same change to the second snippet referenced (the other block covering
plat-prod/plat-staging) so docs are accurate and consistent with the identities
spec.
---
Duplicate comments:
In `@pkg/auth/hooks.go`:
- Around line 159-180: The loop in authenticateAdditionalIdentities incorrectly
calls authManager.Authenticate for Azure identities which are keyed by provider
name and thus overwrite earlier required identities; to fix, detect Azure
identities (e.g., inspect identity.Provider / identity.Type or a method on
types.AuthManager) and skip authenticating additional Azure identities in
authenticateAdditionalIdentities (log a clear warning about the provider
limitation), or alternatively implement a per-identity Azure storage/merge
strategy inside the Azure-specific auth implementation; update
authenticateAdditionalIdentities to only call authManager.Authenticate for
non-Azure providers (and add the warning) unless you also change the Azure auth
backend to support per-identity keys.
---
Nitpick comments:
In `@pkg/auth/hooks_required_test.go`:
- Around line 30-68: The tests in hooks_required_test.go are exercising
resolveTargetIdentityName but pass required identities that are unused because
resolveTargetIdentityName never calls GetIdentities; remove or fold the
duplicate precedence tests (those using mgr.identities with Required:true) and
instead add/keep tests that directly exercise authenticateAdditionalIdentities
and any behavior that reads GetIdentities; specifically, delete or merge tests
referencing resolveTargetIdentityName that duplicate pkg/auth/hooks_test.go, and
replace them with tests that call authenticateAdditionalIdentities (or update
existing tests to assert behavior that depends on mgr.GetIdentities), ensuring
you reference resolveTargetIdentityName only where it actually inspects input
and use authenticateAdditionalIdentities for required-identity logic.
In `@pkg/datafetcher/schema_auth_validation_test.go`:
- Around line 100-165: Add a negative test case to the tests slice that sets
expectErr: true and supplies a manifest with an identity missing the required
"kind" (e.g., under "components" -> "terraform" -> "bad" -> "auth" ->
"identities" include an entry with "default": true but no "kind"), and ensure
the test loop that iterates over tests reads the expectErr flag and asserts an
error is returned when expectErr is true (reference the tests slice, manifest
maps, and the expectErr field in the test runner).
- Around line 35-49: The test uses unchecked type assertions on
tfManifest["oneOf"], loop variable variant, and objectVariant["properties"]
which can panic; change them to the two-value "ok" form and fail the test with
clear messages instead of panicking: safely extract oneOf using v, ok :=
tfManifest["oneOf"].([]interface{}) and require.True/require.NotNil on ok,
inside the loop assert variant, ok := v.(map[string]interface{}) and skip or
fail if not ok, and read props with props, ok :=
objectVariant["properties"].(map[string]interface{}) followed by require.True(t,
ok, ...) so failures produce readable test errors referencing oneOf, variant,
objectVariant and props rather than panics.
In `@pkg/schema/schema_auth_test.go`:
- Around line 128-183: The tests TestIdentity_Required,
TestAuthConfig_WithRequiredIdentities and the added Required assertion in
TestIdentity_Structure are tautological because they only read back boolean
literals; change them to perform a serialization/deserialization round-trip
(e.g., marshal to YAML/JSON or build a map[string]interface{} and use
mapstructure) and then unmarshal into Identity/AuthConfig to assert the Required
and Default fields are preserved after parsing; update TestIdentity_Required to
create input bytes (or map) with required:true/false/missing cases and assert
the parsed Identity.Required matches expectations, and update
TestAuthConfig_WithRequiredIdentities to parse a sample YAML/JSON auth config
and assert identities' Required/Default values post-unmarshal so the tests cover
mapping/decoding behavior rather than literal assignment.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d849cfde-e23f-45a9-ac2f-ccf90b70bdac
📒 Files selected for processing (11)
pkg/auth/hooks.gopkg/auth/hooks_required_test.gopkg/auth/hooks_test.gopkg/datafetcher/schema/atmos/manifest/1.0.jsonpkg/datafetcher/schema_auth_validation_test.gopkg/schema/schema_auth.gopkg/schema/schema_auth_test.gowebsite/blog/2026-03-12-auth-required-concurrent-identities.mdxwebsite/docs/cli/configuration/auth/identities.mdxwebsite/docs/cli/configuration/auth/required.mdxwebsite/src/data/roadmap.js
Add Realm and Integrations fields to ComponentAuthConfig struct to match the JSON schema, preventing silent data loss during unmarshalling. Mark aws/assume-role examples in blog and docs as partial overrides with notes linking to full identity configuration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
what
required: trueboolean field to identity config for automatic authentication without promptingrequiredanddefaultare orthogonal:default: truesets the primary identity,required: truemeans auto-authenticaterequired: true— all are authenticated before Terraform runs--identityCLI flag >default: trueidentity > errorcomponent_auth,auth_providers,auth_identity, etc.)patternPropertiesregex to allow slash-delimited identity names (e.g.,core/network)why
When Terraform components use multiple AWS provider aliases for multi-account patterns (hub-spoke networking, cross-account peering), each provider assumes a different IAM role. In CI with OIDC, only the primary identity's profile existed in the shared credentials file, causing "failed to get shared config profile" errors for additional provider aliases.
Marking identities as
required: trueensures they are automatically authenticated — no prompting, no selection. This is cleaner than the previousauth.needsarray approach because the declaration lives on the identity itself rather than in a disconnected list.example
references
auth.needsarray to per-identityrequiredbooleanSummary by CodeRabbit
Release Notes
New Features
Documentation
Configuration
needs,realm, andintegrations