Skip to content

Commit 553923f

Browse files
authored
Add Terraform configuration for Azure resources
This Terraform configuration sets up various Azure resources including a resource group, Cosmos DB account, storage account, AI Foundry account, and role assignments for service principals. It also includes configuration for a web app and its associated services.
1 parent f77e767 commit 553923f

1 file changed

Lines changed: 280 additions & 0 deletions

File tree

terraform-infrastructure/main.tf

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
2+
# Create resource group if it does not exist
3+
resource "azurerm_resource_group" "rg" {
4+
name = var.resource_group_name
5+
location = var.location
6+
}
7+
8+
# Subscription context for role assignments
9+
data "azurerm_client_config" "current" {}
10+
11+
# Random suffix to mimic uniqueString(resourceGroup().id)
12+
resource "random_id" "suffix" {
13+
byte_length = 4
14+
}
15+
16+
locals {
17+
# Use provided user_principal_id or default to current Azure CLI user
18+
principal_id = var.user_principal_id != null ? var.user_principal_id : data.azurerm_client_config.current.object_id
19+
suffix = substr(random_id.suffix.hex, 0, 8)
20+
cosmos_account_name = "${var.name_prefix}${local.suffix}cosmosdb"
21+
cosmos_db_name = "zava"
22+
storage_account = lower(replace("${var.name_prefix}${local.suffix}sa", "-", ""))
23+
ai_foundry_name = "aif-${local.suffix}" # custom subdomain
24+
ai_project_name = "proj-${local.suffix}"
25+
search_service_name = "${var.name_prefix}-${local.suffix}-search"
26+
app_service_plan = "${var.name_prefix}-${local.suffix}-asp"
27+
log_analytics_name = "${var.name_prefix}-${local.suffix}-la"
28+
app_insights_name = "${var.name_prefix}-${local.suffix}-ai"
29+
registry_name = lower(replace("${var.name_prefix}${local.suffix}cosureg", "-", ""))
30+
web_app_name = "${var.name_prefix}-${local.suffix}-app"
31+
}
32+
33+
resource "azurerm_cosmosdb_account" "cosmos" {
34+
name = local.cosmos_account_name
35+
location = azurerm_resource_group.rg.location
36+
resource_group_name = azurerm_resource_group.rg.name
37+
offer_type = "Standard"
38+
kind = "GlobalDocumentDB"
39+
consistency_policy {
40+
consistency_level = "Session"
41+
max_interval_in_seconds = 5
42+
max_staleness_prefix = 100
43+
}
44+
geo_location {
45+
location = var.location
46+
failover_priority = 0
47+
}
48+
free_tier_enabled = false
49+
analytical_storage_enabled = false
50+
local_authentication_disabled = !var.enable_cosmos_local_auth
51+
}
52+
53+
resource "azurerm_cosmosdb_sql_database" "cosmosdb" {
54+
name = local.cosmos_db_name
55+
resource_group_name = azurerm_resource_group.rg.name
56+
account_name = azurerm_cosmosdb_account.cosmos.name
57+
throughput = 400
58+
}
59+
60+
# Storage account using AzAPI to bypass policy restrictions
61+
resource "azapi_resource" "storage" {
62+
type = "Microsoft.Storage/storageAccounts@2023-01-01"
63+
name = local.storage_account
64+
location = var.location
65+
parent_id = azurerm_resource_group.rg.id
66+
67+
body = jsonencode({
68+
sku = {
69+
name = "Standard_LRS"
70+
}
71+
kind = "StorageV2"
72+
properties = {
73+
accessTier = "Hot"
74+
allowSharedKeyAccess = true
75+
defaultToOAuthAuthentication = false
76+
allowBlobPublicAccess = false
77+
minimumTlsVersion = "TLS1_2"
78+
supportsHttpsTrafficOnly = true
79+
}
80+
})
81+
82+
identity {
83+
type = "SystemAssigned"
84+
}
85+
}
86+
87+
# AI Foundry account (preview) using AzAPI provider.
88+
resource "azapi_resource" "ai_foundry" {
89+
type = "Microsoft.CognitiveServices/accounts@2025-06-01"
90+
name = local.ai_foundry_name
91+
location = var.location
92+
parent_id = azurerm_resource_group.rg.id
93+
schema_validation_enabled = false
94+
identity { type = "SystemAssigned" }
95+
body = jsonencode({
96+
sku = { name = "S0" }
97+
kind = "AIServices"
98+
properties = {
99+
allowProjectManagement = true
100+
customSubDomainName = local.ai_foundry_name
101+
disableLocalAuth = false
102+
}
103+
})
104+
}
105+
106+
resource "azapi_resource" "ai_project" {
107+
type = "Microsoft.CognitiveServices/accounts/projects@2025-06-01"
108+
name = local.ai_project_name
109+
location = var.location
110+
parent_id = azapi_resource.ai_foundry.id
111+
schema_validation_enabled = false
112+
identity { type = "SystemAssigned" }
113+
body = jsonencode({ properties = {} })
114+
depends_on = [azapi_resource.ai_foundry]
115+
}
116+
117+
resource "azurerm_search_service" "search" {
118+
name = local.search_service_name
119+
resource_group_name = azurerm_resource_group.rg.name
120+
location = var.location
121+
sku = "standard"
122+
identity { type = "SystemAssigned" }
123+
}
124+
125+
resource "azurerm_log_analytics_workspace" "law" {
126+
name = local.log_analytics_name
127+
location = var.location
128+
resource_group_name = azurerm_resource_group.rg.name
129+
sku = "PerGB2018"
130+
retention_in_days = 90
131+
daily_quota_gb = 1
132+
}
133+
134+
resource "azurerm_application_insights" "appinsights" {
135+
name = local.app_insights_name
136+
location = var.location
137+
resource_group_name = azurerm_resource_group.rg.name
138+
application_type = "web"
139+
workspace_id = azurerm_log_analytics_workspace.law.id
140+
}
141+
142+
resource "azurerm_container_registry" "acr" {
143+
name = local.registry_name
144+
resource_group_name = azurerm_resource_group.rg.name
145+
location = var.location
146+
sku = "Standard"
147+
admin_enabled = true
148+
}
149+
150+
resource "azurerm_container_registry_webhook" "webhook" {
151+
name = "${local.registry_name}webhook"
152+
resource_group_name = azurerm_resource_group.rg.name
153+
registry_name = azurerm_container_registry.acr.name
154+
location = var.location
155+
156+
service_uri = "https://${local.web_app_name}.scm.azurewebsites.net/docker/hook"
157+
status = "enabled"
158+
scope = "${local.suffix}/techworkshopl300/zava:latest"
159+
actions = ["push"]
160+
161+
custom_headers = {
162+
"Content-Type" = "application/json"
163+
}
164+
165+
depends_on = [azurerm_container_registry.acr]
166+
}
167+
168+
resource "azurerm_service_plan" "appserviceplan" {
169+
name = local.app_service_plan
170+
resource_group_name = azurerm_resource_group.rg.name
171+
location = var.location
172+
os_type = "Linux"
173+
sku_name = "S1"
174+
}
175+
176+
resource "azurerm_linux_web_app" "app" {
177+
name = local.web_app_name
178+
resource_group_name = azurerm_resource_group.rg.name
179+
location = var.location
180+
service_plan_id = azurerm_service_plan.appserviceplan.id
181+
https_only = true
182+
183+
site_config {
184+
application_stack {
185+
docker_image_name = "${local.registry_name}.azurecr.io/${local.suffix}/techworkshopl300/zava:latest"
186+
docker_registry_url = "https://${local.registry_name}.azurecr.io"
187+
}
188+
http2_enabled = true
189+
minimum_tls_version = "1.2"
190+
}
191+
192+
app_settings = {
193+
WEBSITES_ENABLE_APP_SERVICE_STORAGE = "false"
194+
DOCKER_REGISTRY_SERVER_URL = "https://${local.registry_name}.azurecr.io"
195+
DOCKER_REGISTRY_SERVER_USERNAME = azurerm_container_registry.acr.name
196+
DOCKER_REGISTRY_SERVER_PASSWORD = azurerm_container_registry.acr.admin_password
197+
APPINSIGHTS_INSTRUMENTATIONKEY = azurerm_application_insights.appinsights.instrumentation_key
198+
}
199+
200+
depends_on = [azurerm_container_registry.acr]
201+
}
202+
203+
# Cosmos DB SQL Role Assignments (data plane) using AzAPI
204+
locals {
205+
cosmos_db_data_reader_role_id = "00000000-0000-0000-0000-000000000001"
206+
cosmos_db_data_contributor_role_id = "00000000-0000-0000-0000-000000000002"
207+
cosmos_account_reader_role_id = "fbdf93bf-df7d-467e-a4d2-9458aa1360c8"
208+
cognitive_openai_user_role_id = "5e0bd9bd-7b93-4f28-af87-19fc36ad61bd"
209+
cognitive_contributor_role_id = "25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68"
210+
}
211+
212+
# Assign Cosmos DB Built-in Data Contributor role to specified user principal
213+
resource "azapi_resource" "cosmos_user_data_contributor" {
214+
type = "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2023-04-15"
215+
name = md5("${azurerm_cosmosdb_account.cosmos.id}-${local.principal_id}-${local.cosmos_db_data_contributor_role_id}")
216+
parent_id = azurerm_cosmosdb_account.cosmos.id
217+
body = jsonencode({
218+
properties = {
219+
roleDefinitionId = "${azurerm_cosmosdb_account.cosmos.id}/sqlRoleDefinitions/${local.cosmos_db_data_contributor_role_id}"
220+
principalId = local.principal_id
221+
scope = azurerm_cosmosdb_account.cosmos.id
222+
}
223+
})
224+
}
225+
226+
# Role assignments for Search managed identity
227+
resource "azurerm_role_assignment" "search_cosmos_account_reader" {
228+
scope = azurerm_cosmosdb_account.cosmos.id
229+
role_definition_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/providers/Microsoft.Authorization/roleDefinitions/${local.cosmos_account_reader_role_id}"
230+
principal_id = azurerm_search_service.search.identity[0].principal_id
231+
principal_type = "ServicePrincipal"
232+
}
233+
234+
resource "azapi_resource" "search_cosmos_data_reader" {
235+
type = "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2023-04-15"
236+
name = md5("${azurerm_cosmosdb_account.cosmos.id}-${azurerm_search_service.search.identity[0].principal_id}-${local.cosmos_db_data_reader_role_id}")
237+
parent_id = azurerm_cosmosdb_account.cosmos.id
238+
body = jsonencode({
239+
properties = {
240+
roleDefinitionId = "${azurerm_cosmosdb_account.cosmos.id}/sqlRoleDefinitions/${local.cosmos_db_data_reader_role_id}"
241+
principalId = azurerm_search_service.search.identity[0].principal_id
242+
scope = azurerm_cosmosdb_account.cosmos.id
243+
}
244+
})
245+
}
246+
247+
resource "azapi_resource" "search_cosmos_data_contributor" {
248+
type = "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2023-04-15"
249+
name = md5("${azurerm_cosmosdb_account.cosmos.id}-${azurerm_search_service.search.identity[0].principal_id}-${local.cosmos_db_data_contributor_role_id}")
250+
parent_id = azurerm_cosmosdb_account.cosmos.id
251+
body = jsonencode({
252+
properties = {
253+
roleDefinitionId = "${azurerm_cosmosdb_account.cosmos.id}/sqlRoleDefinitions/${local.cosmos_db_data_contributor_role_id}"
254+
principalId = azurerm_search_service.search.identity[0].principal_id
255+
scope = azurerm_cosmosdb_account.cosmos.id
256+
}
257+
})
258+
}
259+
260+
# Role assignments for AI Project & AI Foundry
261+
resource "azurerm_role_assignment" "search_project_openai_user" {
262+
scope = azapi_resource.ai_project.id
263+
role_definition_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/providers/Microsoft.Authorization/roleDefinitions/${local.cognitive_openai_user_role_id}"
264+
principal_id = azurerm_search_service.search.identity[0].principal_id
265+
principal_type = "ServicePrincipal"
266+
}
267+
268+
resource "azurerm_role_assignment" "search_foundry_openai_user" {
269+
scope = azapi_resource.ai_foundry.id
270+
role_definition_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/providers/Microsoft.Authorization/roleDefinitions/${local.cognitive_openai_user_role_id}"
271+
principal_id = azurerm_search_service.search.identity[0].principal_id
272+
principal_type = "ServicePrincipal"
273+
}
274+
275+
resource "azurerm_role_assignment" "search_project_contributor" {
276+
scope = azapi_resource.ai_project.id
277+
role_definition_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/providers/Microsoft.Authorization/roleDefinitions/${local.cognitive_contributor_role_id}"
278+
principal_id = azurerm_search_service.search.identity[0].principal_id
279+
principal_type = "ServicePrincipal"
280+
}

0 commit comments

Comments
 (0)