Skip to content

Anchor ARM resource identity to read and create operations#4851

Draft
ArcturusZhang wants to merge 8 commits into
Azure:mainfrom
ArcturusZhang:arcturus/resolve-arm-resource-identity
Draft

Anchor ARM resource identity to read and create operations#4851
ArcturusZhang wants to merge 8 commits into
Azure:mainfrom
ArcturusZhang:arcturus/resolve-arm-resource-identity

Conversation

@ArcturusZhang

@ArcturusZhang ArcturusZhang commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

  • Change ARM resource resolution so concrete resource identities are seeded only from registered read/createOrUpdate operations
  • Replace permissive operation-path resource ID inference with strict ARM resource instance path parsing
  • Append lifecycle/list/action operations only when they match an already detected resource identity
  • Add regression coverage for list-only/action-only operations not creating resources and create-only operations still creating resources
  • Include the resolveArmResources design proposal and related linter candidates

Designed resource detection flow

  1. Collect candidate resource model details from listArmResources(program). These are registered TypeSpec resource models, not yet concrete ARM resources.

    • Current implementation largely already does this.
  2. Find identity operations for each candidate model: read and createOrUpdate.

    • Required change: previous implementation considered all registered ARM operations equally during resource creation, including list, action, update, delete, and checkExistence.
    • Covered by this PR: resources are now seeded only from read and createOrUpdate.
  3. Use identity operation paths as candidate resource instance paths. The resolver should trust the registered ARM operation kind; HTTP verb/response-shape correctness should be enforced by linters.

    • Required change: previous implementation derived resource identity from every operation path.
    • Covered by this PR: only read/createOrUpdate paths can establish resource identity.
  4. Parse and validate each candidate path as an ARM resource instance path.

    • Required change: previous parser synthesized resource IDs from non-instance paths, especially list/action paths, and fabricated providerless resource identities beyond known exceptions.
    • Covered by this PR: getResourcePathElements was removed and replaced with strict parseArmResourceInstancePath, which requires a literal /providers/{namespace} segment except for known providerless exceptions such as tenant, subscription, resource group, and explicit generic resource-id parameter paths.
  5. Group candidates by normalized resource identity: provider namespace, resource type segments, literal path segments case-insensitively, and variable path segments by position while ignoring variable names.

    • Required change: previous grouping did not consistently compare full normalized instance paths and could merge across paths/scopes when explicit resource names matched.
    • Covered by this PR: resource identity grouping now uses normalized instance-path comparison for identity operations.
  6. Create one concrete ResolvedResource per identity group. read + createOrUpdate on the same normalized path become one resource; different valid paths become separate resources; createOrUpdate alone can create a resource; list/action alone cannot.

    • Required change: previous list and action operations could create a ResolvedResource.
    • Covered by this PR: added tests for list-only/action-only not creating resources and create-only creating a resource.
  7. Derive stable resource facts from the canonical instance path: resourceInstancePath, resourceType, provider namespace, scope, resource name parameter or singleton key, and parent candidate information.

    • Required change: previous implementation derived these facts independently from each operation path; singleton metadata is model/decorator-based rather than path-based.
    • Partially covered by this PR: resourceInstancePath/resourceType now come from strict identity paths. Path-based singleton cleanup remains future work.
  8. Append remaining operations only to already-detected resources: lifecycle operations by matching normalized instance path, list operations by detected-resource-ID prefix, and action operations under the detected resource instance or collection path.

    • Required change: previous detection and operation grouping happened in one phase, allowing non-identity operations to define resources. Previous list matching also parsed list paths to reconstruct possible resources.
    • Covered by this PR: non-identity operations are appended only after detected identities exist. List operations now attach only to the closest detected resource whose normalized resource ID has the list request path as a prefix. Non-prefix list mappings are intentionally left as a TODO and do not create resource identities.
  9. Leave unmatched operations for provider/non-resource handling. They should not invent new resources.

    • Required change: previous registered ARM operations that failed association could be skipped and also excluded from providerOperations.
    • Not covered by this PR.
  10. Resolve parent and scope relationships after the full resource set is known, using normalized path comparison. Missing parents should be skipped rather than synthesized as empty resources.

    • Required change: previous implementation can synthesize empty parent/scope resources and return ExternalResource for ambiguous external scopes.
    • Not covered by this PR.

Validation

  • pnpm --filter @azure-tools/typespec-azure-resource-manager exec tsc -p tsconfig.build.json --noEmit
  • pnpm --filter @azure-tools/typespec-azure-resource-manager exec vitest run test/resource-resolution.test.ts --testNamePattern "parseArmResourceInstancePath"
  • Diagnostic-tolerant resolver behavior script verified list-only=0, create-only=1, full resource prefix-list/action attachment=1/1, resource group exception, and generic resource-id exception
  • Full pnpm --filter @azure-tools/typespec-azure-resource-manager exec vitest run test/resource-resolution.test.ts is currently blocked by baseline experimental-feature warnings from compiler/agent TypeSpec files

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@microsoft-github-policy-service microsoft-github-policy-service Bot added the lib:azure-resource-manager Issues for @azure-tools/typespec-azure-core library label Jul 8, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@azure-tools/typespec-azure-resource-manager@4851

commit: 1e64910

@azure-sdk-automation

azure-sdk-automation Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • @azure-tools/typespec-azure-resource-manager
Show changes

@azure-tools/typespec-azure-resource-manager - fix ✏️

Fix resolveArmResources resource identity detection to seed resources from strict ARM resource instance paths instead of inferring resource IDs from list or action operation paths.

@azure-sdk-automation

Copy link
Copy Markdown
Contributor

You can try these changes here

🛝 Playground 🌐 Website

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

⚡ Benchmark Results

⚠️ 2 metric(s) regressed above the +5% threshold:

Metric Baseline Current Change
 ↳ linter/@azure-tools/typespec-azure-core/no-header-explode 🔴 26.7ms 🔴 28.2ms +5.3% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-query-explode 🔴 27.8ms 🔴 29.5ms +6.0% 🔴
Full details – comparing 651bb03 vs baseline rolling-baseline-4df8556-2398555 (rolling baseline (20 main runs))
Metric Baseline Current Change
total 🔴 1.01s 🔴 1.05s +3.4%
loader 🟡 307.6ms 🟡 318.5ms +3.5%
resolver 🟢 40.5ms 🟢 41.2ms +1.8%
checker 🟡 365.7ms 🟡 379.9ms +3.9%
validation 🟢 85.1ms 🟢 86.9ms +2.1%
 ↳ validation/@azure-tools/typespec-azure-core 🟡 12.6ms 🟡 13.3ms +5.3%
 ↳ validation/@typespec/http 🟡 15.5ms 🟡 16.3ms +4.7%
 ↳ validation/@typespec/rest 🟢 1.6ms 🟢 1.7ms +2.7%
 ↳ validation/@typespec/versioning 🔴 51.4ms 🔴 51.6ms +0.4%
 ↳ validation/compiler 🟢 3.9ms 🟢 3.9ms +1.7%
linter 🟡 215.3ms 🟡 222.6ms +3.4%
 ↳ linter/@azure-tools/typespec-azure-core/auth-required 🟢 0.1ms 🟢 0.1ms +7.4%
 ↳ linter/@azure-tools/typespec-azure-core/bad-record-type 🟢 0.6ms 🟢 0.6ms +1.7%
 ↳ linter/@azure-tools/typespec-azure-core/byos 🟢 8.4ms 🟢 8.6ms +2.8%
 ↳ linter/@azure-tools/typespec-azure-core/casing-style 🟢 1.5ms 🟢 1.5ms +0.9%
 ↳ linter/@azure-tools/typespec-azure-core/composition-over-inheritance 🟢 0.2ms 🟢 0.2ms +1.6%
 ↳ linter/@azure-tools/typespec-azure-core/documentation-required 🟢 1.9ms 🟢 2.0ms +2.2%
 ↳ linter/@azure-tools/typespec-azure-core/friendly-name 🟢 1.4ms 🟢 1.4ms +2.0%
 ↳ linter/@azure-tools/typespec-azure-core/key-visibility-required 🟢 0.4ms 🟢 0.4ms +3.2%
 ↳ linter/@azure-tools/typespec-azure-core/known-encoding 🟢 0.5ms 🟢 0.5ms +3.0%
 ↳ linter/@azure-tools/typespec-azure-core/long-running-polling-operation-required 🟢 0.8ms 🟢 0.8ms +3.4%
 ↳ linter/@azure-tools/typespec-azure-core/no-case-mismatch 🟢 0.7ms 🟢 0.7ms +2.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-closed-literal-union 🟢 1.0ms 🟢 1.0ms +0.1%
 ↳ linter/@azure-tools/typespec-azure-core/no-enum 🟢 0.2ms 🟢 0.3ms +2.5%
 ↳ linter/@azure-tools/typespec-azure-core/no-error-status-codes 🟢 0.3ms 🟢 0.3ms +0.5%
 ↳ linter/@azure-tools/typespec-azure-core/no-explicit-routes-resource-ops 🟢 0.2ms 🟢 0.2ms +1.5%
 ↳ linter/@azure-tools/typespec-azure-core/no-format 🟢 0.8ms 🟢 0.8ms +2.1%
 ↳ linter/@azure-tools/typespec-azure-core/no-generic-numeric 🟢 0.8ms 🟢 0.9ms +2.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-header-explode 🔴 26.7ms 🔴 28.2ms +5.3% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-legacy-usage 🟢 2.1ms 🟢 2.1ms +1.3%
 ↳ linter/@azure-tools/typespec-azure-core/no-multiple-discriminator 🟢 0.2ms 🟢 0.2ms +5.9%
 ↳ linter/@azure-tools/typespec-azure-core/no-nullable 🟢 0.4ms 🟢 0.4ms +4.3%
 ↳ linter/@azure-tools/typespec-azure-core/no-offsetdatetime 🟢 2.0ms 🟢 2.0ms +1.8%
 ↳ linter/@azure-tools/typespec-azure-core/no-openapi 🟢 2.3ms 🟢 2.4ms +1.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-private-usage 🟢 3.2ms 🟢 3.2ms +1.0%
 ↳ linter/@azure-tools/typespec-azure-core/no-query-explode 🔴 27.8ms 🔴 29.5ms +6.0% 🔴
 ↳ linter/@azure-tools/typespec-azure-core/no-response-body 🔴 33.4ms 🔴 35.0ms +4.7%
 ↳ linter/@azure-tools/typespec-azure-core/no-rest-library-interfaces 🟢 0.1ms 🟢 0.1ms +1.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-route-parameter-name-mismatch 🟢 7.7ms 🟢 8.1ms +5.3%
 ↳ linter/@azure-tools/typespec-azure-core/no-rpc-path-params 🟢 0.4ms 🟢 0.4ms +2.6%
 ↳ linter/@azure-tools/typespec-azure-core/no-string-discriminator 🟢 0.1ms 🟢 0.2ms +5.7%
 ↳ linter/@azure-tools/typespec-azure-core/no-unknown 🟢 0.4ms 🟢 0.4ms +3.2%
 ↳ linter/@azure-tools/typespec-azure-core/no-unnamed-union 🟢 0.8ms 🟢 0.8ms +1.5%
 ↳ linter/@azure-tools/typespec-azure-core/operation-missing-api-version 🟢 0.4ms 🟢 0.4ms -0.4%
 ↳ linter/@azure-tools/typespec-azure-core/request-body-problem 🟢 0.5ms 🟢 0.5ms +0.8%
 ↳ linter/@azure-tools/typespec-azure-core/require-versioned 🟢 0.1ms 🟢 0.1ms +2.6%
 ↳ linter/@azure-tools/typespec-azure-core/response-schema-problem 🔴 32.6ms 🔴 33.6ms +2.9%
 ↳ linter/@azure-tools/typespec-azure-core/rpc-operation-request-body 🟢 0.7ms 🟢 0.7ms +4.9%
 ↳ linter/@azure-tools/typespec-azure-core/spread-discriminated-model 🟢 0.5ms 🟢 0.5ms +0.7%
 ↳ linter/@azure-tools/typespec-azure-core/use-standard-names 🟢 7.6ms 🟢 8.0ms +5.3%
 ↳ linter/@azure-tools/typespec-azure-core/use-standard-operations 🟢 0.3ms 🟢 0.3ms +1.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-agent-base-type-child-resources 🔴 21.0ms 🔴 22.0ms +4.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-agent-base-type-lifecycle-operations 🟢 0.3ms 🟢 0.3ms +9.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-common-types-version 🟡 12.1ms 🟡 12.5ms +3.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-custom-resource-no-key 🟢 0.2ms 🟢 0.2ms -0.4%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-custom-resource-usage-discourage 🟢 0.1ms 🟢 0.1ms +1.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-delete-operation-response-codes 🟢 5.0ms 🟢 4.6ms -6.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-feature-file-usage-discourage 🟢 0.1ms 🟢 0.1ms -4.5%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-path-casing-conflicts 🟡 17.1ms 🟡 17.0ms -0.5%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-no-record 🟢 0.6ms 🟢 0.6ms -1.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-post-operation-response-codes 🟢 1.4ms 🟢 1.4ms +2.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-put-operation-response-codes 🟢 0.2ms 🟢 0.2ms -4.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-action-no-segment 🟢 0.4ms 🟢 0.4ms -3.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-duplicate-property 🟢 0.4ms 🟢 0.4ms +1.5%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-interface-requires-decorator 🟢 0.1ms 🟢 0.1ms -0.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-action-verb 🟢 0.2ms 🟢 0.2ms -4.4%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-envelope-property 🟢 0.3ms 🟢 0.3ms +4.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-invalid-version-format 🟢 0.2ms 🟢 0.2ms +0.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-key-invalid-chars 🟢 0.5ms 🟢 0.5ms +1.4%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-name-pattern 🟢 0.1ms 🟢 0.1ms +3.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation 🟢 0.5ms 🟢 0.6ms +2.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-operation-response 🟢 9.0ms 🟢 9.1ms +1.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-patch 🟢 0.8ms 🟢 0.8ms -4.4%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-path-segment-invalid-chars 🟢 0.4ms 🟢 0.4ms +0.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/arm-resource-provisioning-state 🟢 0.4ms 🟢 0.4ms +2.7%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/beyond-nesting-levels 🟢 0.2ms 🟢 0.2ms +2.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/empty-updateable-properties 🟢 0.4ms 🟢 0.4ms +0.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/improper-subscription-list-operation 🟢 0.1ms 🟢 0.1ms +13.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/lro-location-header 🟡 19.7ms 🔴 20.6ms +5.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-operations-endpoint 🟢 0.1ms 🟢 0.1ms +1.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/missing-x-ms-identifiers 🟢 1.0ms 🟢 1.0ms -1.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-empty-model 🟢 0.3ms 🟢 0.3ms +2.4%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-override-props 🟢 0.3ms 🟢 0.3ms -1.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-reserved-resource-property 🟢 0.2ms 🟢 0.2ms -0.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-resource-delete-operation 🟢 0.5ms 🟢 0.5ms -0.9%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/no-response-body 🔴 29.8ms 🔴 30.7ms +3.1%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/patch-envelope 🟢 0.4ms 🟢 0.4ms +1.2%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/resource-name 🟢 0.4ms 🟢 0.4ms -1.3%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/secret-prop 🟢 5.4ms 🟢 5.1ms -6.0%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/unsupported-type 🟢 0.7ms 🟢 0.7ms -2.8%
 ↳ linter/@azure-tools/typespec-azure-resource-manager/version-progression 🟢 0.3ms 🟢 0.3ms -2.1%
 ↳ linter/@azure-tools/typespec-client-generator-core/property-name-conflict 🟢 2.3ms 🟢 2.3ms +0.8%
 ↳ linter/@azure-tools/typespec-client-generator-core/require-client-suffix 🟢 1.7ms 🟢 1.7ms +1.4%
emit 🔴 6.04s 🔴 6.28s +3.9%
 ↳ emit/@Azure-Tools 🟢 0.0ms 🟢 0.0ms +0.0%
 ↳ emit/@azure-tools/typespec-autorest 🟢 125.4ms 🟢 129.9ms +3.6%
 ↳ emit/@azure-tools/typespec-python 🔴 2.25s 🔴 2.35s +4.1%
 ↳ emit/@typespec 🟢 0.0ms 🟢 0.0ms +0.0%
 ↳ emit/@typespec/http-client-js 🔴 571.9ms 🔴 587.6ms +2.7%
 ↳ emit/@typespec/openapi3 🟢 107.9ms 🟢 112.4ms +4.1%
 ↳ emit/@typespec/openapi3/compute 🟢 93.8ms 🟢 97.7ms +4.2%
 ↳ emit/@typespec/openapi3/write 🟢 13.9ms 🟢 14.4ms +3.7%

Averaged across 3 specs (azure-arm-resource-manager, azure-core-dataplane, azure-full).
Threshold: changes > ±5% are highlighted.
🟢 Fast · 🟡 Moderate (stages >200ms, rules >10ms) · 🔴 Slow (stages >400ms, rules >20ms)

ArcturusZhang and others added 7 commits July 9, 2026 11:01
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lib:azure-resource-manager Issues for @azure-tools/typespec-azure-core library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant