Skip to content

fix(iam): include policy description in GetPolicy/ListPolicies/CreatePolicy responses - #2155

Open
sheldon-nadarajah-sft wants to merge 3 commits into
floci-io:mainfrom
sheldon-nadarajah-sft:fix/iam-getpolicy-missing-description
Open

fix(iam): include policy description in GetPolicy/ListPolicies/CreatePolicy responses#2155
sheldon-nadarajah-sft wants to merge 3 commits into
floci-io:mainfrom
sheldon-nadarajah-sft:fix/iam-getpolicy-missing-description

Conversation

@sheldon-nadarajah-sft

Copy link
Copy Markdown

Fixes #2154.

Bug

GetPolicy, ListPolicies, and CreatePolicy all built their response via the shared
policyXml() helper in IamQueryHandler.java, which never emitted a Description element —
even when the policy was created with one, and even though the sibling roleXml() (right above
it) does emit Description for roles. IamPolicy (the model) and IamService.createPolicy()
both handle the field correctly; it was only dropped on the way out to the client.

Real AWS's GetPolicy returns Description (see the
API docs's own sample
response). Consumers that diff against real AWS behavior — e.g. Terraform's AWS provider — see the
description as permanently missing on every read, and since IAM policy descriptions are immutable
(force-new on the AWS provider), this forces a destroy+recreate of the policy on every apply, even
when nothing in the underlying config changed.

Fix

One line: add .elem("Description", p.getDescription()) to policyXml(), matching the existing
pattern in roleXml(). XmlBuilder.elem() already skips the element when the value is null, so
this is a strict superset of the previous output — no behavior change for policies without one.

Tests

Added Description assertions to the existing createPolicy() and getPolicy() integration
tests in IamIntegrationTest — both already pass a Description on CreatePolicy but weren't
asserting on it in the response, so they didn't catch this. Ran locally (Java 25):

IamIntegrationTest:              49/49 passed
IamServiceTest:                  60/60 passed
IamManagedPolicyAccountScopeTest: 7/7 passed
IamConcurrencyTest:               13/13 passed

…Policy responses

policyXml() built the response for CreatePolicy, GetPolicy, and ListPolicies but never
emitted the Description element, even though IamPolicy stores it correctly and the
sibling roleXml() includes it for roles. Callers (e.g. Terraform's AWS provider) that
set a policy description see it as permanently missing on every read, which forces
destroy+recreate since IAM policy descriptions are immutable.

Fixes floci-io#2154
Copilot AI lite review requested due to automatic review settings August 7, 2026 08:51
@greptile-apps

greptile-apps Bot commented Aug 7, 2026

Copy link
Copy Markdown

Greptile Summary

The PR restores IAM policy descriptions in CreatePolicy and GetPolicy XML responses while preserving AWS-compatible omission from ListPolicies.

  • Adds operation-specific description serialization to the shared policy XML helper.
  • Adds integration coverage for CreatePolicy/GetPolicy inclusion and ListPolicies omission.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/main/java/io/github/hectorvent/floci/services/iam/IamQueryHandler.java Serializes policy descriptions for CreatePolicy and GetPolicy while explicitly omitting them from ListPolicies.
src/test/java/io/github/hectorvent/floci/services/iam/IamIntegrationTest.java Verifies description inclusion for CreatePolicy/GetPolicy and element-level omission for ListPolicies.

Reviews (3): Last reviewed commit: "fix(iam): make listPoliciesOmitsDescript..." | Re-trigger Greptile

Copilot AI 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.

Pull request overview

This PR fixes an IAM Query protocol compatibility gap by including the managed policy Description field in CreatePolicy, GetPolicy, and ListPolicies XML responses, aligning Floci’s output with AWS IAM behavior and preventing downstream drift (e.g., Terraform recreations).

Changes:

  • Add <Description> emission to the shared policyXml() serializer used by multiple IAM policy response paths.
  • Extend existing IAM integration tests to assert that Description is returned for CreatePolicy and GetPolicy.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/main/java/io/github/hectorvent/floci/services/iam/IamQueryHandler.java Adds Description to the IAM policy XML serialization helper used by Create/Get/List policy responses.
src/test/java/io/github/hectorvent/floci/services/iam/IamIntegrationTest.java Adds response assertions ensuring Description is present for CreatePolicy and GetPolicy integration flows.

@pgermosen pgermosen added the iam AWS Identity and Access Management (IAM) label Aug 7, 2026

@pgermosen pgermosen 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.

Reviewed in a local worktree. IAM suites pass: 129/129, 0 failures (IamIntegrationTest 49/49, IamServiceTest 60/60, IamManagedPolicyAccountScopeTest 7/7, IamConcurrencyTest 13/13) — matching every count in the description.

The bug is real, and the diagnosis is exactly right. GetPolicy must return Description; AWS's own sample response has it:

<Policy>
  <PolicyName>S3-read-only-example-bucket</PolicyName>
  <Description>Allows read-only access to the example bucket</Description>
  ...

The Terraform reasoning holds too — policy descriptions are immutable, so a permanently-absent description forces destroy+recreate on every apply.

But the one-line fix lands in a helper shared with ListPolicies, and AWS documents that ListPolicies must not return Description. The model's Policy.Description member doc states it directly: "This element is included in the response to the GetPolicy operation. It is not included in the response to the ListPolicies operation." So the change fixes one divergence and introduces another — details inline.

Verified rather than assumed:

  • XmlBuilder.elem() really does skip nulls, so the "strict superset" claim is accurate for the two operations where the element belongs.
  • The three call sites are CreatePolicy (:453), GetPolicy (:459) and ListPolicies (:473), the last inside the <member> loop.
  • CreatePolicy is fine. AWS documents neither inclusion nor exclusion, and its sample response omits Description only because the sample request never set one.
  • The model and the API reference agree, so this isn't a stale-doc artifact.

** The GetPolicy half is right and worth merging — it just needs to not apply to ListPolicies. It's a small fix, and the second comment covers the test that would keep it fixed.

Comment thread src/main/java/io/github/hectorvent/floci/services/iam/IamQueryHandler.java Outdated
@pgermosen pgermosen added waiting-contributor bug Something isn't working labels Aug 7, 2026
policyXml() is shared by CreatePolicy, GetPolicy, and ListPolicies — the
previous fix added Description to all three, but AWS's own Policy model
documents that Description "is included in the response to the GetPolicy
operation. It is not included in the response to the ListPolicies
operation." CreatePolicy documents neither inclusion nor exclusion, so it's
treated the same as GetPolicy.

policyXml() now takes an explicit includeDescription flag: true at the
CreatePolicy/GetPolicy call sites, false in ListPolicies' member loop. Adds
a ListPolicies test asserting no member response ever carries a Description
element, so folding it back into the shared helper unconditionally — the
exact way this would regress — fails loudly.

Addresses review feedback from @pgermosen.
@sheldon-nadarajah-sft

Copy link
Copy Markdown
Author

Thanks for the thorough review, @pgermosen — you're right, and I verified it directly against AWS's own Policy model docs before making the change: Description really is documented as GetPolicy-only, excluded from ListPolicies.

Pushed a fix: policyXml() now takes an explicit includeDescription flag (true for CreatePolicy/GetPolicy, false for ListPolicies' member loop), plus a ListPolicies test asserting no member ever carries a Description element — exactly the regression a well-intentioned "make it consistent with GetPolicy" cleanup would reintroduce.

All 130 IAM tests pass locally (IamIntegrationTest 50/50, IamServiceTest 60/60, IamManagedPolicyAccountScopeTest 7/7, IamConcurrencyTest 13/13).

@pgermosen pgermosen 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.

Looks good, just a minor thing

Both findings are fixed correctly and for the stated reason.

For the record, the resolved blocker was worth the round trip: with 58 seeded AWS-managed policies, each carrying a description, a plain aws iam list-policies would have returned 58+ members with a field real AWS omits — the same class of divergence this PR set out to remove, in the opposite direction.

Comment thread src/test/java/io/github/hectorvent/floci/services/iam/IamIntegrationTest.java Outdated
…he word

Match "<Description>" rather than the bare word "Description" to avoid a
false failure if a future policy's name/path happens to contain that
substring, and assert the created policy is actually present in the
response first, so the absence check can't pass vacuously against an
empty (or wrongly-filtered) member list.
@sheldon-nadarajah-sft

Copy link
Copy Markdown
Author

Good catch — fixed. Both halves of the reasoning are addressed: added .body(containsString("ListPoliciesOmitCheckPolicy")) so the absence check can't pass vacuously against an empty/mis-filtered member list, and switched the negative match to <Description> (the element) instead of the bare word, so it can't collide with a future policy name/path.

All 130 IAM tests still pass locally (IamIntegrationTest 50/50, IamServiceTest 60/60, IamManagedPolicyAccountScopeTest 7/7, IamConcurrencyTest 13/13).

@pgermosen

This comment was marked as outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working iam AWS Identity and Access Management (IAM) waiting-contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GetPolicy/ListPolicies/CreatePolicy responses omit the IAM policy's Description field

3 participants