Skip to content

groups: select only id when listing members and owners - #1911

Open
orgads wants to merge 1 commit into
hashicorp:mainfrom
orgads:fix/group-members-select
Open

groups: select only id when listing members and owners#1911
orgads wants to merge 1 commit into
hashicorp:mainfrom
orgads:fix/group-members-select

Conversation

@orgads

@orgads orgads commented Aug 6, 2026

Copy link
Copy Markdown

Community Note

  • Please vote on this PR by adding a 👍 reaction to the original PR to help the community and maintainers prioritize for review
  • Please do not leave comments along the lines of "+1", "me too" or "any updates", they generate extra noise for PR followers and do not help prioritize for review

Description

The group data source and the group / group_member / group_without_members resources read members
and owners purely for their object IDs — every consumer is
pointer.From(object.DirectoryObject().Id) — but they ask Graph for the full directory object
projection. This requests $select=id instead, through three small helpers next to the existing
groupGetMember in internal/services/groups/groups.go.

The payload difference is substantial. For a group of 39 users:

request response size
/beta/groups/{id}/members 819 KB (79 properties per member)
/beta/groups/{id}/members?$select=id 694 bytes

That is ~1000x less to transfer and unmarshal, for a data source that then discards all of it
except one field per member. It applies on every read of a group's members and owners,
including refreshes of azuread_group resources.

How I got here (#1910): the full projection was also what tripped a Microsoft Graph beta
serialization bug for app-only callers lacking User.Read.All. Every user property in that
restricted projection comes back null, including the non-nullable isProvisionedToOnPremises of
microsoft.graph.onPremisesProvisioningState, so Graph aborted mid-object and appended an error to
the bytes it had already sent, which the provider surfaced as:

Error: Could not retrieve group members for group with object ID: "/groups/<id>"
invalid character '{' after object key:value pair

Microsoft have since fixed that serialization failure, so this PR is no longer needed to fix
#1910
— I have confirmed the failure no longer reproduces and said so on the issue. I still think
the change is worth taking on the two remaining grounds: the payload saving above, and not asking
Graph for properties the provider never reads, which is what exposed it to a server-side bug in a
projection it had no use for.

@odata.type is still returned under $select=id, so the polymorphic directory-object
unmarshalling is unaffected — responses come back as {"@odata.type": "...", "id": "..."}.

Changes to existing Resource / Data Source

  • I have added an explanation of what my changes do and why I'd like you to include them (This may be covered by linking to an issue above, but may benefit from additional explanation).
  • I have written new tests for my resource or datasource changes & updated any relevant documentation. — no new tests: this changes the request sent to Graph, not observable schema or behaviour, and the existing acceptance tests for these resources and the data source already assert the members / owners values this code path produces. Happy to add coverage if you can suggest a shape for asserting $select that fits the suite.
  • I have successfully run tests with my changes locally. See below.
  • (For changes that include a state migration only). I have manually tested the migration path between relevant versions of the provider.

Testing

Unit tests, build and vet pass:

$ go build ./... && go vet ./internal/services/groups/... && go test ./internal/...
ok  	github.com/hashicorp/terraform-provider-azuread/internal/services/groups	0.018s

I do not have a tenant I can point the acceptance tests at. Instead I verified against a real
tenant with a dev_overrides build, reading two security groups (40 and 7 members) with an
app-only service principal, both with include_transitive_members = false and via the owners
path:

data.azuread_group.deployers: Read complete after 2s [id=/groups/7e9a2227-...]
data.azuread_group.admin: Read complete after 2s [id=/groups/11478e71-...]

Changes to Outputs:
  + admin_members     = 40
  + admin_owners      = 7
  + deployers_members = 7
  + deployers_sample  = "2612466e-..."

The member and owner IDs are identical to what the unfiltered request returns for the same groups,
i.e. narrowing the projection loses nothing the data source exposes. Note this is no longer a
before/after comparison: when I first ran it, these two reads failed on released 3.9.0 with the
error quoted above and succeeded with this change — but with Microsoft's fix in place, stock 3.9.0
now reads them successfully too.

Change Log

This is a (please select all that apply):

  • Bug Fix
  • New Feature (ie adding a service, resource, or data source)
  • Enhancement
  • Breaking Change

Related Issue(s)

Relates to #1910

Rollback Plan

If a change needs to be reverted, we will publish an updated version of the provider.

Changes to Security Controls

No changes to security controls. The requests are narrowed, not widened: the provider now asks
Graph for strictly fewer properties than before, and no permission requirements change.

The group data source and the group/group_member/group_without_members
resources read members and owners purely for their object IDs, but ask
Graph for the full directory object projection. Request $select=id
instead.

For a group of 39 users that takes the response from 819KB, at 79
properties per member, down to 694 bytes -- nearly all of which the
callers discarded, since each keeps only the id. It applies to every read
of a group's members and owners, resource refreshes included.

The unfiltered projection was also what exposed the provider to a Graph
beta serialization bug (hashicorp#1910): for app-only callers lacking
User.Read.All, every user property came back null, including the
non-nullable isProvisionedToOnPremises of onPremisesProvisioningState, so
Graph aborted mid-object and appended an error to the bytes it had
already sent, leaving a body that failed to unmarshal. Microsoft have
since fixed that, so this is no longer a fix for hashicorp#1910 -- but not asking
for properties the provider never reads is what would have kept it clear
of a server-side bug in a projection it has no use for.

Verified against two security groups (40 and 7 members) with an app-only
token: members and owners come back identical to the unfiltered request.
@odata.type is still returned under $select=id, so the polymorphic
directory object unmarshalling is unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@orgads
orgads force-pushed the fix/group-members-select branch from b006a16 to a56c771 Compare August 7, 2026 10:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant