From 99d2af5f03f1f78654a437f320c1f47977915b10 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:28:14 +0000 Subject: [PATCH 1/5] Initial plan From f98741a6f1cb4f02b295c892d776186fb3a00df0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:40:30 +0000 Subject: [PATCH 2/5] Add Foundry and Foundry project display names to azd output - Add AzureResourceTypeCognitiveServiceAccountProject constant for Microsoft.CognitiveServices/accounts/projects - Update getCognitiveServiceResourceTypeDisplayName to detect Foundry projects by checking for /projects/ in resource ID - Update logic to show "Foundry" for AIServices and AIHub kinds instead of "Azure AI Services" - Add comprehensive tests for all Cognitive Services resource type display names - All tests pass, linting clean, spelling checked Co-authored-by: vhvb1989 <24213737+vhvb1989@users.noreply.github.com> --- cli/azd/pkg/azapi/azure_resource_types.go | 3 + cli/azd/pkg/infra/azure_resource_manager.go | 12 +- .../pkg/infra/azure_resource_manager_test.go | 108 ++++++++++++++++++ 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/cli/azd/pkg/azapi/azure_resource_types.go b/cli/azd/pkg/azapi/azure_resource_types.go index 61827354535..c08e0c96326 100644 --- a/cli/azd/pkg/azapi/azure_resource_types.go +++ b/cli/azd/pkg/azapi/azure_resource_types.go @@ -52,6 +52,7 @@ const ( //nolint:lll AzureResourceTypeMachineLearningEndpoint AzureResourceType = "Microsoft.MachineLearningServices/workspaces/onlineEndpoints" AzureResourceTypeCognitiveServiceAccountDeployment AzureResourceType = "Microsoft.CognitiveServices/accounts/deployments" + AzureResourceTypeCognitiveServiceAccountProject AzureResourceType = "Microsoft.CognitiveServices/accounts/projects" ) // GetResourceTypeDisplayName retrieves the display name for the given resource type. @@ -123,6 +124,8 @@ func GetResourceTypeDisplayName(resourceType AzureResourceType) string { return "Azure AI Services" case AzureResourceTypeCognitiveServiceAccountDeployment: return "Azure AI Services Model Deployment" + case AzureResourceTypeCognitiveServiceAccountProject: + return "Foundry project" case AzureResourceTypeSearchService: return "Search service" case AzureResourceTypeVideoIndexer: diff --git a/cli/azd/pkg/infra/azure_resource_manager.go b/cli/azd/pkg/infra/azure_resource_manager.go index 3887472a516..5427889ab05 100644 --- a/cli/azd/pkg/infra/azure_resource_manager.go +++ b/cli/azd/pkg/infra/azure_resource_manager.go @@ -224,7 +224,8 @@ func (rm *AzureResourceManager) GetResourceTypeDisplayName( } else { return resourceTypeDisplayName, nil } - } else if resourceType == azapi.AzureResourceTypeCognitiveServiceAccount { + } else if resourceType == azapi.AzureResourceTypeCognitiveServiceAccount || + resourceType == azapi.AzureResourceTypeCognitiveServiceAccountProject { resourceTypeDisplayName, err := rm.getCognitiveServiceResourceTypeDisplayName(ctx, subscriptionId, resourceId) if err != nil { @@ -277,6 +278,11 @@ func (rm *AzureResourceManager) getCognitiveServiceResourceTypeDisplayName( subscriptionId string, resourceId string, ) (string, error) { + // Check if this is a Foundry project resource (child resource) + if strings.Contains(resourceId, "/projects/") { + return "Foundry project", nil + } + resource, err := rm.resourceService.GetResource(ctx, subscriptionId, resourceId, cognitiveServiceApiVersion) if err != nil { @@ -287,6 +293,10 @@ func (rm *AzureResourceManager) getCognitiveServiceResourceTypeDisplayName( return "Azure OpenAI", nil } else if strings.Contains(resource.Kind, "FormRecognizer") { return "Document Intelligence", nil + } else if strings.Contains(resource.Kind, "AIHub") { + return "Foundry", nil + } else if resource.Kind == "AIServices" { + return "Foundry", nil } else { return "Azure AI Services", nil } diff --git a/cli/azd/pkg/infra/azure_resource_manager_test.go b/cli/azd/pkg/infra/azure_resource_manager_test.go index f3b3d6b1ce5..a0191ee2749 100644 --- a/cli/azd/pkg/infra/azure_resource_manager_test.go +++ b/cli/azd/pkg/infra/azure_resource_manager_test.go @@ -512,6 +512,114 @@ func TestFindResourceGroupForEnvironment(t *testing.T) { } } +func TestGetResourceTypeDisplayNameForCognitiveServices(t *testing.T) { + const SUBSCRIPTION_ID = "273f1e6b-6c19-4c9e-8b67-5fbe78b14063" + + tests := []struct { + name string + resourceId string + kind string + expectedName string + }{ + { + name: "Azure OpenAI", + resourceId: fmt.Sprintf( + "/subscriptions/%s/resourceGroups/test-rg/providers/Microsoft.CognitiveServices/accounts/test-openai", + SUBSCRIPTION_ID, + ), + kind: "OpenAI", + expectedName: "Azure OpenAI", + }, + { + name: "Document Intelligence (FormRecognizer)", + resourceId: fmt.Sprintf( + "/subscriptions/%s/resourceGroups/test-rg/providers/"+ + "Microsoft.CognitiveServices/accounts/test-formrecognizer", + SUBSCRIPTION_ID, + ), + kind: "FormRecognizer", + expectedName: "Document Intelligence", + }, + { + name: "Foundry (AIServices)", + resourceId: fmt.Sprintf( + "/subscriptions/%s/resourceGroups/test-rg/providers/Microsoft.CognitiveServices/accounts/test-foundry", + SUBSCRIPTION_ID, + ), + kind: "AIServices", + expectedName: "Foundry", + }, + { + name: "Foundry project", + resourceId: fmt.Sprintf( + "/subscriptions/%s/resourceGroups/test-rg/providers/"+ + "Microsoft.CognitiveServices/accounts/test-foundry/projects/test-project", + SUBSCRIPTION_ID, + ), + kind: "AIServices", + expectedName: "Foundry project", + }, + { + name: "Azure AI Services (CognitiveServices)", + resourceId: fmt.Sprintf( + "/subscriptions/%s/resourceGroups/test-rg/providers/Microsoft.CognitiveServices/accounts/test-cogservices", + SUBSCRIPTION_ID, + ), + kind: "CognitiveServices", + expectedName: "Azure AI Services", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockContext := mocks.NewMockContext(context.Background()) + resourceService := azapi.NewResourceService( + mockContext.SubscriptionCredentialProvider, + mockContext.ArmClientOptions, + ) + deploymentService := mockazapi.NewStandardDeploymentsFromMockContext(mockContext) + + mockContext.HttpClient.When(func(request *http.Request) bool { + return request.Method == http.MethodGet && + strings.Contains(request.URL.Path, "/Microsoft.CognitiveServices/accounts/") + }).RespondFn(func(request *http.Request) (*http.Response, error) { + response := map[string]interface{}{ + "id": tt.resourceId, + "name": "test-resource", + "type": "Microsoft.CognitiveServices/accounts", + "location": "eastus2", + "kind": tt.kind, + } + + body, err := json.Marshal(response) + if err != nil { + return nil, err + } + + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader(body)), + Request: &http.Request{ + Method: http.MethodGet, + URL: request.URL, + }, + }, nil + }) + + arm := NewAzureResourceManager(resourceService, deploymentService) + displayName, err := arm.GetResourceTypeDisplayName( + *mockContext.Context, + SUBSCRIPTION_ID, + tt.resourceId, + azapi.AzureResourceTypeCognitiveServiceAccount, + ) + + require.NoError(t, err) + require.Equal(t, tt.expectedName, displayName) + }) + } +} + func TestGetResourceTypeDisplayNameForRedisEnterprise(t *testing.T) { const SUBSCRIPTION_ID = "273f1e6b-6c19-4c9e-8b67-5fbe78b14063" From 19da31c0c4f68a496ef7c370dc77b101752a6315 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Dec 2025 00:43:41 +0000 Subject: [PATCH 3/5] Address PR feedback: improve Foundry resource detection - Change AIServices check from exact match to strings.Contains for consistency with other checks - Add test case for AIHub kind returning "Foundry" - Add test case using AzureResourceTypeCognitiveServiceAccountProject constant - Add support for capabilityHosts resource type (Microsoft.CognitiveServices/accounts/capabilityHosts) - Display capabilityHosts as "Foundry capability host" Co-authored-by: vhvb1989 <24213737+vhvb1989@users.noreply.github.com> --- cli/azd/pkg/azapi/azure_resource_types.go | 10 ++++-- cli/azd/pkg/infra/azure_resource_manager.go | 2 +- .../pkg/infra/azure_resource_manager_test.go | 31 +++++++++++++++++-- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/cli/azd/pkg/azapi/azure_resource_types.go b/cli/azd/pkg/azapi/azure_resource_types.go index c08e0c96326..9e1476db0b3 100644 --- a/cli/azd/pkg/azapi/azure_resource_types.go +++ b/cli/azd/pkg/azapi/azure_resource_types.go @@ -50,9 +50,13 @@ const ( AzureResourceTypeRoleAssignment AzureResourceType = "Microsoft.Authorization/roleAssignments" //nolint:lll - AzureResourceTypeMachineLearningEndpoint AzureResourceType = "Microsoft.MachineLearningServices/workspaces/onlineEndpoints" + AzureResourceTypeMachineLearningEndpoint AzureResourceType = "Microsoft.MachineLearningServices/workspaces/onlineEndpoints" + //nolint:lll AzureResourceTypeCognitiveServiceAccountDeployment AzureResourceType = "Microsoft.CognitiveServices/accounts/deployments" - AzureResourceTypeCognitiveServiceAccountProject AzureResourceType = "Microsoft.CognitiveServices/accounts/projects" + //nolint:lll + AzureResourceTypeCognitiveServiceAccountProject AzureResourceType = "Microsoft.CognitiveServices/accounts/projects" + //nolint:lll + AzureResourceTypeCognitiveServiceAccountCapabilityHost AzureResourceType = "Microsoft.CognitiveServices/accounts/capabilityHosts" ) // GetResourceTypeDisplayName retrieves the display name for the given resource type. @@ -126,6 +130,8 @@ func GetResourceTypeDisplayName(resourceType AzureResourceType) string { return "Azure AI Services Model Deployment" case AzureResourceTypeCognitiveServiceAccountProject: return "Foundry project" + case AzureResourceTypeCognitiveServiceAccountCapabilityHost: + return "Foundry capability host" case AzureResourceTypeSearchService: return "Search service" case AzureResourceTypeVideoIndexer: diff --git a/cli/azd/pkg/infra/azure_resource_manager.go b/cli/azd/pkg/infra/azure_resource_manager.go index 5427889ab05..ec47b1ca25a 100644 --- a/cli/azd/pkg/infra/azure_resource_manager.go +++ b/cli/azd/pkg/infra/azure_resource_manager.go @@ -295,7 +295,7 @@ func (rm *AzureResourceManager) getCognitiveServiceResourceTypeDisplayName( return "Document Intelligence", nil } else if strings.Contains(resource.Kind, "AIHub") { return "Foundry", nil - } else if resource.Kind == "AIServices" { + } else if strings.Contains(resource.Kind, "AIServices") { return "Foundry", nil } else { return "Azure AI Services", nil diff --git a/cli/azd/pkg/infra/azure_resource_manager_test.go b/cli/azd/pkg/infra/azure_resource_manager_test.go index a0191ee2749..c8750d3e0aa 100644 --- a/cli/azd/pkg/infra/azure_resource_manager_test.go +++ b/cli/azd/pkg/infra/azure_resource_manager_test.go @@ -518,6 +518,7 @@ func TestGetResourceTypeDisplayNameForCognitiveServices(t *testing.T) { tests := []struct { name string resourceId string + resourceType azapi.AzureResourceType kind string expectedName string }{ @@ -527,6 +528,7 @@ func TestGetResourceTypeDisplayNameForCognitiveServices(t *testing.T) { "/subscriptions/%s/resourceGroups/test-rg/providers/Microsoft.CognitiveServices/accounts/test-openai", SUBSCRIPTION_ID, ), + resourceType: azapi.AzureResourceTypeCognitiveServiceAccount, kind: "OpenAI", expectedName: "Azure OpenAI", }, @@ -537,6 +539,7 @@ func TestGetResourceTypeDisplayNameForCognitiveServices(t *testing.T) { "Microsoft.CognitiveServices/accounts/test-formrecognizer", SUBSCRIPTION_ID, ), + resourceType: azapi.AzureResourceTypeCognitiveServiceAccount, kind: "FormRecognizer", expectedName: "Document Intelligence", }, @@ -546,16 +549,39 @@ func TestGetResourceTypeDisplayNameForCognitiveServices(t *testing.T) { "/subscriptions/%s/resourceGroups/test-rg/providers/Microsoft.CognitiveServices/accounts/test-foundry", SUBSCRIPTION_ID, ), + resourceType: azapi.AzureResourceTypeCognitiveServiceAccount, kind: "AIServices", expectedName: "Foundry", }, { - name: "Foundry project", + name: "Foundry (AIHub)", + resourceId: fmt.Sprintf( + "/subscriptions/%s/resourceGroups/test-rg/providers/Microsoft.CognitiveServices/accounts/test-aihub", + SUBSCRIPTION_ID, + ), + resourceType: azapi.AzureResourceTypeCognitiveServiceAccount, + kind: "AIHub", + expectedName: "Foundry", + }, + { + name: "Foundry project (account resource type)", + resourceId: fmt.Sprintf( + "/subscriptions/%s/resourceGroups/test-rg/providers/"+ + "Microsoft.CognitiveServices/accounts/test-foundry/projects/test-project", + SUBSCRIPTION_ID, + ), + resourceType: azapi.AzureResourceTypeCognitiveServiceAccount, + kind: "AIServices", + expectedName: "Foundry project", + }, + { + name: "Foundry project (project resource type)", resourceId: fmt.Sprintf( "/subscriptions/%s/resourceGroups/test-rg/providers/"+ "Microsoft.CognitiveServices/accounts/test-foundry/projects/test-project", SUBSCRIPTION_ID, ), + resourceType: azapi.AzureResourceTypeCognitiveServiceAccountProject, kind: "AIServices", expectedName: "Foundry project", }, @@ -565,6 +591,7 @@ func TestGetResourceTypeDisplayNameForCognitiveServices(t *testing.T) { "/subscriptions/%s/resourceGroups/test-rg/providers/Microsoft.CognitiveServices/accounts/test-cogservices", SUBSCRIPTION_ID, ), + resourceType: azapi.AzureResourceTypeCognitiveServiceAccount, kind: "CognitiveServices", expectedName: "Azure AI Services", }, @@ -611,7 +638,7 @@ func TestGetResourceTypeDisplayNameForCognitiveServices(t *testing.T) { *mockContext.Context, SUBSCRIPTION_ID, tt.resourceId, - azapi.AzureResourceTypeCognitiveServiceAccount, + tt.resourceType, ) require.NoError(t, err) From 2cb8491b01bf19fd1476a8463fd7ccdff2e9525b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Dec 2025 01:53:45 +0000 Subject: [PATCH 4/5] Optimize project resource type handling - Handle AzureResourceTypeCognitiveServiceAccountProject directly in GetResourceTypeDisplayName - Return "Foundry project" immediately without API call when resource type is already known to be a project - Keep path-based check in getCognitiveServiceResourceTypeDisplayName for cases where resource type is account but ID points to project - Improves performance by avoiding unnecessary API calls for project resources Co-authored-by: JeffreyCA <9157833+JeffreyCA@users.noreply.github.com> --- cli/azd/pkg/infra/azure_resource_manager.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cli/azd/pkg/infra/azure_resource_manager.go b/cli/azd/pkg/infra/azure_resource_manager.go index ec47b1ca25a..89b6303cc12 100644 --- a/cli/azd/pkg/infra/azure_resource_manager.go +++ b/cli/azd/pkg/infra/azure_resource_manager.go @@ -226,6 +226,11 @@ func (rm *AzureResourceManager) GetResourceTypeDisplayName( } } else if resourceType == azapi.AzureResourceTypeCognitiveServiceAccount || resourceType == azapi.AzureResourceTypeCognitiveServiceAccountProject { + // For project resources, we can return the display name directly + if resourceType == azapi.AzureResourceTypeCognitiveServiceAccountProject { + return "Foundry project", nil + } + resourceTypeDisplayName, err := rm.getCognitiveServiceResourceTypeDisplayName(ctx, subscriptionId, resourceId) if err != nil { @@ -278,7 +283,9 @@ func (rm *AzureResourceManager) getCognitiveServiceResourceTypeDisplayName( subscriptionId string, resourceId string, ) (string, error) { - // Check if this is a Foundry project resource (child resource) + // Check if this is a Foundry project resource (child resource) based on path + // This handles cases where the resource type is Microsoft.CognitiveServices/accounts + // but the resource ID points to a project if strings.Contains(resourceId, "/projects/") { return "Foundry project", nil } From 227ed0fd7e7d25643115d7bc5914eb2c7cc0139b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Dec 2025 17:13:35 +0000 Subject: [PATCH 5/5] Simplify Foundry resource type handling - Remove AzureResourceTypeCognitiveServiceAccountProject from condition routing - Let project resources use static display name mapping via fallback path - Remove path-based check for projects (not needed since resource type is correct) - Remove redundant test case (project resources always have correct type) - Cleaner, simpler code that relies on static mappings Co-authored-by: JeffreyCA <9157833+JeffreyCA@users.noreply.github.com> --- cli/azd/pkg/infra/azure_resource_manager.go | 15 +-------------- cli/azd/pkg/infra/azure_resource_manager_test.go | 13 +------------ 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/cli/azd/pkg/infra/azure_resource_manager.go b/cli/azd/pkg/infra/azure_resource_manager.go index 89b6303cc12..a3d6e5773fa 100644 --- a/cli/azd/pkg/infra/azure_resource_manager.go +++ b/cli/azd/pkg/infra/azure_resource_manager.go @@ -224,13 +224,7 @@ func (rm *AzureResourceManager) GetResourceTypeDisplayName( } else { return resourceTypeDisplayName, nil } - } else if resourceType == azapi.AzureResourceTypeCognitiveServiceAccount || - resourceType == azapi.AzureResourceTypeCognitiveServiceAccountProject { - // For project resources, we can return the display name directly - if resourceType == azapi.AzureResourceTypeCognitiveServiceAccountProject { - return "Foundry project", nil - } - + } else if resourceType == azapi.AzureResourceTypeCognitiveServiceAccount { resourceTypeDisplayName, err := rm.getCognitiveServiceResourceTypeDisplayName(ctx, subscriptionId, resourceId) if err != nil { @@ -283,13 +277,6 @@ func (rm *AzureResourceManager) getCognitiveServiceResourceTypeDisplayName( subscriptionId string, resourceId string, ) (string, error) { - // Check if this is a Foundry project resource (child resource) based on path - // This handles cases where the resource type is Microsoft.CognitiveServices/accounts - // but the resource ID points to a project - if strings.Contains(resourceId, "/projects/") { - return "Foundry project", nil - } - resource, err := rm.resourceService.GetResource(ctx, subscriptionId, resourceId, cognitiveServiceApiVersion) if err != nil { diff --git a/cli/azd/pkg/infra/azure_resource_manager_test.go b/cli/azd/pkg/infra/azure_resource_manager_test.go index c8750d3e0aa..bf2a692b855 100644 --- a/cli/azd/pkg/infra/azure_resource_manager_test.go +++ b/cli/azd/pkg/infra/azure_resource_manager_test.go @@ -564,18 +564,7 @@ func TestGetResourceTypeDisplayNameForCognitiveServices(t *testing.T) { expectedName: "Foundry", }, { - name: "Foundry project (account resource type)", - resourceId: fmt.Sprintf( - "/subscriptions/%s/resourceGroups/test-rg/providers/"+ - "Microsoft.CognitiveServices/accounts/test-foundry/projects/test-project", - SUBSCRIPTION_ID, - ), - resourceType: azapi.AzureResourceTypeCognitiveServiceAccount, - kind: "AIServices", - expectedName: "Foundry project", - }, - { - name: "Foundry project (project resource type)", + name: "Foundry project", resourceId: fmt.Sprintf( "/subscriptions/%s/resourceGroups/test-rg/providers/"+ "Microsoft.CognitiveServices/accounts/test-foundry/projects/test-project",