Skip to content

Add support for IAM policies (both managed and inline).#132

Open
ggreer wants to merge 1 commit into
mainfrom
ggreer/iam-policy
Open

Add support for IAM policies (both managed and inline).#132
ggreer wants to merge 1 commit into
mainfrom
ggreer/iam-policy

Conversation

@ggreer

@ggreer ggreer commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@ggreer ggreer requested a review from a team June 30, 2026 22:33
Comment thread pkg/connector/iam_policy.go Outdated
Comment thread pkg/connector/iam_policy.go Outdated
})
}

listPoliciesInput := &iam.ListPoliciesInput{}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: ListPoliciesInput{} leaves Scope at its default (All), so every sync also pulls the 1000+ AWS-managed policies. Combined with the principal-ARN reconstruction issue above (AWS-managed policy ARNs use account aws), these produce many broken grants and large syncs. If only customer-managed policies are intended, set Scope: types.PolicyScopeTypeLocal. (confidence: medium)

@github-actions

Copy link
Copy Markdown
Contributor

@-

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking issues found — see review comments.

Comment thread pkg/connector/iam_policy.go Outdated
})
}

listPoliciesInput := &iam.ListPoliciesInput{}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion (medium confidence): ListPolicies with no Scope/OnlyAttached defaults to Scope=All, OnlyAttached=false, which enumerates all ~1000+ AWS-managed policies per account. Each then drives a ListEntitiesForPolicy call in Grants (plus GetUser/GetRole/GetGroup per attached entity), creating many unattached policy resources and a large API/throttling load. Consider OnlyAttached: true (and/or Scope: types.PolicyScopeTypeLocal) to limit to policies actually in use.

Comment thread pkg/connector/iam_policy.go Outdated
Comment on lines +175 to +184
continue
}
userARN, err := iamEntityARN(ctx, iamClient, "user", userName)
if err != nil {
l.Warn("baton-aws: failed to resolve IAM user ARN for managed policy grant, skipping",
zap.String("user_name", userName),
zap.Error(err),
)
continue
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion (low confidence): entity-ARN resolution failures here are logged and skipped for all error types. A transient/throttling error (e.g. 429 on GetUser) would silently drop that principal's grant and under-report access. Consider propagating retryable errors (let the SDK retry) and only skipping on genuine not-found/permanent failures.

Comment thread pkg/connector/inline_policy.go Outdated
}

func (o *inlinePolicyResourceType) Grant(_ context.Context, _ *v2.Resource, _ *v2.Entitlement) ([]*v2.Grant, annotations.Annotations, error) {
return nil, nil, fmt.Errorf("baton-aws: inline policies cannot be created via provisioning")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: inline_policy advertises CAPABILITY_PROVISION, but Grant returns a plain fmt.Errorf. Returning status.Errorf(codes.Unimplemented, ...) (matching the gRPC-status convention used in Revoke) lets callers distinguish "unsupported" from a real failure.

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Connector PR Review: Add support for IAM policies (both managed and inline).

Blocking Issues: 1 | Suggestions: 3 | Threads Resolved: 0
Criteria: Criteria status: loaded .claude/skills/ci-review.md from trusted base 7d7551883228.
Review mode: full
View review run

Review Summary

Full PR diff scanned for security and correctness. This PR adds managed (iam_policy) and inline (inline_policy) IAM policy resource types, emits attached-managed-policy grants from the user/role/group Grants paths, and adds a sync-only-attached-policies config flag. The prior feedback about missing GrantExpandable on inline-policy grants now appears addressed (inline_policy.go Grants calls policyGrantExpansion for role/group parents). No new security issues found; the remaining items below were raised previously and are still open in the current diff.

Security Issues

None found.

Correctness Issues

  • pkg/connector/inline_policy.go:174-181 (previously raised, still open) — listInlinePolicyNames only handles NoSuchEntityException; on AccessDenied it returns the wrapped error and hard-fails the whole inline-policy List. This is inconsistent with getInlinePolicyDocument (line 236) and the managed-policy paths in iam_user.go/role.go/iam_group.go, which Warn+continue. Existing installs upgraded to this version without the newly required iam:ListRolePolicies/iam:ListGroupPolicies scopes will fail the sync instead of degrading gracefully (R7/B7).

Suggestions

  • pkg/connector/iam_policy.go:44-45 (previously raised, still open) — ListPolicies leaves Scope unset (defaults to All); with sync-only-attached-policies=false (the default) this enumerates ~1150 AWS-managed policies per account. Because iam_policy is a child of every account, the same AWS-managed ARN (arn:aws:iam::aws:policy/...) is emitted as a duplicate resource under each account parent. Consider Scope=Local for the managed-policy list, or documenting the volume/duplication tradeoff. The new sync-only-attached-policies flag partially mitigates volume.
  • docs/connector.mdx (previously raised, still open) — not updated for the newly required scopes (iam:ListPolicies, iam:GetPolicy, iam:ListAttachedRolePolicies, iam:ListAttachedGroupPolicies, iam:ListRolePolicies, iam:ListGroupPolicies, and the inline Delete*Policy provisioning scopes). Customers copying it will hit AccessDenied on the new sync/provision paths (D3).
  • No tests accompany the new code (previously raised, still open) — add coverage for inlinePolicyResourceID/parseInlinePolicyResourceID round-trip, iamRoleNameFromARN, policyGrantExpansion, and the listInlinePolicyNames parent-type switch.
Prompt for AI agents
Verify each finding against the current code and only fix it if needed.

## Correctness Issues

In `pkg/connector/inline_policy.go`:
- Around line 174-181: listInlinePolicyNames only treats NoSuchEntityException as skippable;
  every other error (including AccessDenied) is returned via wrapAWSError, which hard-fails the
  inline-policy List and thus the sync. Mirror the graceful degradation used in
  getInlinePolicyDocument (line 236) and the managed-policy grant paths: when
  isAccessDeniedError(listErr) is true, log a Warn (with parent ARN/type) and return an empty
  result with no error so the inline-policy sync degrades to empty instead of failing when
  iam:ListUserPolicies/iam:ListRolePolicies/iam:ListGroupPolicies is missing.

## Suggestions

In `pkg/connector/iam_policy.go`:
- Around line 44-45: ListPoliciesInput sets OnlyAttached but leaves Scope unset (defaults to
  All), so by default every account enumerates all AWS-managed policies and emits the same
  aws:policy ARNs as duplicate resources under each account parent. Consider setting
  Scope=iamTypes.PolicyScopeTypeLocal, or otherwise deduplicating AWS-managed policies across
  account parents, or document the tradeoff.

In `docs/connector.mdx`:
- Add the newly required IAM permissions to the credential/scope documentation:
  iam:ListPolicies, iam:GetPolicy, iam:ListAttachedRolePolicies, iam:ListAttachedGroupPolicies,
  iam:ListRolePolicies, iam:ListGroupPolicies, and the inline provisioning scopes
  iam:DeleteUserPolicy/iam:DeleteRolePolicy/iam:DeleteGroupPolicy.

In `pkg/connector` tests:
- Add unit tests covering inlinePolicyResourceID/parseInlinePolicyResourceID round-trip
  (including names with special chars), iamRoleNameFromARN for role ARNs with paths,
  policyGrantExpansion for user/role/group principals, and the listInlinePolicyNames
  parent-type switch.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@ggreer ggreer force-pushed the ggreer/iam-policy branch from 6b4ada0 to a5d35c0 Compare June 30, 2026 23:18
Comment thread pkg/connector/iam_user.go Outdated
return nil, nil, err
}

grants, err := listAttachedUserPolicyGrants(ctx, iamClient, userName, resource.Id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: This propagates an error and hard-fails the whole iam_user Grants phase if ListAttachedUserPolicies returns AccessDenied. role.go handles the equivalent call by logging a Warn and continuing (listAttachedRolePolicyGrants), but here (and in iam_group.go:162 and inline_policy.go List) the error is returned. For existing installs whose IAM policy lacks the newly required iam:ListAttachedUserPolicies / iam:ListGroupPolicies scopes, this turns a missing permission into a full sync failure. Consider degrading gracefully and consistently across all three paths. (confidence: medium)

Comment thread pkg/connector/iam_policy.go Outdated
Comment on lines +43 to +44
listPoliciesInput := &iam.ListPoliciesInput{
// TODO: Only list attached policies?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: ListPolicies with no Scope defaults to Scope=All / OnlyAttached=false, so this enumerates every AWS-managed policy (~1150) per account on every sync, and iam_policy is a child of each account. Since grants only reference policies that are actually attached, consider Scope: types.PolicyScopeTypeLocal and/or OnlyAttached: true to bound the resource count and per-policy traffic (addresses the existing TODO). (confidence: medium)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@ggreer ggreer force-pushed the ggreer/iam-policy branch from a5d35c0 to 3dfcbfc Compare June 30, 2026 23:33

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@ggreer ggreer force-pushed the ggreer/iam-policy branch 2 times, most recently from 11b1a89 to 146a010 Compare June 30, 2026 23:53

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@carolinaroncaglia

carolinaroncaglia commented Jul 1, 2026

Copy link
Copy Markdown

I detected this with my automated connector smoke suite. This change is live on the latest channel in our sandbox, so the smoke picked it up and aws-v2 started failing its sync on 2026-07-01:

sync-grants-for-resource: error listing grants for resource
  group/arn:aws:iam::531807593589:group/test-users failed:
  baton-aws: iam.ListAttachedGroupPolicies failed:
  StatusCode: 403, api error AccessDenied

Two things worth considering:

  1. Policy enumeration isn't gated on the resource type being enabled. In that tenant the IAM Managed Policy and Inline Policy resource types are toggled Off, yet the group-grant sync still calls ListAttachedGroupPolicies (and presumably ListGroupPolicies for inline). If those resource types are disabled, the connector ideally shouldn't enumerate them at all — that avoids requiring the extra IAM permission when the capability isn't in use.
  2. A permission error there aborts the entire sync. The 403 AccessDenied on one group's ListAttachedGroupPolicies failed the whole run — users, roles, SSO, everything — not just the policy data. Since this is a new/optional capability, it may be safer to skip + annotate (warn) on a permission error and let the rest of the sync complete, rather than failing hard.

The 403 itself is because our sandbox role (C1IntegrationTestAWS) lacks iam:ListAttachedGroupPolicies — we can add that, but the gating/resilience above would matter for any customer who enables the connector without the new IAM permissions, or who leaves the policy resource types off.

Happy to share more repro detail.

@ggreer ggreer force-pushed the ggreer/iam-policy branch 2 times, most recently from 9a59fe3 to d5b7f58 Compare July 2, 2026 20:39
Comment thread pkg/connector/inline_policy.go Outdated
Comment on lines +213 to +225
grant := grantSdk.NewGrant(
resource,
inlinePolicyAttachedEntitlement,
resource.ParentResourceId,
grantSdk.WithAnnotation(
&v2.V1Identifier{
Id: V1GrantID(
V1MembershipEntitlementID(resource.Id),
resource.ParentResourceId.Resource,
),
},
),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: For group/role parents, this inline-policy grant is emitted without a GrantExpandable annotation, unlike the managed-policy path (grantForAttachedManagedPolicy) which expands group grants to group:...:member (shallow) and role grants to role:...:assignment. AWS inline policies on a group/role apply to members/assumers, so as written those members won't inherit the inline-policy grant. Consider mirroring the managed-policy expansion for group and role principals for consistency. (confidence: medium)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking issues found — see review comments.

@ggreer ggreer force-pushed the ggreer/iam-policy branch 2 times, most recently from 5c7aad1 to 3980aa1 Compare July 2, 2026 22:55
@ggreer ggreer force-pushed the ggreer/iam-policy branch from 3980aa1 to f97bc3d Compare July 2, 2026 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants