@@ -278,3 +278,322 @@ resource "azurerm_role_assignment" "search_project_contributor" {
278278 principal_id = azurerm_search_service. search . identity [0 ]. principal_id
279279 principal_type = " ServicePrincipal"
280280}
281+
282+ # Storage account permissions for Azure AI Foundry project
283+ resource "azurerm_role_assignment" "storage_blob_data_contributor_user" {
284+ scope = azapi_resource. storage . id
285+ role_definition_id = " /subscriptions/${ data . azurerm_client_config . current . subscription_id } /providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe"
286+ principal_id = local. principal_id
287+ principal_type = " User"
288+ }
289+
290+ resource "azurerm_role_assignment" "storage_blob_data_contributor_project" {
291+ scope = azapi_resource. storage . id
292+ role_definition_id = " /subscriptions/${ data . azurerm_client_config . current . subscription_id } /providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe"
293+ principal_id = azapi_resource. ai_project . identity [0 ]. principal_id
294+ principal_type = " ServicePrincipal"
295+ }
296+
297+ # Azure AI model deployments automation
298+ resource "null_resource" "ai_model_deployments" {
299+ count = var. enable_ai_automation ? 1 : 0
300+
301+ depends_on = [
302+ azapi_resource . ai_project ,
303+ azapi_resource . ai_foundry ,
304+ azurerm_role_assignment . storage_blob_data_contributor_user
305+ ]
306+
307+ provisioner "local-exec" {
308+ command = <<- EOT
309+ # Create AI model deployments
310+ Write-Host "Creating Azure AI model deployments..."
311+
312+ # Wait for AI Foundry to be fully ready
313+ Write-Host "Waiting for AI Foundry to be ready..."
314+ Start-Sleep -Seconds 30
315+
316+ try {
317+ # Create gpt-4o-mini deployment
318+ Write-Host "Creating gpt-4o-mini deployment..."
319+ az cognitiveservices account deployment create `
320+ --resource-group "${ azurerm_resource_group . rg . name } " `
321+ --name "${ local . ai_foundry_name } " `
322+ --deployment-name "gpt-4o-mini" `
323+ --model-name "gpt-4o-mini" `
324+ --model-version "2024-07-18" `
325+ --model-format "OpenAI" `
326+ --sku-capacity 10 `
327+ --sku-name "GlobalStandard"
328+
329+ if ($LASTEXITCODE -eq 0) {
330+ Write-Host "gpt-4o-mini deployment created successfully"
331+ } else {
332+ Write-Host "gpt-4o-mini deployment may already exist or failed to create"
333+ } # Create text-embedding-3-small deployment
334+ Write-Host "Creating text-embedding-3-small deployment..."
335+ az cognitiveservices account deployment create `
336+ --resource-group "${ azurerm_resource_group . rg . name } " `
337+ --name "${ local . ai_foundry_name } " `
338+ --deployment-name "text-embedding-3-small" `
339+ --model-name "text-embedding-3-small" `
340+ --model-version "1" `
341+ --model-format "OpenAI" `
342+ --sku-capacity 10 `
343+ --sku-name "GlobalStandard"
344+
345+ if ($LASTEXITCODE -eq 0) {
346+ Write-Host "text-embedding-3-small deployment created successfully"
347+ } else {
348+ Write-Host "text-embedding-3-small deployment may already exist or failed to create"
349+ } # Create phi-4 deployment
350+ Write-Host "Creating phi-4 deployment..."
351+ try {
352+ az cognitiveservices account deployment create `
353+ --resource-group "${ azurerm_resource_group . rg . name } " `
354+ --name "${ local . ai_foundry_name } " `
355+ --deployment-name "phi-4" `
356+ --model-name "phi-4" `
357+ --model-version "1" `
358+ --model-format "OpenAI" `
359+ --sku-capacity 5 `
360+ --sku-name "GlobalStandard"
361+
362+ if ($LASTEXITCODE -eq 0) {
363+ Write-Host "phi-4 deployment created successfully"
364+ $phi4Available = $true
365+ } else {
366+ Write-Host "phi-4 model not available in this region/tier, skipping"
367+ $phi4Available = $false
368+ }
369+ } catch {
370+ Write-Host "phi-4 model not supported, skipping"
371+ $phi4Available = $false
372+ }
373+
374+ # List all deployments to verify
375+ Write-Host "`nCurrent model deployments:"
376+ az cognitiveservices account deployment list `
377+ --resource-group "${ azurerm_resource_group . rg . name } " `
378+ --name "${ local . ai_foundry_name } " `
379+ --query "[].{Name:name,Model:properties.model.name,Version:properties.model.version,Capacity:properties.currentCapacity}" `
380+ --output table
381+
382+ Write-Host "`nModel deployment process completed successfully."
383+ }
384+ catch {
385+ Write-Host "Error during model deployment: $_"
386+ Write-Host "This may be expected if deployments already exist."
387+ }
388+ EOT
389+ interpreter = [" PowerShell" , " -Command" ]
390+ }
391+
392+ triggers = {
393+ ai_foundry_id = azapi_resource.ai_foundry.id
394+ ai_project_id = azapi_resource.ai_project.id
395+ }
396+ }
397+
398+ # Connect resources to Azure AI Foundry project
399+ resource "null_resource" "ai_project_connections" {
400+ count = var. enable_ai_automation ? 1 : 0
401+
402+ depends_on = [
403+ null_resource. ai_model_deployments ,
404+ azurerm_application_insights . appinsights ,
405+ azapi_resource . storage
406+ ]
407+
408+ provisioner "local-exec" {
409+ command = <<- EOT
410+ Write-Host "Verifying Azure AI Foundry project configuration..."
411+
412+ # Check if Azure ML extension is installed
413+ $mlExtension = az extension list --query "[?name=='ml'].name" --output tsv
414+ if (-not $mlExtension) {
415+ Write-Host "Installing Azure ML extension..."
416+ az extension add --name ml
417+ }
418+
419+ # Set the AI project as the default workspace for future ML operations
420+ az config set defaults.workspace="${ local . ai_project_name } "
421+ az config set defaults.group="${ azurerm_resource_group . rg . name } "
422+
423+ Write-Host "Azure AI project configuration completed successfully."
424+ Write-Host "Project Name: ${ local . ai_project_name } "
425+ Write-Host "AI Foundry: ${ local . ai_foundry_name } "
426+ Write-Host "Resource Group: ${ azurerm_resource_group . rg . name } "
427+ EOT
428+ interpreter = [" PowerShell" , " -Command" ]
429+ }
430+
431+ triggers = {
432+ storage_id = azapi_resource.storage.id
433+ app_insights_id = azurerm_application_insights.appinsights.id
434+ ai_project_id = azapi_resource.ai_project.id
435+ }
436+ }
437+
438+ # Create .env file with all necessary configuration
439+ resource "null_resource" "create_env_file" {
440+ count = var. enable_ai_automation ? 1 : 0
441+
442+ depends_on = [
443+ null_resource. ai_project_connections ,
444+ azurerm_cosmosdb_account . cosmos ,
445+ azurerm_search_service . search
446+ ]
447+
448+ provisioner "local-exec" {
449+ command = <<- EOT
450+ Write-Host "Creating .env file with Azure resource configuration..."
451+
452+ # Create src directory if it doesn't exist
453+ if (!(Test-Path "../src")) {
454+ New-Item -ItemType Directory -Path "../src" -Force
455+ }
456+
457+ # Get Azure AI Foundry endpoint
458+ $aiFoundryEndpoint = az cognitiveservices account show `
459+ --resource-group "${ azurerm_resource_group . rg . name } " `
460+ --name "${ local . ai_foundry_name } " `
461+ --query "properties.endpoint" `
462+ --output tsv
463+
464+ # Get Azure AI Foundry access key
465+ $aiFoundryKey = az cognitiveservices account keys list `
466+ --resource-group "${ azurerm_resource_group . rg . name } " `
467+ --name "${ local . ai_foundry_name } " `
468+ --query "key1" `
469+ --output tsv
470+
471+ # Get Cosmos DB primary key
472+ $cosmosKey = az cosmosdb keys list `
473+ --resource-group "${ azurerm_resource_group . rg . name } " `
474+ --name "${ local . cosmos_account_name } " `
475+ --query "primaryMasterKey" `
476+ --output tsv
477+
478+ # Get Azure Search admin key
479+ $searchKey = az search admin-key show `
480+ --resource-group "${ azurerm_resource_group . rg . name } " `
481+ --service-name "${ local . search_service_name } " `
482+ --query "primaryKey" `
483+ --output tsv
484+
485+ # Get storage account connection string
486+ $storageConnectionString = az storage account show-connection-string `
487+ --resource-group "${ azurerm_resource_group . rg . name } " `
488+ --name "${ local . storage_account } " `
489+ --query "connectionString" `
490+ --output tsv
491+
492+ # Create .env file content
493+ if ($phi4Available) {
494+ $envContent = @"
495+ # Azure AI Foundry Configuration
496+ AZURE_AI_FOUNDRY_ENDPOINT=$aiFoundryEndpoint
497+ AZURE_AI_FOUNDRY_API_KEY=$aiFoundryKey
498+ AZURE_AI_PROJECT_NAME=${ local . ai_project_name }
499+
500+ # Azure OpenAI Model Deployments
501+ AZURE_OPENAI_CHAT_DEPLOYMENT=gpt-4o-mini
502+ AZURE_OPENAI_EMBEDDING_DEPLOYMENT=text-embedding-3-small
503+ AZURE_OPENAI_PHI_DEPLOYMENT=phi-4
504+ AZURE_OPENAI_ENDPOINT=$aiFoundryEndpoint
505+ AZURE_OPENAI_API_KEY=$aiFoundryKey
506+ AZURE_OPENAI_API_VERSION=2024-02-01
507+
508+ # Azure Cosmos DB Configuration
509+ COSMOS_DB_ENDPOINT=${ azurerm_cosmosdb_account . cosmos . endpoint }
510+ COSMOS_DB_KEY=$cosmosKey
511+ COSMOS_DB_NAME=${ local . cosmos_db_name }
512+ COSMOS_DB_CONTAINER_NAME=products
513+
514+ # Azure AI Search Configuration
515+ SEARCH_SERVICE_ENDPOINT=https://${ local . search_service_name } .search.windows.net
516+ SEARCH_SERVICE_KEY=$searchKey
517+ SEARCH_INDEX_NAME=products-index
518+
519+ # Azure Storage Configuration
520+ STORAGE_ACCOUNT_NAME=${ local . storage_account }
521+ STORAGE_CONNECTION_STRING=$storageConnectionString
522+
523+ # Azure Application Insights
524+ APPLICATION_INSIGHTS_CONNECTION_STRING=${ azurerm_application_insights . appinsights . connection_string }
525+
526+ # Azure Resource Information
527+ AZURE_SUBSCRIPTION_ID=${ data . azurerm_client_config . current . subscription_id }
528+ AZURE_RESOURCE_GROUP=${ azurerm_resource_group . rg . name }
529+ AZURE_LOCATION=${ var . location }
530+ "@
531+ } else {
532+ $envContent = @"
533+ # Azure AI Foundry Configuration
534+ AZURE_AI_FOUNDRY_ENDPOINT=$aiFoundryEndpoint
535+ AZURE_AI_FOUNDRY_API_KEY=$aiFoundryKey
536+ AZURE_AI_PROJECT_NAME=${ local . ai_project_name }
537+
538+ # Azure OpenAI Model Deployments
539+ AZURE_OPENAI_CHAT_DEPLOYMENT=gpt-4o-mini
540+ AZURE_OPENAI_EMBEDDING_DEPLOYMENT=text-embedding-3-small
541+ AZURE_OPENAI_ENDPOINT=$aiFoundryEndpoint
542+ AZURE_OPENAI_API_KEY=$aiFoundryKey
543+ AZURE_OPENAI_API_VERSION=2024-02-01
544+
545+ # Azure Cosmos DB Configuration
546+ COSMOS_DB_ENDPOINT=${ azurerm_cosmosdb_account . cosmos . endpoint }
547+ COSMOS_DB_KEY=$cosmosKey
548+ COSMOS_DB_NAME=${ local . cosmos_db_name }
549+ COSMOS_DB_CONTAINER_NAME=products
550+
551+ # Azure AI Search Configuration
552+ SEARCH_SERVICE_ENDPOINT=https://${ local . search_service_name } .search.windows.net
553+ SEARCH_SERVICE_KEY=$searchKey
554+ SEARCH_INDEX_NAME=products-index
555+
556+ # Azure Storage Configuration
557+ STORAGE_ACCOUNT_NAME=${ local . storage_account }
558+ STORAGE_CONNECTION_STRING=$storageConnectionString
559+
560+ # Azure Application Insights
561+ APPLICATION_INSIGHTS_CONNECTION_STRING=${ azurerm_application_insights . appinsights . connection_string }
562+
563+ # Azure Resource Information
564+ AZURE_SUBSCRIPTION_ID=${ data . azurerm_client_config . current . subscription_id }
565+ AZURE_RESOURCE_GROUP=${ azurerm_resource_group . rg . name }
566+ AZURE_LOCATION=${ var . location }
567+ "@
568+ }
569+
570+ # Write .env file
571+ $envContent | Out-File -FilePath "../src/.env" -Encoding UTF8
572+
573+ Write-Host ".env file created successfully at ../src/.env"
574+ Write-Host "Environment variables configured for:"
575+ if ($phi4Available) {
576+ Write-Host " - Models: gpt-4o-mini, text-embedding-3-small, phi-4"
577+ } else {
578+ Write-Host " - Models: gpt-4o-mini, text-embedding-3-small (phi-4 not available)"
579+ }
580+ Write-Host " - Azure AI Foundry: ${ local . ai_foundry_name } "
581+ Write-Host " - Azure AI Project: ${ local . ai_project_name } "
582+ Write-Host " - Cosmos DB: ${ local . cosmos_account_name } "
583+ Write-Host " - Search Service: ${ local . search_service_name } "
584+ Write-Host " - Storage Account: ${ local . storage_account } "
585+ Write-Host " - Application Insights: ${ local . app_insights_name } "
586+ EOT
587+ interpreter = [" PowerShell" , " -Command" ]
588+ }
589+
590+ triggers = {
591+ # Trigger recreation when any of these resources change
592+ ai_foundry_id = azapi_resource.ai_foundry.id
593+ ai_project_id = azapi_resource.ai_project.id
594+ cosmos_id = azurerm_cosmosdb_account.cosmos.id
595+ search_id = azurerm_search_service.search.id
596+ storage_id = azapi_resource.storage.id
597+ app_insights_id = azurerm_application_insights.appinsights.id
598+ }
599+ }
0 commit comments