@@ -22,6 +22,21 @@ var appServicePlanName = 'az-${resourcePrefix}-plan-${resourceToken}'
2222// App Service name
2323var appServiceName = 'az-${resourcePrefix }-app-${resourceToken }'
2424
25+ // Azure AI Hub name
26+ var aiHubName = 'az-${resourcePrefix }-hub-${resourceToken }'
27+
28+ // Azure AI Project name
29+ var aiProjectName = 'az-${resourcePrefix }-project-${resourceToken }'
30+
31+ // Azure OpenAI account name
32+ var openAIAccountName = 'az-${resourcePrefix }-openai-${resourceToken }'
33+
34+ // Storage account name (required for AI Hub)
35+ var storageAccountName = 'az${resourcePrefix }st${take (resourceToken , 8 )}'
36+
37+ // Key Vault name (required for AI Hub)
38+ var keyVaultName = 'az-${resourcePrefix }-kv-${take (resourceToken , 8 )}'
39+
2540// Create App Service Plan (P0V3 Linux)
2641resource appServicePlan 'Microsoft.Web/serverfarms@2024-04-01' = {
2742 name : appServicePlanName
@@ -83,6 +98,22 @@ resource appService 'Microsoft.Web/sites@2024-04-01' = {
8398 name : 'PYTHONPATH'
8499 value : '/home/site/wwwroot'
85100 }
101+ {
102+ name : 'AZURE_OPENAI_ENDPOINT'
103+ value : openAIAccount .properties .endpoint
104+ }
105+ {
106+ name : 'AZURE_OPENAI_DEPLOYMENT_NAME'
107+ value : gpt41MiniDeployment .name
108+ }
109+ {
110+ name : 'AZURE_AI_PROJECT_ENDPOINT'
111+ value : 'https://${aiProject .properties .discoveryUrl }'
112+ }
113+ {
114+ name : 'AZURE_APP_SERVICE_URL'
115+ value : 'https://${appServiceName }.azurewebsites.net'
116+ }
86117 ]
87118 cors : {
88119 allowedOrigins : ['*' ]
@@ -93,6 +124,190 @@ resource appService 'Microsoft.Web/sites@2024-04-01' = {
93124 }
94125}
95126
127+ // Create Storage Account (required for AI Hub)
128+ resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = {
129+ name : storageAccountName
130+ location : location
131+ tags : {
132+ 'azd-env-name' : environmentName
133+ }
134+ sku : {
135+ name : 'Standard_LRS'
136+ }
137+ kind : 'StorageV2'
138+ properties : {
139+ accessTier : 'Hot'
140+ allowBlobPublicAccess : false
141+ allowSharedKeyAccess : false
142+ minimumTlsVersion : 'TLS1_2'
143+ }
144+ }
145+
146+ // Create Key Vault (required for AI Hub)
147+ resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
148+ name : keyVaultName
149+ location : location
150+ tags : {
151+ 'azd-env-name' : environmentName
152+ }
153+ properties : {
154+ sku : {
155+ family : 'A'
156+ name : 'standard'
157+ }
158+ tenantId : tenant ().tenantId
159+ enableRbacAuthorization : true
160+ enableSoftDelete : true
161+ enablePurgeProtection : true
162+ accessPolicies : []
163+ }
164+ }
165+
166+ // Create Azure OpenAI Account
167+ resource openAIAccount 'Microsoft.CognitiveServices/accounts@2024-10-01' = {
168+ name : openAIAccountName
169+ location : location
170+ tags : {
171+ 'azd-env-name' : environmentName
172+ }
173+ kind : 'OpenAI'
174+ sku : {
175+ name : 'S0'
176+ }
177+ identity : {
178+ type : 'SystemAssigned'
179+ }
180+ properties : {
181+ customSubDomainName : openAIAccountName
182+ publicNetworkAccess : 'Enabled'
183+ disableLocalAuth : false
184+ }
185+ }
186+
187+ // Create GPT-4.1-mini deployment
188+ resource gpt41MiniDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = {
189+ parent : openAIAccount
190+ name : 'gpt-4-1-mini'
191+ sku : {
192+ name : 'GlobalStandard'
193+ capacity : 150
194+ }
195+ properties : {
196+ model : {
197+ format : 'OpenAI'
198+ name : 'gpt-4.1-mini'
199+ version : '2025-04-14'
200+ }
201+ versionUpgradeOption : 'OnceCurrentVersionExpired'
202+ raiPolicyName : 'Microsoft.DefaultV2'
203+ }
204+ }
205+
206+ // Create Azure AI Hub (workspace)
207+ resource aiHub 'Microsoft.MachineLearningServices/workspaces@2024-10-01' = {
208+ name : aiHubName
209+ location : location
210+ tags : {
211+ 'azd-env-name' : environmentName
212+ }
213+ identity : {
214+ type : 'SystemAssigned'
215+ }
216+ sku : {
217+ name : 'Basic'
218+ }
219+ kind : 'Hub'
220+ properties : {
221+ friendlyName : 'Todo MCP AI Hub'
222+ description : 'AI Hub for Todo MCP Agent chat functionality'
223+ keyVault : keyVault .id
224+ storageAccount : storageAccount .id
225+ hbiWorkspace : false
226+ publicNetworkAccess : 'Enabled'
227+ }
228+ }
229+
230+ // Create Azure AI Project
231+ resource aiProject 'Microsoft.MachineLearningServices/workspaces@2024-10-01' = {
232+ name : aiProjectName
233+ location : location
234+ tags : {
235+ 'azd-env-name' : environmentName
236+ }
237+ identity : {
238+ type : 'SystemAssigned'
239+ }
240+ sku : {
241+ name : 'Basic'
242+ }
243+ kind : 'Project'
244+ properties : {
245+ friendlyName : 'Todo MCP AI Project'
246+ description : 'AI Project for Todo MCP Agent chat functionality'
247+ hubResourceId : aiHub .id
248+ publicNetworkAccess : 'Enabled'
249+ }
250+ }
251+
252+ // Create AI Hub connection to Azure OpenAI
253+ resource aiHubOpenAIConnection 'Microsoft.MachineLearningServices/workspaces/connections@2024-10-01' = {
254+ parent : aiHub
255+ name : 'openai-connection'
256+ properties : {
257+ authType : 'AAD'
258+ category : 'AzureOpenAI'
259+ target : openAIAccount .properties .endpoint
260+ metadata : {
261+ ApiType : 'Azure'
262+ ResourceId : openAIAccount .id
263+ }
264+ }
265+ }
266+
267+ // Grant App Service access to OpenAI
268+ resource appServiceOpenAIRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
269+ name : guid (subscription ().id , appService .id , openAIAccount .id , 'CognitiveServicesOpenAIUser' )
270+ scope : openAIAccount
271+ properties : {
272+ roleDefinitionId : subscriptionResourceId ('Microsoft.Authorization/roleDefinitions' , '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' ) // Cognitive Services OpenAI User
273+ principalId : appService .identity .principalId
274+ principalType : 'ServicePrincipal'
275+ }
276+ }
277+
278+ // Grant App Service access to AI Project
279+ resource appServiceAIProjectRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
280+ name : guid (subscription ().id , appService .id , aiProject .id , 'AzureMLDataScientist' )
281+ scope : aiProject
282+ properties : {
283+ roleDefinitionId : subscriptionResourceId ('Microsoft.Authorization/roleDefinitions' , 'f6c7c914-8db3-469d-8ca1-694a8f32e121' ) // AzureML Data Scientist
284+ principalId : appService .identity .principalId
285+ principalType : 'ServicePrincipal'
286+ }
287+ }
288+
289+ // Grant AI Hub access to OpenAI (for Azure AI Foundry integration)
290+ resource aiHubOpenAIRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
291+ name : guid (subscription ().id , aiHub .id , openAIAccount .id , 'CognitiveServicesOpenAIContributor' )
292+ scope : openAIAccount
293+ properties : {
294+ roleDefinitionId : subscriptionResourceId ('Microsoft.Authorization/roleDefinitions' , 'a001fd3d-188f-4b5d-821b-7da978bf7442' ) // Cognitive Services OpenAI Contributor
295+ principalId : aiHub .identity .principalId
296+ principalType : 'ServicePrincipal'
297+ }
298+ }
299+
300+ // Grant AI Project access to OpenAI (for Azure AI Foundry integration)
301+ resource aiProjectOpenAIRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
302+ name : guid (subscription ().id , aiProject .id , openAIAccount .id , 'CognitiveServicesOpenAIContributor' )
303+ scope : openAIAccount
304+ properties : {
305+ roleDefinitionId : subscriptionResourceId ('Microsoft.Authorization/roleDefinitions' , 'a001fd3d-188f-4b5d-821b-7da978bf7442' ) // Cognitive Services OpenAI Contributor
306+ principalId : aiProject .identity .principalId
307+ principalType : 'ServicePrincipal'
308+ }
309+ }
310+
96311// Outputs
97312output RESOURCE_GROUP_ID string = resourceGroup ().id
98313output AZURE_LOCATION string = location
@@ -101,3 +316,9 @@ output AZURE_RESOURCE_GROUP string = resourceGroup().name
101316output SERVICE_WEB_NAME string = appService .name
102317output SERVICE_WEB_URI string = 'https://${appService .properties .defaultHostName }'
103318output SERVICE_TODO_APP_IDENTITY_PRINCIPAL_ID string = appService .identity .principalId
319+ output AZURE_OPENAI_ENDPOINT string = openAIAccount .properties .endpoint
320+ output AZURE_OPENAI_NAME string = openAIAccount .name
321+ output AZURE_AI_PROJECT_ENDPOINT string = 'https://${aiProject .properties .discoveryUrl }'
322+ output AZURE_AI_PROJECT_NAME string = aiProject .name
323+ output AZURE_AI_HUB_NAME string = aiHub .name
324+ output AZURE_OPENAI_DEPLOYMENT_NAME string = gpt41MiniDeployment .name
0 commit comments