From b0601afbda0569ce15118927a6ef171501460e51 Mon Sep 17 00:00:00 2001 From: Timna Brown <24630902+brown9804@users.noreply.github.com> Date: Fri, 21 Nov 2025 18:03:41 -0600 Subject: [PATCH 1/6] AI resources automated connection Added role assignments for storage account permissions and automated AI model deployments. Created .env file for Azure resource configuration. --- terraform-infrastructure/main.tf | 319 +++++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) diff --git a/terraform-infrastructure/main.tf b/terraform-infrastructure/main.tf index 2e7dd37..ece0b39 100644 --- a/terraform-infrastructure/main.tf +++ b/terraform-infrastructure/main.tf @@ -278,3 +278,322 @@ resource "azurerm_role_assignment" "search_project_contributor" { principal_id = azurerm_search_service.search.identity[0].principal_id principal_type = "ServicePrincipal" } + +# Storage account permissions for Azure AI Foundry project +resource "azurerm_role_assignment" "storage_blob_data_contributor_user" { + scope = azapi_resource.storage.id + role_definition_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe" + principal_id = local.principal_id + principal_type = "User" +} + +resource "azurerm_role_assignment" "storage_blob_data_contributor_project" { + scope = azapi_resource.storage.id + role_definition_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe" + principal_id = azapi_resource.ai_project.identity[0].principal_id + principal_type = "ServicePrincipal" +} + +# Azure AI model deployments automation +resource "null_resource" "ai_model_deployments" { + count = var.enable_ai_automation ? 1 : 0 + + depends_on = [ + azapi_resource.ai_project, + azapi_resource.ai_foundry, + azurerm_role_assignment.storage_blob_data_contributor_user + ] + + provisioner "local-exec" { + command = <<-EOT + # Create AI model deployments + Write-Host "Creating Azure AI model deployments..." + + # Wait for AI Foundry to be fully ready + Write-Host "Waiting for AI Foundry to be ready..." + Start-Sleep -Seconds 30 + + try { + # Create gpt-4o-mini deployment + Write-Host "Creating gpt-4o-mini deployment..." + az cognitiveservices account deployment create ` + --resource-group "${azurerm_resource_group.rg.name}" ` + --name "${local.ai_foundry_name}" ` + --deployment-name "gpt-4o-mini" ` + --model-name "gpt-4o-mini" ` + --model-version "2024-07-18" ` + --model-format "OpenAI" ` + --sku-capacity 10 ` + --sku-name "GlobalStandard" + + if ($LASTEXITCODE -eq 0) { + Write-Host "gpt-4o-mini deployment created successfully" + } else { + Write-Host "gpt-4o-mini deployment may already exist or failed to create" + } # Create text-embedding-3-small deployment + Write-Host "Creating text-embedding-3-small deployment..." + az cognitiveservices account deployment create ` + --resource-group "${azurerm_resource_group.rg.name}" ` + --name "${local.ai_foundry_name}" ` + --deployment-name "text-embedding-3-small" ` + --model-name "text-embedding-3-small" ` + --model-version "1" ` + --model-format "OpenAI" ` + --sku-capacity 10 ` + --sku-name "GlobalStandard" + + if ($LASTEXITCODE -eq 0) { + Write-Host "text-embedding-3-small deployment created successfully" + } else { + Write-Host "text-embedding-3-small deployment may already exist or failed to create" + } # Create phi-4 deployment + Write-Host "Creating phi-4 deployment..." + try { + az cognitiveservices account deployment create ` + --resource-group "${azurerm_resource_group.rg.name}" ` + --name "${local.ai_foundry_name}" ` + --deployment-name "phi-4" ` + --model-name "phi-4" ` + --model-version "1" ` + --model-format "OpenAI" ` + --sku-capacity 5 ` + --sku-name "GlobalStandard" + + if ($LASTEXITCODE -eq 0) { + Write-Host "phi-4 deployment created successfully" + $phi4Available = $true + } else { + Write-Host "phi-4 model not available in this region/tier, skipping" + $phi4Available = $false + } + } catch { + Write-Host "phi-4 model not supported, skipping" + $phi4Available = $false + } + + # List all deployments to verify + Write-Host "`nCurrent model deployments:" + az cognitiveservices account deployment list ` + --resource-group "${azurerm_resource_group.rg.name}" ` + --name "${local.ai_foundry_name}" ` + --query "[].{Name:name,Model:properties.model.name,Version:properties.model.version,Capacity:properties.currentCapacity}" ` + --output table + + Write-Host "`nModel deployment process completed successfully." + } + catch { + Write-Host "Error during model deployment: $_" + Write-Host "This may be expected if deployments already exist." + } + EOT + interpreter = ["PowerShell", "-Command"] + } + + triggers = { + ai_foundry_id = azapi_resource.ai_foundry.id + ai_project_id = azapi_resource.ai_project.id + } +} + +# Connect resources to Azure AI Foundry project +resource "null_resource" "ai_project_connections" { + count = var.enable_ai_automation ? 1 : 0 + + depends_on = [ + null_resource.ai_model_deployments, + azurerm_application_insights.appinsights, + azapi_resource.storage + ] + + provisioner "local-exec" { + command = <<-EOT + Write-Host "Verifying Azure AI Foundry project configuration..." + + # Check if Azure ML extension is installed + $mlExtension = az extension list --query "[?name=='ml'].name" --output tsv + if (-not $mlExtension) { + Write-Host "Installing Azure ML extension..." + az extension add --name ml + } + + # Set the AI project as the default workspace for future ML operations + az config set defaults.workspace="${local.ai_project_name}" + az config set defaults.group="${azurerm_resource_group.rg.name}" + + Write-Host "Azure AI project configuration completed successfully." + Write-Host "Project Name: ${local.ai_project_name}" + Write-Host "AI Foundry: ${local.ai_foundry_name}" + Write-Host "Resource Group: ${azurerm_resource_group.rg.name}" + EOT + interpreter = ["PowerShell", "-Command"] + } + + triggers = { + storage_id = azapi_resource.storage.id + app_insights_id = azurerm_application_insights.appinsights.id + ai_project_id = azapi_resource.ai_project.id + } +} + +# Create .env file with all necessary configuration +resource "null_resource" "create_env_file" { + count = var.enable_ai_automation ? 1 : 0 + + depends_on = [ + null_resource.ai_project_connections, + azurerm_cosmosdb_account.cosmos, + azurerm_search_service.search + ] + + provisioner "local-exec" { + command = <<-EOT + Write-Host "Creating .env file with Azure resource configuration..." + + # Create src directory if it doesn't exist + if (!(Test-Path "../src")) { + New-Item -ItemType Directory -Path "../src" -Force + } + + # Get Azure AI Foundry endpoint + $aiFoundryEndpoint = az cognitiveservices account show ` + --resource-group "${azurerm_resource_group.rg.name}" ` + --name "${local.ai_foundry_name}" ` + --query "properties.endpoint" ` + --output tsv + + # Get Azure AI Foundry access key + $aiFoundryKey = az cognitiveservices account keys list ` + --resource-group "${azurerm_resource_group.rg.name}" ` + --name "${local.ai_foundry_name}" ` + --query "key1" ` + --output tsv + + # Get Cosmos DB primary key + $cosmosKey = az cosmosdb keys list ` + --resource-group "${azurerm_resource_group.rg.name}" ` + --name "${local.cosmos_account_name}" ` + --query "primaryMasterKey" ` + --output tsv + + # Get Azure Search admin key + $searchKey = az search admin-key show ` + --resource-group "${azurerm_resource_group.rg.name}" ` + --service-name "${local.search_service_name}" ` + --query "primaryKey" ` + --output tsv + + # Get storage account connection string + $storageConnectionString = az storage account show-connection-string ` + --resource-group "${azurerm_resource_group.rg.name}" ` + --name "${local.storage_account}" ` + --query "connectionString" ` + --output tsv + + # Create .env file content + if ($phi4Available) { + $envContent = @" +# Azure AI Foundry Configuration +AZURE_AI_FOUNDRY_ENDPOINT=$aiFoundryEndpoint +AZURE_AI_FOUNDRY_API_KEY=$aiFoundryKey +AZURE_AI_PROJECT_NAME=${local.ai_project_name} + +# Azure OpenAI Model Deployments +AZURE_OPENAI_CHAT_DEPLOYMENT=gpt-4o-mini +AZURE_OPENAI_EMBEDDING_DEPLOYMENT=text-embedding-3-small +AZURE_OPENAI_PHI_DEPLOYMENT=phi-4 +AZURE_OPENAI_ENDPOINT=$aiFoundryEndpoint +AZURE_OPENAI_API_KEY=$aiFoundryKey +AZURE_OPENAI_API_VERSION=2024-02-01 + +# Azure Cosmos DB Configuration +COSMOS_DB_ENDPOINT=${azurerm_cosmosdb_account.cosmos.endpoint} +COSMOS_DB_KEY=$cosmosKey +COSMOS_DB_NAME=${local.cosmos_db_name} +COSMOS_DB_CONTAINER_NAME=products + +# Azure AI Search Configuration +SEARCH_SERVICE_ENDPOINT=https://${local.search_service_name}.search.windows.net +SEARCH_SERVICE_KEY=$searchKey +SEARCH_INDEX_NAME=products-index + +# Azure Storage Configuration +STORAGE_ACCOUNT_NAME=${local.storage_account} +STORAGE_CONNECTION_STRING=$storageConnectionString + +# Azure Application Insights +APPLICATION_INSIGHTS_CONNECTION_STRING=${azurerm_application_insights.appinsights.connection_string} + +# Azure Resource Information +AZURE_SUBSCRIPTION_ID=${data.azurerm_client_config.current.subscription_id} +AZURE_RESOURCE_GROUP=${azurerm_resource_group.rg.name} +AZURE_LOCATION=${var.location} +"@ + } else { + $envContent = @" +# Azure AI Foundry Configuration +AZURE_AI_FOUNDRY_ENDPOINT=$aiFoundryEndpoint +AZURE_AI_FOUNDRY_API_KEY=$aiFoundryKey +AZURE_AI_PROJECT_NAME=${local.ai_project_name} + +# Azure OpenAI Model Deployments +AZURE_OPENAI_CHAT_DEPLOYMENT=gpt-4o-mini +AZURE_OPENAI_EMBEDDING_DEPLOYMENT=text-embedding-3-small +AZURE_OPENAI_ENDPOINT=$aiFoundryEndpoint +AZURE_OPENAI_API_KEY=$aiFoundryKey +AZURE_OPENAI_API_VERSION=2024-02-01 + +# Azure Cosmos DB Configuration +COSMOS_DB_ENDPOINT=${azurerm_cosmosdb_account.cosmos.endpoint} +COSMOS_DB_KEY=$cosmosKey +COSMOS_DB_NAME=${local.cosmos_db_name} +COSMOS_DB_CONTAINER_NAME=products + +# Azure AI Search Configuration +SEARCH_SERVICE_ENDPOINT=https://${local.search_service_name}.search.windows.net +SEARCH_SERVICE_KEY=$searchKey +SEARCH_INDEX_NAME=products-index + +# Azure Storage Configuration +STORAGE_ACCOUNT_NAME=${local.storage_account} +STORAGE_CONNECTION_STRING=$storageConnectionString + +# Azure Application Insights +APPLICATION_INSIGHTS_CONNECTION_STRING=${azurerm_application_insights.appinsights.connection_string} + +# Azure Resource Information +AZURE_SUBSCRIPTION_ID=${data.azurerm_client_config.current.subscription_id} +AZURE_RESOURCE_GROUP=${azurerm_resource_group.rg.name} +AZURE_LOCATION=${var.location} +"@ + } + + # Write .env file + $envContent | Out-File -FilePath "../src/.env" -Encoding UTF8 + + Write-Host ".env file created successfully at ../src/.env" + Write-Host "Environment variables configured for:" + if ($phi4Available) { + Write-Host " - Models: gpt-4o-mini, text-embedding-3-small, phi-4" + } else { + Write-Host " - Models: gpt-4o-mini, text-embedding-3-small (phi-4 not available)" + } + Write-Host " - Azure AI Foundry: ${local.ai_foundry_name}" + Write-Host " - Azure AI Project: ${local.ai_project_name}" + Write-Host " - Cosmos DB: ${local.cosmos_account_name}" + Write-Host " - Search Service: ${local.search_service_name}" + Write-Host " - Storage Account: ${local.storage_account}" + Write-Host " - Application Insights: ${local.app_insights_name}" + EOT + interpreter = ["PowerShell", "-Command"] + } + + triggers = { + # Trigger recreation when any of these resources change + ai_foundry_id = azapi_resource.ai_foundry.id + ai_project_id = azapi_resource.ai_project.id + cosmos_id = azurerm_cosmosdb_account.cosmos.id + search_id = azurerm_search_service.search.id + storage_id = azapi_resource.storage.id + app_insights_id = azurerm_application_insights.appinsights.id + } +} From b99a7d03657fa01aa6e7362a327514769b274758 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 22 Nov 2025 00:03:59 +0000 Subject: [PATCH 2/6] Update visitor count --- terraform-infrastructure/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform-infrastructure/README.md b/terraform-infrastructure/README.md index b09fd6a..cb38b65 100644 --- a/terraform-infrastructure/README.md +++ b/terraform-infrastructure/README.md @@ -119,7 +119,7 @@ graph TD;
- Total views -

Refresh Date: 2025-11-21

+ Total views +

Refresh Date: 2025-11-22

From 389b4e6f384f0ebc0d6f54237de7fdb1750979bb Mon Sep 17 00:00:00 2001 From: Timna Brown <24630902+brown9804@users.noreply.github.com> Date: Fri, 21 Nov 2025 18:04:31 -0600 Subject: [PATCH 3/6] Add multiple outputs for Azure resources in Terraform --- terraform-infrastructure/outputs.tf | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/terraform-infrastructure/outputs.tf b/terraform-infrastructure/outputs.tf index b6b82ce..d83c350 100644 --- a/terraform-infrastructure/outputs.tf +++ b/terraform-infrastructure/outputs.tf @@ -27,3 +27,52 @@ output "application_url" { value = azurerm_linux_web_app.app.default_hostname description = "Primary host name for the App Service" } + +output "ai_foundry_name" { + value = local.ai_foundry_name + description = "Azure AI Foundry account name" +} + +output "ai_project_name" { + value = local.ai_project_name + description = "Azure AI Foundry project name" +} + +output "resource_group_name" { + value = azurerm_resource_group.rg.name + description = "Resource group name" +} + +output "subscription_id" { + value = data.azurerm_client_config.current.subscription_id + description = "Azure subscription ID" +} + +output "application_insights_connection_string" { + value = azurerm_application_insights.appinsights.connection_string + description = "Application Insights connection string" + sensitive = true +} + +output "cosmos_db_name" { + value = local.cosmos_db_name + description = "Cosmos DB database name" +} + +output "ai_foundry_endpoint" { + value = "https://${local.ai_foundry_name}.cognitiveservices.azure.com/" + description = "Azure AI Foundry endpoint URL" +} + +output "deployed_models" { + value = var.enable_ai_automation ? [ + "gpt-4o-mini", + "text-embedding-3-small" + ] : [] + description = "List of AI models actually deployed (phi-4 not available in this region)" +} + +output "env_file_location" { + value = var.enable_ai_automation ? "../src/.env" : "Not created (AI automation disabled)" + description = "Location of the generated .env file" +} From 5c7e6ffc467e2074eddf1ca158d5ea9eca1f3e95 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 22 Nov 2025 00:04:46 +0000 Subject: [PATCH 4/6] Update visitor count --- terraform-infrastructure/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform-infrastructure/README.md b/terraform-infrastructure/README.md index cb38b65..c337aff 100644 --- a/terraform-infrastructure/README.md +++ b/terraform-infrastructure/README.md @@ -119,7 +119,7 @@ graph TD;
- Total views + Total views

Refresh Date: 2025-11-22

From d6e81a940e8e5020b8980aafdd59f2a51dab9e69 Mon Sep 17 00:00:00 2001 From: Timna Brown <24630902+brown9804@users.noreply.github.com> Date: Fri, 21 Nov 2025 18:04:56 -0600 Subject: [PATCH 5/6] Add variable for Azure AI automation enablement --- terraform-infrastructure/variables.tf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/terraform-infrastructure/variables.tf b/terraform-infrastructure/variables.tf index 295814a..bda5d3d 100644 --- a/terraform-infrastructure/variables.tf +++ b/terraform-infrastructure/variables.tf @@ -26,3 +26,9 @@ variable "enable_cosmos_local_auth" { description = "Whether to enable local auth on Cosmos DB account" default = true } + +variable "enable_ai_automation" { + type = bool + description = "Whether to run Azure AI Foundry automation steps (model deployments, connections, .env creation)" + default = true +} From e2bb079daae8dc06972a30e8041452a1805a3bee Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 22 Nov 2025 00:05:15 +0000 Subject: [PATCH 6/6] Update visitor count --- terraform-infrastructure/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform-infrastructure/README.md b/terraform-infrastructure/README.md index c337aff..cb38b65 100644 --- a/terraform-infrastructure/README.md +++ b/terraform-infrastructure/README.md @@ -119,7 +119,7 @@ graph TD;
- Total views + Total views

Refresh Date: 2025-11-22