Skip to content

Express and read the sites identity block - #9

Merged
StanBarrows merged 1 commit into
mainfrom
feature/site-identity-block
Aug 7, 2026
Merged

Express and read the sites identity block#9
StanBarrows merged 1 commit into
mainfrom
feature/site-identity-block

Conversation

@aicodebar

Copy link
Copy Markdown
Collaborator

Closes the gap that made the consuming app bypass this package for Microsoft.Web/sites entirely.

The problem

WebSitePayload::toAzureBody() emits only location, kind, properties and tags. There is no way to inject a top-level key, and properties nests under properties, so identity could not be smuggled in. On the read side, WebSiteData::fromAzure() read no identity at all — a repo-wide search for principalId returned only RoleAssignmentData, RoleAssignmentPayload and UserAssignedIdentityData; no DTO read a top-level identity block off any resource.

The app needs identity.principalId to grant a Function App's system-assigned managed identity storage RBAC, and hard-fails without it. So it hand-rolled its own ARM requests and skipped this package's sites surface.

The change

  • WebSitePayload — trailing ?string $identityType = null; emits identity: { type } when non-null. Copied verbatim from the existing CognitiveServicesAccountPayload / FoundryProjectPayload shape, so this is the pattern the codebase already blesses rather than a new one.
  • WebSiteData — trailing nullable identityType, identityPrincipalId, identityTenantId, read via the existing Field::arrNullableString() helpers.
  • FunctionAppResource::createOrUpdate() — trailing optional ?string $identityType = null.
  • docs/usage/functions-and-web-apps.md — a short section showing createOrUpdate(..., identityType:)$site->identityPrincipalIdroleAssignments()->create(), which is the exact flow that motivated this.

All additions are trailing optional/nullable parameters — non-breaking for both positional and named callers.

Gates

Gate Result
composer inventory:parity pass
generate-api-reference.php --check up to date (regenerated and committed)
composer test 598 passed, 1618 assertions
composer test:coverage --min=100 100.0 %
PHPStan level 10, no baseline No errors
pint --test passed

Notes

  • docs/inventory-parity.md is rewritten by inventory:parity even in --check mode; the only delta was the Generated at: line, so it was reverted rather than committing timestamp-only churn.
  • The 100% gate forced exactly one assertion that would not otherwise exist: not->toHaveKey('identity') on the pre-existing body test. The false branch was already executed by existing tests, so coverage was satisfied without it — the assertion was added so the null branch is genuinely pinned rather than merely traversed.
  • This repo has no coverage driver on the default PHP 8.5; composer test:coverage needs Herd's xdebug .so passed explicitly via -d zend_extension=....

Merging this auto-tags a release (release.yml runs github-tag-action on PR close against main, DEFAULT_BUMP: minor), which is why the work was put on a branch rather than committed to main.

🤖 Generated with Claude Code

Consuming apps had to hand-roll their own ARM `sites` GET/PUT requests
because neither `WebSitePayload` nor `WebSiteData` could touch the
top-level `identity` block: there was no way to ask ARM for a
system-assigned identity on a Function App, and no way to read back the
`identity.principalId` that a subsequent role assignment needs to grant
that identity storage/Key Vault RBAC. That single missing field was
enough to make the whole typed surface unusable for provisioning, so the
app bypassed it entirely and grew a bespoke request pair alongside it.

- `WebSitePayload` takes a trailing optional `identityType` and emits
  `identity: {type: ...}`, matching the existing precedent in
  `CognitiveServicesAccountPayload` and `FoundryProjectPayload`.
- `WebSiteData` exposes `identityType`, `identityPrincipalId` and
  `identityTenantId`, read via the usual `Field::arrNullableString`
  dotted-path helpers.
- `FunctionAppResource::createOrUpdate()` threads `identityType` through.

All three additions are trailing optional/nullable parameters, so this is
non-breaking for existing callers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@StanBarrows
StanBarrows merged commit 8e50167 into main Aug 7, 2026
10 checks passed
@StanBarrows
StanBarrows deleted the feature/site-identity-block branch August 8, 2026 05:29
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