Skip to content

[Mgmt CodeGen] Resources using Legacy.RoutedOperations with dynamic parent types are not generated as Resource/Collection/Data classes #58137

@mzhongl524

Description

@mzhongl524

Problem

The MPG TypeSpec codegen (@azure-typespec/http-client-csharp-mgmt) does NOT generate Resource, Collection, and Data classes for resources that use the Legacy.RoutedOperations pattern with dynamic/parameterized parent types in their URL paths.

Three resources in EventGrid are affected:

  1. NetworkSecurityPerimeterConfiguration — path uses {resourceType}/{resourceName} as dynamic parent
  2. PrivateEndpointConnection — path uses {parentType}/{parentName} as dynamic parent
  3. PrivateLinkResource — path uses {parentType}/{parentName} as dynamic parent + uses @customAzureResource

Expected (Old AutoRest SDK on main branch)

Each resource had separate Resource/Collection/Data classes per parent type:

Resource Generated Classes (main branch)
PrivateEndpointConnection EventGridDomainPrivateEndpointConnectionResource, EventGridTopicPrivateEndpointConnectionResource, EventGridPartnerNamespacePrivateEndpointConnectionResource + corresponding Collections + shared EventGridPrivateEndpointConnectionData
PrivateLinkResource EventGridDomainPrivateLinkResource, EventGridTopicPrivateLinkResource, PartnerNamespacePrivateLinkResource + corresponding Collections + shared EventGridPrivateLinkResourceData
NetworkSecurityPerimeter DomainNetworkSecurityPerimeterConfigurationResource, TopicNetworkSecurityPerimeterConfigurationResource + corresponding Collections + shared NetworkSecurityPerimeterConfigurationData

Actual (New TypeSpec codegen)

No Resource, Collection, or Data classes are generated. Instead, operations are emitted as plain methods on MockableEventGridResourceGroupResource:

// Operations are on MockableEventGridResourceGroupResource instead of proper Resource classes
public virtual async Task<Response<EventGridPrivateEndpointConnection>> GetAsync(
    PrivateEndpointConnectionsParentType parentType, string parentName, 
    string privateEndpointConnectionName, CancellationToken cancellationToken = default)

Root Cause Analysis

1. Resources NOT in @armProviderSchema

The @armProviderSchema decorator on the root client lists 27 resources, but none of the three affected resources are included:

0: Microsoft.EventGrid.CaCertificate
1: Microsoft.EventGrid.Namespace
2: Microsoft.EventGrid.Channel
...
26: Microsoft.EventGrid.TopicTypeInfo

Missing:

  • NetworkSecurityPerimeterConfiguration
  • PrivateEndpointConnection
  • PrivateLinkResource

The codegen generates Resource classes only for resources listed in @armProviderSchema (ManagementInputLibrary.cs BuildArmProviderSchema()). Since these three are not listed, no Resource classes are generated.

2. Why are they missing from @armProviderSchema?

These resources use Azure.ResourceManager.Legacy.RoutedOperations with hardcoded full route paths and dynamic parent type parameters:

PrivateEndpointConnection.tsp:

@armResourceOperations
interface PrivateEndpointConnectionOps
  extends Azure.ResourceManager.Legacy.RoutedOperations<
      {
        ...ApiVersionParameter,
        ...SubscriptionIdParameter,
        ...ResourceGroupParameter,
        @path parentType: PrivateEndpointConnectionsParentType,
        @path parentName: string,
      },
      {},
      ResourceRoute = #{
        route: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/microsoft.EventGrid/{parentType}/{parentName}",
      }
    > {}

@armResourceOperations(#{ allowStaticRoutes: true })
interface PrivateEndpointConnections {
  @get
  @route("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/microsoft.EventGrid/{parentType}/{parentName}/privateEndpointConnections/{privateEndpointConnectionName}")
  get is PrivateEndpointConnectionOps.ActionSync<...>;
  // ...
}

NetworkSecurityPerimeterConfiguration.tsp:

@armResourceOperations
interface NetworkSecurityPerimeterConfigurationOps
  extends Azure.ResourceManager.Legacy.RoutedOperations<
      {
        ...ApiVersionParameter,
        ...SubscriptionIdParameter,
        ...ResourceGroupParameter,
        @path resourceType: NetworkSecurityPerimeterResourceType,
        @path resourceName: string,
      },
      {},
      ResourceRoute = #{
        route: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/microsoft.EventGrid/{resourceType}/{resourceName}",
      }
    > {}

PrivateLinkResource.tsp:

@Azure.ResourceManager.Legacy.customAzureResource
model PrivateLinkResource {
  properties?: PrivateLinkResourceProperties;
  id?: armResourceIdentifier;
  name?: string;
  type?: armResourceType;
}

@armResourceOperations
interface PrivateLinkResourceOps
  extends Azure.ResourceManager.Legacy.RoutedOperations<
      {
        @path parentType: PrivateLinkResourcesParentType,
        @path parentName: string,
      },
      {},
      ResourceRoute = #{
        route: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/microsoft.EventGrid/{parentType}/{parentName}",
      }
    > {}

The pattern is: the TypeSpec compiler's ARM library does not recognize these Legacy.RoutedOperations with dynamic parent segments as proper ARM resources, so they are not included in the @armProviderSchema output. The codegen then treats them as standalone operations on extension methods rather than building proper Resource hierarchies.

3. Swagger definitions use parameterized paths

In the swagger (EventGrid.json), these operations use parameterized parent type segments:

"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.EventGrid/{parentType}/{parentName}/privateEndpointConnections/{privateEndpointConnectionName}": {
    "get": { "operationId": "PrivateEndpointConnections_Get" },
    "put": { "operationId": "PrivateEndpointConnections_Update", "x-ms-long-running-operation": true },
    "delete": { "operationId": "PrivateEndpointConnections_Delete", "x-ms-long-running-operation": true }
}

The old AutoRest codegen handled this by expanding {parentType} into concrete parent types and generating separate Resource classes per parent. The new TypeSpec codegen does not support this pattern.


Impact

This is a significant breaking change for SDK consumers migrating from the AutoRest-generated SDK:

  1. No Resource lifecycle management — Users cannot use standard ARM resource patterns (Get, CreateOrUpdate, Delete on typed resource objects)
  2. No Collection pattern — Users cannot enumerate resources using typed collections
  3. No Data classes — Resources are returned as plain model types (EventGridPrivateEndpointConnection) instead of proper resource data types with ARM metadata
  4. Breaking API surface — All existing code using EventGridDomainPrivateEndpointConnectionResource, EventGridTopicPrivateLinkResource, etc. will break

Suggested Fix

The codegen (or TypeSpec ARM library) needs to handle the Legacy.RoutedOperations pattern with dynamic parent types by:

  1. Recognizing these resources as ARM resources in @armProviderSchema
  2. Expanding dynamic parent types into concrete parent-specific resources (e.g., {parentType}topics, domains, partnerNamespaces, namespaces)
  3. Generating separate Resource/Collection classes per concrete parent type, with a shared Data class

Generator Version

  • @azure-typespec/http-client-csharp-mgmt@1.0.0-alpha.20260407.1
  • TypeSpec Compiler: 1.10.0

Metadata

Metadata

Assignees

Labels

CodeGenIssues that relate to code generationMgmtThis issue is related to a management package.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions