From 0fc0fef331a178b5273b4d43a01b2513e75ec7fe Mon Sep 17 00:00:00 2001 From: Linda Li <139801625+lindazqli@users.noreply.github.com> Date: Sun, 12 Apr 2026 15:50:04 -0700 Subject: [PATCH 1/2] fix: add OAuth, audience, connectorName fields to ConnectionConfig type Add missing fields to the Bicep ConnectionConfig type so that all connection properties defined in azure.yaml are passed through to the ARM API without being silently dropped: - authorizationUrl / tokenUrl / refreshUrl / scopes (OAuth2 flows) - audience (UserEntraToken / AgenticIdentity auth types) - connectorName (managed OAuth connectors) Also extend the authType union to include all values the REST API accepts: UserEntraToken, AgenticIdentity, ManagedIdentity, ServicePrincipal, UsernamePassword, AccessKey, AccountKey, SAS. --- infra/core/ai/connection.bicep | 200 ++++++++++++++++++--------------- 1 file changed, 112 insertions(+), 88 deletions(-) diff --git a/infra/core/ai/connection.bicep b/infra/core/ai/connection.bicep index d002097..8af16cf 100644 --- a/infra/core/ai/connection.bicep +++ b/infra/core/ai/connection.bicep @@ -1,88 +1,112 @@ -targetScope = 'resourceGroup' - -@description('AI Services account name') -param aiServicesAccountName string - -@description('AI project name') -param aiProjectName string - -// Connection configuration type definition -type ConnectionConfig = { - @description('Name of the connection') - name: string - - @description('Category of the connection (e.g., ContainerRegistry, AzureStorageAccount, CognitiveSearch, AzureOpenAI)') - category: string - - @description('Target endpoint or URL for the connection') - target: string - - @description('Authentication type') - authType: 'AAD' | 'AccessKey' | 'AccountKey' | 'ApiKey' | 'CustomKeys' | 'ManagedIdentity' | 'None' | 'OAuth2' | 'PAT' | 'SAS' | 'ServicePrincipal' | 'UsernamePassword' - - @description('Whether the connection is shared to all users (optional, defaults to true)') - isSharedToAll: bool? - - @description('Additional metadata for the connection (optional)') - metadata: object? - - @description('Error message if the connection fails (optional)') - error: string? - - @description('Expiry time for the connection (optional)') - expiryTime: string? - - @description('Private endpoint requirement: Required, NotRequired, or NotApplicable (optional)') - peRequirement: ('NotApplicable' | 'NotRequired' | 'Required')? - - @description('Private endpoint status: Active, Inactive, or NotApplicable (optional)') - peStatus: ('Active' | 'Inactive' | 'NotApplicable')? - - @description('List of users to share the connection with (optional, alternative to isSharedToAll)') - sharedUserList: string[]? - - @description('Whether to use workspace managed identity (optional)') - useWorkspaceManagedIdentity: bool? -} - -@description('Connection configuration') -param connectionConfig ConnectionConfig - -@secure() -@description('Credentials for the connection. Kept as a separate @secure parameter to prevent secrets from appearing in deployment logs. Shape depends on authType — e.g. { key: "..." } for ApiKey, { clientId: "...", clientSecret: "..." } for OAuth2/ServicePrincipal.') -param credentials object = {} - - -// Get reference to the AI Services account and project -resource aiAccount 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { - name: aiServicesAccountName - - resource project 'projects' existing = { - name: aiProjectName - } -} - -// Create the connection -resource connection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = { - parent: aiAccount::project - name: connectionConfig.name - properties: { - category: connectionConfig.category - target: connectionConfig.target - authType: connectionConfig.authType - isSharedToAll: connectionConfig.?isSharedToAll ?? true - credentials: !empty(credentials) ? credentials : null - metadata: connectionConfig.?metadata - // Only include if they appear in the connectionConfig - ...connectionConfig.?error != null ? { error: connectionConfig.?error } : {} - ...connectionConfig.?expiryTime != null ? { expiryTime: connectionConfig.?expiryTime } : {} - ...connectionConfig.?peRequirement != null ? { peRequirement: connectionConfig.?peRequirement } : {} - ...connectionConfig.?peStatus != null ? { peStatus: connectionConfig.?peStatus } : {} - ...connectionConfig.?sharedUserList != null ? { sharedUserList: connectionConfig.?sharedUserList } : {} - ...connectionConfig.?useWorkspaceManagedIdentity != null ? { useWorkspaceManagedIdentity: connectionConfig.?useWorkspaceManagedIdentity } : {} - } -} - -// Outputs -output connectionName string = connection.name -output connectionId string = connection.id +targetScope = 'resourceGroup' + +@description('AI Services account name') +param aiServicesAccountName string + +@description('AI project name') +param aiProjectName string + +// Connection configuration type definition +type ConnectionConfig = { + @description('Name of the connection') + name: string + + @description('Category of the connection (e.g., ContainerRegistry, AzureStorageAccount, CognitiveSearch, AzureOpenAI)') + category: string + + @description('Target endpoint or URL for the connection') + target: string + + @description('Authentication type') + authType: 'AAD' | 'AccessKey' | 'AccountKey' | 'AgenticIdentity' | 'ApiKey' | 'CustomKeys' | 'ManagedIdentity' | 'None' | 'OAuth2' | 'PAT' | 'SAS' | 'ServicePrincipal' | 'UsernamePassword' | 'UserEntraToken' + + @description('Whether the connection is shared to all users (optional, defaults to true)') + isSharedToAll: bool? + + @description('Additional metadata for the connection (optional)') + metadata: object? + + @description('Error message if the connection fails (optional)') + error: string? + + @description('Expiry time for the connection (optional)') + expiryTime: string? + + @description('Private endpoint requirement: Required, NotRequired, or NotApplicable (optional)') + peRequirement: ('NotApplicable' | 'NotRequired' | 'Required')? + + @description('Private endpoint status: Active, Inactive, or NotApplicable (optional)') + peStatus: ('Active' | 'Inactive' | 'NotApplicable')? + + @description('List of users to share the connection with (optional, alternative to isSharedToAll)') + sharedUserList: string[]? + + @description('Whether to use workspace managed identity (optional)') + useWorkspaceManagedIdentity: bool? + + @description('OAuth2 authorization endpoint URL (optional, OAuth2 authType only)') + authorizationUrl: string? + + @description('OAuth2 token endpoint URL (optional, OAuth2 authType only)') + tokenUrl: string? + + @description('OAuth2 refresh token endpoint URL (optional, OAuth2 authType only)') + refreshUrl: string? + + @description('OAuth2 scopes to request (optional, OAuth2 authType only)') + scopes: string[]? + + @description('Token audience for UserEntraToken / AgenticIdentity auth types (optional)') + audience: string? + + @description('Managed connector name for OAuth2 managed connectors (optional)') + connectorName: string? +} + +@description('Connection configuration') +param connectionConfig ConnectionConfig + +@secure() +@description('Credentials for the connection. Kept as a separate @secure parameter to prevent secrets from appearing in deployment logs. Shape depends on authType — e.g. { key: "..." } for ApiKey, { clientId: "...", clientSecret: "..." } for OAuth2/ServicePrincipal.') +param credentials object = {} + + +// Get reference to the AI Services account and project +resource aiAccount 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { + name: aiServicesAccountName + + resource project 'projects' existing = { + name: aiProjectName + } +} + +// Create the connection +resource connection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = { + parent: aiAccount::project + name: connectionConfig.name + properties: { + category: connectionConfig.category + target: connectionConfig.target + authType: connectionConfig.authType + isSharedToAll: connectionConfig.?isSharedToAll ?? true + credentials: !empty(credentials) ? credentials : null + metadata: connectionConfig.?metadata + // Only include if they appear in the connectionConfig + ...connectionConfig.?error != null ? { error: connectionConfig.?error } : {} + ...connectionConfig.?expiryTime != null ? { expiryTime: connectionConfig.?expiryTime } : {} + ...connectionConfig.?peRequirement != null ? { peRequirement: connectionConfig.?peRequirement } : {} + ...connectionConfig.?peStatus != null ? { peStatus: connectionConfig.?peStatus } : {} + ...connectionConfig.?sharedUserList != null ? { sharedUserList: connectionConfig.?sharedUserList } : {} + ...connectionConfig.?useWorkspaceManagedIdentity != null ? { useWorkspaceManagedIdentity: connectionConfig.?useWorkspaceManagedIdentity } : {} + ...connectionConfig.?authorizationUrl != null ? { authorizationUrl: connectionConfig.?authorizationUrl } : {} + ...connectionConfig.?tokenUrl != null ? { tokenUrl: connectionConfig.?tokenUrl } : {} + ...connectionConfig.?refreshUrl != null ? { refreshUrl: connectionConfig.?refreshUrl } : {} + ...connectionConfig.?scopes != null ? { scopes: connectionConfig.?scopes } : {} + ...connectionConfig.?audience != null ? { audience: connectionConfig.?audience } : {} + ...connectionConfig.?connectorName != null ? { connectorName: connectionConfig.?connectorName } : {} + } +} + +// Outputs +output connectionName string = connection.name +output connectionId string = connection.id From 0ecce2773f4f4941aebd94edfe01f54fbc68f913 Mon Sep 17 00:00:00 2001 From: Linda Li <139801625+lindazqli@users.noreply.github.com> Date: Mon, 13 Apr 2026 09:37:50 -0700 Subject: [PATCH 2/2] fix: preserve line endings, apply only functional changes