Skip to content

Commit f2582c2

Browse files
JeffreyCAvhvb1989
andauthored
compose - AI Foundry 1RP changes (#5157)
Composability changes to support AI Foundry's new 1RP - basic setup (no capability host). During the first provision, azd will check quota for all Foundry models added to the project and will prompt for a single location. Those models get deployed under a single AI Services account in that location. With 1RP, there is still a concept of a project, but it's now a child resource of the AI Services account. --------- Co-authored-by: Victor Vazquez <vhvb1989@gmail.com>
1 parent 8066369 commit f2582c2

File tree

9 files changed

+221
-260
lines changed

9 files changed

+221
-260
lines changed

cli/azd/internal/scaffold/funcs.go

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"regexp"
1111
"strings"
1212

13-
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
13+
"github.com/tidwall/gjson"
1414
)
1515

1616
// BicepName returns a name suitable for use as a bicep variable name.
@@ -267,27 +267,22 @@ func hostFromEndpoint(endpoint string) (string, error) {
267267
return urlEndpoint.Hostname(), nil
268268
}
269269

270-
func aiProjectConnectionString(resourceId string, projectUrl string) (string, error) {
271-
hostName, err := hostFromEndpoint(projectUrl)
272-
if err != nil {
273-
return "", fmt.Errorf("failed to parse project URL: %w", err)
270+
func aiProjectEndpoint(endpoints string) (string, error) {
271+
result := gjson.Parse(endpoints)
272+
if !result.Exists() {
273+
return "", fmt.Errorf("invalid endpoints JSON: %s", endpoints)
274274
}
275275

276-
resId, err := arm.ParseResourceID(resourceId)
277-
if err != nil {
278-
return "", nil
276+
endpoint := result.Get("AI Foundry API")
277+
if !endpoint.Exists() {
278+
return "", fmt.Errorf("endpoint 'AI Foundry API' not found in endpoints")
279279
}
280280

281-
return fmt.Sprintf("%s;%s;%s;%s", hostName, resId.SubscriptionID, resId.ResourceGroupName, resId.Name), nil
281+
return endpoint.String(), nil
282282
}
283283

284-
func emitAiProjectConnectionString(resourceIdVar string, projectUrlVar string) (string, error) {
285-
return fmt.Sprintf(
286-
"${split(%s, '/')[2]};${split(%s, '/')[2]};${split(%s, '/')[4]};${split(%s, '/')[8]}",
287-
projectUrlVar,
288-
resourceIdVar,
289-
resourceIdVar,
290-
resourceIdVar), nil
284+
func emitAiProjectEndpoint(projectEndpointsVar string) (string, error) {
285+
return fmt.Sprintf("%s['AI Foundry API']", projectEndpointsVar), nil
291286
}
292287

293288
func emitHostFromEndpoint(endpointVar string) (string, error) {

cli/azd/internal/scaffold/resource_expr_eval.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ type FuncMap map[string]any
4242
// The functions are evaluated at runtime against live Azure resources.
4343
func BaseEvalFuncMap() FuncMap {
4444
return FuncMap{
45-
"lower": strings.ToLower,
46-
"upper": strings.ToUpper,
47-
"replace": strings.ReplaceAll,
48-
"host": hostFromEndpoint,
49-
"aiProjectConnectionString": aiProjectConnectionString,
45+
"lower": strings.ToLower,
46+
"upper": strings.ToUpper,
47+
"replace": strings.ReplaceAll,
48+
"host": hostFromEndpoint,
49+
"aiProjectEndpoint": aiProjectEndpoint,
5050
}
5151
}
5252

@@ -57,11 +57,11 @@ func BaseEvalFuncMap() FuncMap {
5757
// The functions are not evaluated at runtime, but rather emitted as part of the Bicep template.
5858
func BaseEmitBicepFuncMap() FuncMap {
5959
return FuncMap{
60-
"lower": bicepFuncCall("toLower"),
61-
"upper": bicepFuncCall("toUpper"),
62-
"replace": bicepFuncCallThree("replace"),
63-
"host": emitHostFromEndpoint,
64-
"aiProjectConnectionString": emitAiProjectConnectionString,
60+
"lower": bicepFuncCall("toLower"),
61+
"upper": bicepFuncCall("toUpper"),
62+
"replace": bicepFuncCallThree("replace"),
63+
"host": emitHostFromEndpoint,
64+
"aiProjectEndpoint": emitAiProjectEndpoint,
6565
}
6666
}
6767

cli/azd/internal/scaffold/resource_meta.go

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,42 @@ var Resources = []ResourceMeta{
9191
},
9292
{
9393
ResourceType: "Microsoft.CognitiveServices/accounts",
94-
ApiVersion: "2023-05-01",
94+
ApiVersion: "2025-04-01-preview",
9595
},
9696
{
9797
ResourceType: "Microsoft.CognitiveServices/accounts/deployments",
98-
ApiVersion: "2023-05-01",
98+
ApiVersion: "2025-04-01-preview",
9999
ParentForEval: "Microsoft.CognitiveServices/accounts",
100100
StandardVarPrefix: "AZURE_OPENAI",
101101
Variables: map[string]string{
102102
"endpoint": "${.properties.endpoint}",
103103
},
104104
},
105+
{
106+
ResourceType: "Microsoft.CognitiveServices/accounts/projects",
107+
ResourceKind: "AIServices",
108+
ApiVersion: "2025-04-01-preview",
109+
StandardVarPrefix: "AZURE_AI_PROJECT",
110+
Variables: map[string]string{
111+
"endpoint": "${aiProjectEndpoint .properties.endpoints}",
112+
},
113+
RoleAssignments: RoleAssignments{
114+
Write: []RoleAssignment{
115+
{
116+
Name: "AzureAIDeveloper",
117+
RoleDefinitionName: "Azure AI Developer",
118+
RoleDefinitionId: "64702f94-c441-49e6-a78b-ef80e0188fee",
119+
Scope: RoleAssignmentScopeGroup,
120+
},
121+
{
122+
Name: "CognitiveServicesUser",
123+
RoleDefinitionName: "Cognitive Services User",
124+
RoleDefinitionId: "a97b65f3-24c7-4388-baec-2e87135dc908",
125+
Scope: RoleAssignmentScopeGroup,
126+
},
127+
},
128+
},
129+
},
105130
{
106131
ResourceType: "Microsoft.ContainerRegistry/registries",
107132
ApiVersion: "2023-06-01-preview",
@@ -235,31 +260,11 @@ var Resources = []ResourceMeta{
235260
},
236261
},
237262
{
238-
ResourceType: "Microsoft.MachineLearningServices/workspaces",
239-
ResourceKind: "Project",
240-
ApiVersion: "2024-10-01",
241-
StandardVarPrefix: "AZURE_AI_PROJECT",
242-
Variables: map[string]string{
243-
"connectionString": "${aiProjectConnectionString .id .properties.discoveryUrl}",
244-
},
245-
RoleAssignments: RoleAssignments{
246-
Write: []RoleAssignment{
247-
{
248-
Name: "AIDeveloper",
249-
RoleDefinitionName: "Azure AI Developer",
250-
RoleDefinitionId: "64702f94-c441-49e6-a78b-ef80e0188fee",
251-
Scope: RoleAssignmentScopeGroup,
252-
},
253-
},
254-
},
255-
},
256-
{
257-
ResourceType: "Microsoft.Search/searchServices",
258-
// TODO: Switch to 2025-02-01-preview once available, which has a new 'endpoint' property
259-
ApiVersion: "2024-06-01-preview",
263+
ResourceType: "Microsoft.Search/searchServices",
264+
ApiVersion: "2025-02-01-preview",
260265
StandardVarPrefix: "AZURE_AI_SEARCH",
261266
Variables: map[string]string{
262-
"endpoint": "https://${.name}.search.windows.net",
267+
"endpoint": "${.properties.endpoint}",
263268
},
264269
RoleAssignments: RoleAssignments{
265270
Write: []RoleAssignment{

cli/azd/pkg/azapi/azure_resource_types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const (
4343
AzureResourceTypeDevCenterProject AzureResourceType = "Microsoft.DevCenter/projects"
4444
AzureResourceTypeMachineLearningWorkspace AzureResourceType = "Microsoft.MachineLearningServices/workspaces"
4545
AzureResourceTypeMachineLearningConnection AzureResourceType = "Microsoft.MachineLearningServices/workspaces/connections"
46+
AzureResourceTypeRoleAssignment AzureResourceType = "Microsoft.Authorization/roleAssignments"
4647

4748
//nolint:lll
4849
AzureResourceTypeMachineLearningEndpoint AzureResourceType = "Microsoft.MachineLearningServices/workspaces/onlineEndpoints"

cli/azd/pkg/project/resources.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func (r ResourceType) AzureResourceType() string {
115115
case ResourceTypeKeyVault:
116116
return "Microsoft.KeyVault/vaults"
117117
case ResourceTypeAiProject:
118-
return "Microsoft.MachineLearningServices/workspaces"
118+
return "Microsoft.CognitiveServices/accounts/projects"
119119
case ResourceTypeAiSearch:
120120
return "Microsoft.Search/searchServices"
121121
}
Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,55 @@
1-
@description('The name of the AI Hub')
2-
param aiHubName string
1+
@description('The name of the AI Services account')
2+
param aiServicesName string
3+
4+
@description('The name of the AI Services project')
5+
param aiServicesProjectName string
36

47
@description('The name of the AI Search')
58
param aiSearchName string
69

7-
resource search 'Microsoft.Search/searchServices@2024-06-01-preview' existing = {
10+
resource search 'Microsoft.Search/searchServices@2025-02-01-preview' existing = {
811
name: aiSearchName
912
}
1013

11-
resource hub 'Microsoft.MachineLearningServices/workspaces@2024-10-01' existing = {
12-
name: aiHubName
13-
14-
resource AzureAISearch 'connections@2024-10-01' = {
15-
name: 'AzureAISearch-connection'
16-
properties: {
17-
category: 'CognitiveSearch'
18-
target: 'https://${search.name}.search.windows.net'
19-
authType: 'ApiKey'
20-
isSharedToAll: true
21-
credentials: {
22-
key: search.listAdminKeys().primaryKey
23-
}
24-
metadata: {
25-
ApiType: 'Azure'
26-
ResourceId: search.id
14+
resource aiServices 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = {
15+
name: aiServicesName
16+
17+
resource project 'projects' existing = {
18+
name: aiServicesProjectName
19+
20+
resource AzureAISearch 'connections' = {
21+
name: 'AzureAISearch-connection'
22+
properties: {
23+
category: 'CognitiveSearch'
24+
target: search.properties.endpoint
25+
authType: 'AAD'
26+
isSharedToAll: true
27+
metadata: {
28+
ApiType: 'Azure'
29+
ResourceId: search.id
30+
location: search.location
31+
}
2732
}
2833
}
2934
}
3035
}
36+
37+
resource projectSearchIndexDataContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
38+
scope: search
39+
name: guid(subscription().id, resourceGroup().id, aiServices::project.id, '8ebe5a00-799e-43f5-93ac-243d3dce84a7')
40+
properties: {
41+
principalId: aiServices::project.identity.principalId
42+
principalType: 'ServicePrincipal'
43+
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7')
44+
}
45+
}
46+
47+
resource projectSearchServiceContributorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
48+
scope: search
49+
name: guid(subscription().id, resourceGroup().id, aiServices::project.id, '7ca78c08-252a-4471-8644-bb5ff32d4ba0')
50+
properties: {
51+
principalId: aiServices::project.identity.principalId
52+
principalType: 'ServicePrincipal'
53+
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')
54+
}
55+
}

0 commit comments

Comments
 (0)