diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml
index 7c1eedb6..b99c06a3 100644
--- a/.github/workflows/terraform.yml
+++ b/.github/workflows/terraform.yml
@@ -36,7 +36,7 @@ jobs:
tenant_id: "37963dd4-f4e6-40f8-a7d6-24b97919e452"
subscription_id: "9842be63-c8c0-4647-a5d1-0c5e7f8bbb25"
secrets:
- CLIENT_ID_PLAN: ${{ secrets.CLIENT_ID_PLAN }}
+ CLIENT_ID_PLAN: ${{ secrets.CLIENT_ID }}
CLIENT_ID_APPLY: ${{ secrets.CLIENT_ID }}
terraform_destroy:
diff --git a/README.md b/README.md
index 6ccd02f5..01d57e31 100644
--- a/README.md
+++ b/README.md
@@ -242,6 +242,7 @@ object(
storage_subnet = string
consumption_subnet = string
fabric_subnet = string
+ aifoundry_subnet = optional(string, "")
databricks_engineering_private_subnet = string
databricks_engineering_public_subnet = string
databricks_consumption_private_subnet = optional(string, "")
@@ -260,6 +261,29 @@ Type: `string`
The following input variables are optional (have default values):
+### [ai\_foundry\_account\_details](#input\_ai\_foundry\_account\_details)
+
+Description: Specifies the ai foundry configuration.
+
+Type:
+
+```hcl
+object({
+ enabled = optional(bool, false)
+ search_service = optional(object({
+ sku = optional(string, "basic")
+ semantic_search_sku = optional(string, "standard")
+ partition_count = optional(number, 1)
+ replica_count = optional(number, 1)
+ }), {})
+ cosmos_db = optional(object({
+ consistency_level = optional(string, "Session")
+ }), {})
+ })
+```
+
+Default: `{}`
+
### [customer\_managed\_key](#input\_customer\_managed\_key)
Description: Specifies the customer managed key configurations.
@@ -411,6 +435,14 @@ Type: `string`
Default: `""`
+### [private\_dns\_zone\_id\_ai\_services](#input\_private\_dns\_zone\_id\_ai\_services)
+
+Description: Specifies the resource ID of the private DNS zone for Azure Foundry (AI Services). Not required if DNS A-records get created via Azure Policy.
+
+Type: `string`
+
+Default: `""`
+
### [private\_dns\_zone\_id\_blob](#input\_private\_dns\_zone\_id\_blob)
Description: Specifies the resource ID of the private DNS zone for Azure Storage blob endpoints. Not required if DNS A-records get created via Azue Policy.
@@ -427,6 +459,14 @@ Type: `string`
Default: `""`
+### [private\_dns\_zone\_id\_cosmos\_sql](#input\_private\_dns\_zone\_id\_cosmos\_sql)
+
+Description: Specifies the resource ID of the private DNS zone for cosmos db sql. Not required if DNS A-records get created via Azure Policy.
+
+Type: `string`
+
+Default: `""`
+
### [private\_dns\_zone\_id\_data\_factory](#input\_private\_dns\_zone\_id\_data\_factory)
Description: Specifies the resource ID of the private DNS zone for Azure Data Factory. Not required if DNS A-records get created via Azure Policy.
diff --git a/databricks.tf b/databricks.tf
index ad197682..e81a7b6b 100644
--- a/databricks.tf
+++ b/databricks.tf
@@ -23,6 +23,7 @@ module "databricks_core" {
databricks_ip_access_list_deny = []
databricks_network_connectivity_config_name = var.databricks_network_connectivity_config_name
databricks_compliance_security_profile_standards = var.databricks_compliance_security_profile_standards
+ # databricks_network_policy_details = var.databricks_network_policy_details
# Identity variables
service_principal_name_terraform_plan = var.service_principal_name_terraform_plan
diff --git a/main.tf b/main.tf
index af35356d..2dda0b21 100644
--- a/main.tf
+++ b/main.tf
@@ -19,6 +19,7 @@ module "platform" {
subnet_cidr_range_storage = var.subnet_cidr_ranges.storage_subnet
subnet_cidr_range_consumption = var.subnet_cidr_ranges.consumption_subnet
subnet_cidr_range_fabric = var.subnet_cidr_ranges.fabric_subnet
+ subnet_cidr_range_aifoundry = var.subnet_cidr_ranges.aifoundry_subnet
subnet_cidr_range_engineering_private = var.subnet_cidr_ranges.databricks_engineering_private_subnet
subnet_cidr_range_engineering_public = var.subnet_cidr_ranges.databricks_engineering_public_subnet
subnet_cidr_range_consumption_private = var.subnet_cidr_ranges.databricks_consumption_private_subnet
@@ -30,6 +31,7 @@ module "platform" {
}
}
databricks_workspace_consumption_enabled = var.databricks_workspace_consumption_enabled
+ aifoundry_enabled = var.ai_foundry_account_details.enabled
}
module "core" {
@@ -53,6 +55,7 @@ module "core" {
databricks_compliance_security_profile_standards = var.databricks_compliance_security_profile_standards
databricks_workspace_consumption_enabled = var.databricks_workspace_consumption_enabled
fabric_capacity_details = var.fabric_capacity_details
+ ai_foundry_account_details = var.ai_foundry_account_details
# HA/DR variables
zone_redundancy_enabled = var.zone_redundancy_enabled
@@ -68,6 +71,7 @@ module "core" {
vnet_id = var.vnet_id
subnet_id_storage = module.platform.subnet_id_storage
subnet_id_consumption = module.platform.subnet_id_consumption
+ subnet_id_aifoundry = module.platform.subnet_id_aifoundry
subnet_id_engineering_private = module.platform.subnet_id_engineering_private
subnet_id_engineering_public = module.platform.subnet_id_engineering_public
subnet_id_consumption_private = module.platform.subnet_id_consumption_private
@@ -75,10 +79,15 @@ module "core" {
connectivity_delay_in_seconds = local.connectivity_delay_in_seconds
# DNS variables
- private_dns_zone_id_blob = var.private_dns_zone_id_blob
- private_dns_zone_id_dfs = var.private_dns_zone_id_dfs
- private_dns_zone_id_queue = var.private_dns_zone_id_queue
- private_dns_zone_id_databricks = var.private_dns_zone_id_databricks
+ private_dns_zone_id_blob = var.private_dns_zone_id_blob
+ private_dns_zone_id_dfs = var.private_dns_zone_id_dfs
+ private_dns_zone_id_queue = var.private_dns_zone_id_queue
+ private_dns_zone_id_databricks = var.private_dns_zone_id_databricks
+ private_dns_zone_id_ai_services = var.private_dns_zone_id_ai_services
+ private_dns_zone_id_cognitive_account = var.private_dns_zone_id_cognitive_account
+ private_dns_zone_id_open_ai = var.private_dns_zone_id_open_ai
+ private_dns_zone_id_search_service = var.private_dns_zone_id_search_service
+ private_dns_zone_id_cosmos_sql = var.private_dns_zone_id_cosmos_sql
# Customer-managed key variables
customer_managed_key = var.customer_managed_key
@@ -135,12 +144,17 @@ module "data_application" {
root_folder = try(each.value.repository.github.fabric_root_folder, "")
}
}
+ ai_foundry_account_details = module.core.ai_foundry_account_details
+ ai_foundry_project_details = {
+ enabled = try(each.value.ai_foundry_project.enabled, false)
+ }
storage_dependencies = module.core.storage_dependencies
# HA/DR variables
zone_redundancy_enabled = var.zone_redundancy_enabled
# Logging and monitoring variables
+ log_analytics_workspace_id = var.log_analytics_workspace_id
diagnostics_configurations = local.diagnostics_configurations
alerting = try(each.value.alerting, {})
diff --git a/modules/core/aifoundry.tf b/modules/core/aifoundry.tf
new file mode 100644
index 00000000..f9351bcc
--- /dev/null
+++ b/modules/core/aifoundry.tf
@@ -0,0 +1,34 @@
+module "ai_foundry_account" {
+ source = "github.com/PerfectThymeTech/terraform-azurerm-modules//modules/aifoundry?ref=marvinbuss/ai_foundry"
+ providers = {
+ azurerm = azurerm
+ time = time
+ }
+
+ count = var.ai_foundry_account_details.enabled ? 1 : 0
+
+ location = var.location
+ resource_group_name = one(azurerm_resource_group.resource_group_ai[*].name)
+ tags = var.tags
+ ai_services_name = "${local.prefix}-aif001"
+ ai_services_sku = "S0"
+ ai_services_firewall_bypass_azure_services = true
+ ai_services_outbound_network_access_restricted = true
+ ai_services_outbound_network_access_allowed_fqdns = []
+ ai_services_local_auth_enabled = false
+ ai_services_projects = {}
+ ai_services_cosmosdb_accounts = {}
+ ai_services_storage_accounts = {}
+ ai_services_aisearch_accounts = {}
+ ai_services_openai_accounts = {}
+ ai_services_connections_account = {}
+ ai_services_deployments = {}
+ diagnostics_configurations = var.diagnostics_configurations
+ subnet_id = var.subnet_id_consumption
+ subnet_id_capability_hosts = var.subnet_id_aifoundry
+ connectivity_delay_in_seconds = var.connectivity_delay_in_seconds
+ private_dns_zone_id_ai_services = var.private_dns_zone_id_ai_services
+ private_dns_zone_id_cognitive_account = var.private_dns_zone_id_cognitive_account
+ private_dns_zone_id_open_ai = var.private_dns_zone_id_open_ai
+ customer_managed_key = var.customer_managed_key
+}
diff --git a/modules/core/aisearch.tf b/modules/core/aisearch.tf
new file mode 100644
index 00000000..3faee5a4
--- /dev/null
+++ b/modules/core/aisearch.tf
@@ -0,0 +1,27 @@
+module "ai_search" {
+ source = "github.com/PerfectThymeTech/terraform-azurerm-modules//modules/aisearch?ref=main"
+ providers = {
+ azurerm = azurerm
+ time = time
+ }
+
+ count = var.ai_foundry_account_details.enabled ? 1 : 0
+
+ location = var.location
+ resource_group_name = one(azurerm_resource_group.resource_group_ai[*].name)
+ tags = var.tags
+ search_service_name = "${local.prefix}-srch001"
+ search_service_sku = var.ai_foundry_account_details.search_service.sku
+ search_service_semantic_search_sku = var.ai_foundry_account_details.search_service.semantic_search_sku
+ search_service_local_authentication_enabled = false
+ search_service_authentication_failure_mode = null
+ search_service_hosting_mode = "default"
+ search_service_partition_count = var.ai_foundry_account_details.search_service.partition_count
+ search_service_replica_count = var.ai_foundry_account_details.search_service.replica_count
+ search_service_shared_private_links = local.search_service_shared_private_links
+ diagnostics_configurations = var.diagnostics_configurations
+ subnet_id = var.subnet_id_consumption
+ connectivity_delay_in_seconds = var.connectivity_delay_in_seconds
+ private_dns_zone_id_search_service = var.private_dns_zone_id_search_service
+ customer_managed_key = var.customer_managed_key
+}
diff --git a/modules/core/cosmosdb.tf b/modules/core/cosmosdb.tf
new file mode 100644
index 00000000..69201a8f
--- /dev/null
+++ b/modules/core/cosmosdb.tf
@@ -0,0 +1,56 @@
+module "cosmos_db" {
+ source = "github.com/PerfectThymeTech/terraform-azurerm-modules//modules/cosmosdb?ref=main"
+ providers = {
+ azurerm = azurerm
+ time = time
+ }
+
+ count = var.ai_foundry_account_details.enabled ? 1 : 0
+
+ location = var.location
+ resource_group_name = one(azurerm_resource_group.resource_group_ai[*].name)
+ tags = var.tags
+ cosmosdb_account_name = "${local.prefix}-csms001"
+ cosmosdb_account_access_key_metadata_writes_enabled = false
+ cosmosdb_account_analytical_storage_enabled = false
+ cosmosdb_account_automatic_failover_enabled = false
+ cosmosdb_account_backup = {
+ type = "Continuous"
+ tier = "Continuous7Days"
+ storage_redundancy = null
+ retention_in_hours = null
+ interval_in_minutes = null
+ }
+ cosmosdb_account_capabilities = []
+ cosmosdb_account_capacity_total_throughput_limit = -1
+ cosmosdb_account_consistency_policy = {
+ consistency_level = var.ai_foundry_account_details.cosmos_db.consistency_level
+ max_interval_in_seconds = null
+ max_staleness_prefix = null
+ }
+ cosmosdb_account_cors_rules = {}
+ cosmosdb_account_default_identity_type = null
+ cosmosdb_account_geo_location = [
+ {
+ location = var.location
+ failover_priority = 0
+ zone_redundant = false
+ }
+ ]
+ cosmosdb_account_kind = "GlobalDocumentDB"
+ cosmosdb_account_mongo_server_version = null
+ cosmosdb_account_local_authentication_disabled = true
+ cosmosdb_account_partition_merge_enabled = false
+ diagnostics_configurations = var.diagnostics_configurations
+ subnet_id = var.subnet_id_consumption
+ connectivity_delay_in_seconds = var.connectivity_delay_in_seconds
+ private_endpoint_subresource_names = ["Sql"]
+ private_dns_zone_id_cosmos_sql = var.private_dns_zone_id_cosmos_sql
+ private_dns_zone_id_cosmos_mongodb = ""
+ private_dns_zone_id_cosmos_cassandra = ""
+ private_dns_zone_id_cosmos_gremlin = ""
+ private_dns_zone_id_cosmos_table = ""
+ private_dns_zone_id_cosmos_analytical = ""
+ private_dns_zone_id_cosmos_coordinator = ""
+ customer_managed_key = var.customer_managed_key
+}
diff --git a/modules/core/locals.tf b/modules/core/locals.tf
index 5e0e11a1..518beec9 100644
--- a/modules/core/locals.tf
+++ b/modules/core/locals.tf
@@ -103,4 +103,7 @@ locals {
group_id = "dfs"
}
}
+
+ # Search service locals
+ search_service_shared_private_links = {}
}
diff --git a/modules/core/main.tf b/modules/core/main.tf
index ac55b992..652f1f27 100644
--- a/modules/core/main.tf
+++ b/modules/core/main.tf
@@ -18,6 +18,14 @@ resource "azurerm_resource_group" "resource_group_consumption" {
tags = var.tags
}
+resource "azurerm_resource_group" "resource_group_ai" {
+ count = var.ai_foundry_account_details.enabled ? 1 : 0
+
+ name = "${local.prefix}-ai-rg"
+ location = var.location
+ tags = var.tags
+}
+
resource "azurerm_resource_group" "resource_group_fabric" {
name = "${local.prefix}-fbrc-rg"
location = var.location
diff --git a/modules/core/outputs.tf b/modules/core/outputs.tf
index 97c56de4..0e906a2f 100644
--- a/modules/core/outputs.tf
+++ b/modules/core/outputs.tf
@@ -52,3 +52,30 @@ output "fabric_capacity_name" {
description = "Specifies the name of the Fabric capacity."
value = try(reverse(split("/", one(module.fabric_capacity[*].fabric_capacity_id), "/"))[0], "")
}
+
+# AI Foundry details
+output "ai_foundry_account_details" {
+ description = "Specifies the ai foundry details of the account."
+ value = {
+ enabled = var.ai_foundry_account_details.enabled
+ ai_foundry_account = {
+ id = one(module.ai_foundry_account[*].ai_services_id)
+ }
+ search_service = {
+ id = one(module.ai_search[*].search_service_id)
+ target = "https://${one(module.ai_search[*].search_service_name)}.search.windows.net"
+ }
+ cosmos_db = {
+ id = one(module.cosmos_db[*].cosmosdb_account_id)
+ target = one(module.cosmos_db[*].cosmosdb_account_endpoint)
+ }
+ storage_account = {
+ id = one(module.storage_account_aifoundry[*].storage_account_id)
+ target = one(module.storage_account_aifoundry[*].storage_account_primary_blob_endpoint)
+ }
+ }
+ sensitive = false
+ depends_on = [
+ # one(module.ai_foundry_account[*].ai_services_setup_completed),
+ ]
+}
diff --git a/modules/core/storage.tf b/modules/core/storage.tf
index 93a591d3..3275e239 100644
--- a/modules/core/storage.tf
+++ b/modules/core/storage.tf
@@ -217,3 +217,49 @@ module "storage_account_workspace" {
private_dns_zone_id_dfs = var.private_dns_zone_id_dfs
customer_managed_key = var.customer_managed_key
}
+
+module "storage_account_aifoundry" {
+ source = "github.com/PerfectThymeTech/terraform-azurerm-modules//modules/storage?ref=main"
+ providers = {
+ azurerm = azurerm
+ time = time
+ }
+
+ count = var.ai_foundry_account_details.enabled ? 1 : 0
+
+ location = var.location
+ resource_group_name = one(azurerm_resource_group.resource_group_ai[*].name)
+ tags = var.tags
+
+ storage_account_name = replace("${local.prefix}-stg-aif", "-", "")
+ storage_access_tier = "Hot"
+ storage_account_type = "StorageV2"
+ storage_account_tier = "Standard"
+ storage_account_replication_type = var.zone_redundancy_enabled ? "ZRS" : "LRS"
+ storage_blob_change_feed_enabled = false
+ storage_blob_container_delete_retention_in_days = 30
+ storage_blob_delete_retention_in_days = 30
+ storage_blob_cors_rules = {}
+ storage_blob_last_access_time_enabled = false
+ storage_blob_versioning_enabled = false
+ storage_is_hns_enabled = false
+ storage_network_bypass = ["AzureServices"]
+ storage_network_private_link_access = local.storage_network_private_link_access
+ storage_public_network_access_enabled = true
+ storage_nfsv3_enabled = false
+ storage_sftp_enabled = false
+ storage_shared_access_key_enabled = false
+ storage_container_names = []
+ storage_static_website = []
+ diagnostics_configurations = var.diagnostics_configurations
+ subnet_id = var.subnet_id_storage
+ connectivity_delay_in_seconds = var.connectivity_delay_in_seconds
+ private_endpoint_subresource_names = ["blob", ]
+ private_dns_zone_id_blob = var.private_dns_zone_id_blob
+ private_dns_zone_id_file = ""
+ private_dns_zone_id_table = ""
+ private_dns_zone_id_queue = ""
+ private_dns_zone_id_web = ""
+ private_dns_zone_id_dfs = ""
+ customer_managed_key = var.customer_managed_key
+}
diff --git a/modules/core/variables.tf b/modules/core/variables.tf
index bc015352..595e2492 100644
--- a/modules/core/variables.tf
+++ b/modules/core/variables.tf
@@ -86,6 +86,25 @@ variable "fabric_capacity_details" {
}
}
+variable "ai_foundry_account_details" {
+ description = "Specifies the ai foundry configuration."
+ type = object({
+ enabled = optional(bool, false)
+ search_service = optional(object({
+ sku = optional(string, "basic")
+ semantic_search_sku = optional(string, "standard")
+ partition_count = optional(number, 1)
+ replica_count = optional(number, 1)
+ }), {})
+ cosmos_db = optional(object({
+ consistency_level = optional(string, "Session")
+ }), {})
+ })
+ sensitive = false
+ nullable = false
+ default = {}
+}
+
# HA/DR variables
variable "zone_redundancy_enabled" {
description = "Specifies whether zone-redundancy should be enabled for all resources."
@@ -169,6 +188,16 @@ variable "subnet_id_consumption" {
}
}
+variable "subnet_id_aifoundry" {
+ description = "Specifies the id of the ai foundry subnet used for the agent service."
+ type = string
+ sensitive = false
+ validation {
+ condition = var.subnet_id_aifoundry == "" || length(split("/", var.subnet_id_aifoundry)) == 11
+ error_message = "Please specify a valid resource ID."
+ }
+}
+
variable "subnet_id_engineering_private" {
description = "Specifies the id of the private subnet used for the databricks workspace for engineering."
type = string
@@ -268,6 +297,61 @@ variable "private_dns_zone_id_databricks" {
}
}
+variable "private_dns_zone_id_cognitive_account" {
+ description = "Specifies the resource ID of the private DNS zone for Azure Cognitive Services. Not required if DNS A-records get created via Azure Policy."
+ type = string
+ sensitive = false
+ default = ""
+ validation {
+ condition = var.private_dns_zone_id_cognitive_account == "" || (length(split("/", var.private_dns_zone_id_cognitive_account)) == 9 && (endswith(var.private_dns_zone_id_cognitive_account, "privatelink.cognitiveservices.azure.com")))
+ error_message = "Please specify a valid resource ID for the private DNS Zone."
+ }
+}
+
+variable "private_dns_zone_id_open_ai" {
+ description = "Specifies the resource ID of the private DNS zone for Azure Open AI. Not required if DNS A-records get created via Azure Policy."
+ type = string
+ sensitive = false
+ default = ""
+ validation {
+ condition = var.private_dns_zone_id_open_ai == "" || (length(split("/", var.private_dns_zone_id_open_ai)) == 9 && (endswith(var.private_dns_zone_id_open_ai, "privatelink.openai.azure.com")))
+ error_message = "Please specify a valid resource ID for the private DNS Zone."
+ }
+}
+
+variable "private_dns_zone_id_ai_services" {
+ description = "Specifies the resource ID of the private DNS zone for Azure Foundry (AI Services). Not required if DNS A-records get created via Azure Policy."
+ type = string
+ sensitive = false
+ default = ""
+ validation {
+ condition = var.private_dns_zone_id_ai_services == "" || (length(split("/", var.private_dns_zone_id_ai_services)) == 9 && endswith(var.private_dns_zone_id_ai_services, "privatelink.services.ai.azure.com"))
+ error_message = "Please specify a valid resource ID for the private DNS Zone."
+ }
+}
+
+variable "private_dns_zone_id_search_service" {
+ description = "Specifies the resource ID of the private DNS zone for Azure Cognitive Search endpoints. Not required if DNS A-records get created via Azure Policy."
+ type = string
+ sensitive = false
+ default = ""
+ validation {
+ condition = var.private_dns_zone_id_search_service == "" || (length(split("/", var.private_dns_zone_id_search_service)) == 9 && endswith(var.private_dns_zone_id_search_service, "privatelink.search.windows.net"))
+ error_message = "Please specify a valid resource ID for the private DNS Zone."
+ }
+}
+
+variable "private_dns_zone_id_cosmos_sql" {
+ description = "Specifies the resource ID of the private DNS zone for cosmos db sql. Not required if DNS A-records get created via Azure Policy."
+ type = string
+ sensitive = false
+ default = ""
+ validation {
+ condition = var.private_dns_zone_id_cosmos_sql == "" || (length(split("/", var.private_dns_zone_id_cosmos_sql)) == 9 && endswith(var.private_dns_zone_id_cosmos_sql, "privatelink.documents.azure.com"))
+ error_message = "Please specify a valid resource ID for the private DNS Zone."
+ }
+}
+
# Customer-managed key variables
variable "customer_managed_key" {
description = "Specifies the customer managed key configurations."
diff --git a/modules/dataapplication/aifoundry.tf b/modules/dataapplication/aifoundry.tf
new file mode 100644
index 00000000..a26a95b0
--- /dev/null
+++ b/modules/dataapplication/aifoundry.tf
@@ -0,0 +1,24 @@
+resource "azapi_resource" "ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ type = "Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview"
+ name = local.prefix
+ location = var.location
+ parent_id = var.ai_foundry_account_details.ai_foundry_account.id
+ identity {
+ type = "SystemAssigned"
+ }
+
+ body = {
+ properties = {
+ description = "Azure AI Foundry Project - ${local.prefix}"
+ displayName = "${local.prefix} Project"
+ }
+ }
+
+ response_export_values = ["properties.internalId"]
+ schema_validation_enabled = true
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+}
diff --git a/modules/dataapplication/aifoundry_capabilityhost.tf b/modules/dataapplication/aifoundry_capabilityhost.tf
new file mode 100644
index 00000000..08dad602
--- /dev/null
+++ b/modules/dataapplication/aifoundry_capabilityhost.tf
@@ -0,0 +1,32 @@
+resource "azapi_resource" "ai_foundry_project_capability_hosts" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ type = "Microsoft.CognitiveServices/accounts/projects/capabilityHosts@2025-04-01-preview"
+ name = "default-project-${local.prefix}"
+ parent_id = one(azapi_resource.ai_foundry_project[*].id)
+
+ body = {
+ properties = {
+ aiServicesConnections = slice(local.ai_foundry_project_connection_openai_names, 0, 1)
+ capabilityHostKind = "Agents"
+ storageConnections = [one(azapi_resource.ai_foundry_project_connection_storage_account[*].name)]
+ threadStorageConnections = [one(azapi_resource.ai_foundry_project_connection_cosmosdb_account[*].name)]
+ vectorStoreConnections = [one(azapi_resource.ai_foundry_project_connection_search_service_account[*].name)]
+ }
+ }
+
+ response_export_values = []
+ schema_validation_enabled = false
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+
+ depends_on = [
+ azurerm_role_assignment.role_assignment_storage_account_aifoundry_blob_data_contributor_ai_foundry_project,
+ azurerm_role_assignment.role_assignment_storage_account_aifoundry_blob_data_owner_ai_foundry_project,
+ azurerm_role_assignment.role_assignment_cosmosdb_account_operator_ai_foundry_project,
+ azurerm_role_assignment.role_assignment_search_service_account_search_index_data_contributor_ai_foundry_project,
+ azurerm_role_assignment.role_assignment_search_service_account_search_service_contributor_ai_foundry_project,
+ azurerm_role_assignment.role_assignment_ai_service_ai_foundry_project,
+ ]
+}
diff --git a/modules/dataapplication/aifoundry_connections.tf b/modules/dataapplication/aifoundry_connections.tf
new file mode 100644
index 00000000..d9d38911
--- /dev/null
+++ b/modules/dataapplication/aifoundry_connections.tf
@@ -0,0 +1,359 @@
+# Cosmos DB connections
+resource "azapi_resource" "ai_foundry_project_connection_cosmosdb_account" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ type = "Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview"
+ name = "cosmosdb-account-${local.prefix}"
+ parent_id = one(azapi_resource.ai_foundry_project[*].id)
+
+ body = {
+ properties = {
+ authType = "AAD"
+ category = "CosmosDb"
+ error = null
+ expiryTime = null
+ isSharedToAll = false
+ metadata = {
+ ApiType = "Azure"
+ ResourceId = var.ai_foundry_account_details.cosmos_db.id
+ location = var.location
+ }
+ peRequirement = "NotRequired"
+ target = var.ai_foundry_account_details.cosmos_db.target
+ useWorkspaceManagedIdentity = true
+ }
+ }
+
+ response_export_values = []
+ schema_validation_enabled = false
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+}
+
+# Search connections
+resource "azapi_resource" "ai_foundry_project_connection_search_service_account" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ type = "Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview"
+ name = "cognitivesearch-account-${local.prefix}"
+ parent_id = one(azapi_resource.ai_foundry_project[*].id)
+
+ body = {
+ properties = {
+ authType = "AAD"
+ category = "CognitiveSearch"
+ error = null
+ expiryTime = null
+ isSharedToAll = false
+ metadata = {
+ ApiType = "Azure"
+ ResourceId = var.ai_foundry_account_details.search_service.id
+ location = var.location
+ }
+ peRequirement = "NotRequired"
+ target = var.ai_foundry_account_details.search_service.target
+ useWorkspaceManagedIdentity = true
+ }
+ }
+
+ response_export_values = []
+ schema_validation_enabled = false
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+}
+
+resource "azapi_resource" "ai_foundry_project_connection_search_service" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled && var.search_service_details.enabled ? 1 : 0
+
+ type = "Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview"
+ name = "cognitivesearch-${local.prefix}"
+ parent_id = one(azapi_resource.ai_foundry_project[*].id)
+
+ body = {
+ properties = {
+ authType = "AAD"
+ category = "CognitiveSearch"
+ error = null
+ expiryTime = null
+ isSharedToAll = false
+ metadata = {
+ ApiType = "Azure"
+ ResourceId = one(module.ai_search[*].search_service_id)
+ location = var.location
+ }
+ peRequirement = "NotRequired"
+ target = "https://${one(module.ai_search[*].search_service_name)}.search.windows.net"
+ useWorkspaceManagedIdentity = true
+ }
+ }
+
+ response_export_values = []
+ schema_validation_enabled = false
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+}
+
+# Open AI connections
+resource "azapi_resource" "ai_foundry_project_connection_openai" {
+ for_each = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? local.ai_foundry_project_connection_openai : {}
+
+ type = "Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview"
+ name = "openai-${local.prefix}-${each.key}"
+ parent_id = one(azapi_resource.ai_foundry_project[*].id)
+
+ body = {
+ properties = {
+ authType = "AAD"
+ category = "AzureOpenAI"
+ error = null
+ expiryTime = null
+ isSharedToAll = false
+ metadata = {
+ ApiType = "Azure"
+ ResourceId = each.value.id
+ location = each.value.location
+ }
+ peRequirement = "NotRequired"
+ target = each.value.target
+ useWorkspaceManagedIdentity = true
+ }
+ }
+
+ response_export_values = []
+ schema_validation_enabled = false
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+}
+
+# Application Insights connections
+resource "azapi_resource" "ai_foundry_project_connection_appinsights" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ type = "Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview"
+ name = "appinsights-${local.prefix}"
+ parent_id = one(azapi_resource.ai_foundry_project[*].id)
+
+ body = {
+ properties = {
+ authType = "ApiKey"
+ category = "AppInsights"
+ credentials = {
+ key = module.application_insights.application_insights_connection_string
+ }
+ error = null
+ expiryTime = null
+ isSharedToAll = false
+ metadata = {
+ ApiType = "Azure"
+ ResourceId = module.application_insights.application_insights_id
+ location = var.location
+ }
+ peRequirement = "NotRequired"
+ target = module.application_insights.application_insights_id
+ useWorkspaceManagedIdentity = true
+ }
+ }
+
+ response_export_values = []
+ schema_validation_enabled = false
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+}
+
+# Storage connections
+resource "azapi_resource" "ai_foundry_project_connection_storage_account" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ type = "Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview"
+ name = "azurestorageaccount-account-${local.prefix}"
+ parent_id = one(azapi_resource.ai_foundry_project[*].id)
+
+ body = {
+ properties = {
+ authType = "AAD"
+ category = "AzureStorageAccount"
+ error = null
+ expiryTime = null
+ isSharedToAll = false
+ metadata = {
+ ApiType = "Azure"
+ ResourceId = var.ai_foundry_account_details.storage_account.id
+ location = var.location
+ }
+ peRequirement = "NotRequired"
+ target = var.ai_foundry_account_details.storage_account.target
+ useWorkspaceManagedIdentity = true
+ }
+ }
+
+ response_export_values = []
+ schema_validation_enabled = false
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+}
+
+resource "azapi_resource" "ai_foundry_project_connection_storage_provider" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ type = "Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview"
+ name = "azurestorageaccount-provider-${local.prefix}"
+ parent_id = one(azapi_resource.ai_foundry_project[*].id)
+
+ body = {
+ properties = {
+ authType = "AAD"
+ category = "AzureStorageAccount"
+ error = null
+ expiryTime = null
+ isSharedToAll = false
+ metadata = {
+ ApiType = "Azure"
+ ResourceId = var.storage_account_ids.provider
+ location = var.location
+ }
+ peRequirement = "NotRequired"
+ target = "https://${reverse(split("/", var.storage_account_ids.provider))[0]}.blob.core.windows.net/"
+ useWorkspaceManagedIdentity = true
+ }
+ }
+
+ response_export_values = []
+ schema_validation_enabled = false
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+}
+
+resource "azapi_resource" "ai_foundry_project_connection_storage_raw" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ type = "Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview"
+ name = "azurestorageaccount-raw-${local.prefix}"
+ parent_id = one(azapi_resource.ai_foundry_project[*].id)
+
+ body = {
+ properties = {
+ authType = "AAD"
+ category = "AzureStorageAccount"
+ error = null
+ expiryTime = null
+ isSharedToAll = false
+ metadata = {
+ ApiType = "Azure"
+ ResourceId = var.storage_account_ids.raw
+ location = var.location
+ }
+ peRequirement = "NotRequired"
+ target = "https://${reverse(split("/", var.storage_account_ids.raw))[0]}.blob.core.windows.net/"
+ useWorkspaceManagedIdentity = true
+ }
+ }
+
+ response_export_values = []
+ schema_validation_enabled = false
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+}
+
+resource "azapi_resource" "ai_foundry_project_connection_storage_enriched" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ type = "Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview"
+ name = "azurestorageaccount-enriched-${local.prefix}"
+ parent_id = one(azapi_resource.ai_foundry_project[*].id)
+
+ body = {
+ properties = {
+ authType = "AAD"
+ category = "AzureStorageAccount"
+ error = null
+ expiryTime = null
+ isSharedToAll = false
+ metadata = {
+ ApiType = "Azure"
+ ResourceId = var.storage_account_ids.enriched
+ location = var.location
+ }
+ peRequirement = "NotRequired"
+ target = "https://${reverse(split("/", var.storage_account_ids.enriched))[0]}.blob.core.windows.net/"
+ useWorkspaceManagedIdentity = true
+ }
+ }
+
+ response_export_values = []
+ schema_validation_enabled = false
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+}
+
+resource "azapi_resource" "ai_foundry_project_connection_storage_curated" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ type = "Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview"
+ name = "azurestorageaccount-curated-${local.prefix}"
+ parent_id = one(azapi_resource.ai_foundry_project[*].id)
+
+ body = {
+ properties = {
+ authType = "AAD"
+ category = "AzureStorageAccount"
+ error = null
+ expiryTime = null
+ isSharedToAll = false
+ metadata = {
+ ApiType = "Azure"
+ ResourceId = var.storage_account_ids.curated
+ location = var.location
+ }
+ peRequirement = "NotRequired"
+ target = "https://${reverse(split("/", var.storage_account_ids.curated))[0]}.blob.core.windows.net/"
+ useWorkspaceManagedIdentity = true
+ }
+ }
+
+ response_export_values = []
+ schema_validation_enabled = false
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+}
+
+resource "azapi_resource" "ai_foundry_project_connection_storage_workspace" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ type = "Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview"
+ name = "azurestorageaccount-workspace-${local.prefix}"
+ parent_id = one(azapi_resource.ai_foundry_project[*].id)
+
+ body = {
+ properties = {
+ authType = "AAD"
+ category = "AzureStorageAccount"
+ error = null
+ expiryTime = null
+ isSharedToAll = false
+ metadata = {
+ ApiType = "Azure"
+ ResourceId = var.storage_account_ids.workspace
+ location = var.location
+ }
+ peRequirement = "NotRequired"
+ target = "https://${reverse(split("/", var.storage_account_ids.workspace))[0]}.blob.core.windows.net/"
+ useWorkspaceManagedIdentity = true
+ }
+ }
+
+ response_export_values = []
+ schema_validation_enabled = false
+ locks = []
+ ignore_casing = false
+ ignore_missing_property = true
+}
diff --git a/modules/dataapplication/applicationinsights.tf b/modules/dataapplication/applicationinsights.tf
new file mode 100644
index 00000000..dacdba4a
--- /dev/null
+++ b/modules/dataapplication/applicationinsights.tf
@@ -0,0 +1,14 @@
+module "application_insights" {
+ source = "github.com/PerfectThymeTech/terraform-azurerm-modules//modules/applicationinsights?ref=main"
+ providers = {
+ azurerm = azurerm
+ }
+
+ location = var.location
+ resource_group_name = azurerm_resource_group.resource_group_app_monitoring.name
+ tags = local.tags
+ application_insights_name = "${local.prefix}-ai001"
+ application_insights_application_type = "web"
+ application_insights_log_analytics_workspace_id = var.log_analytics_workspace_id
+ diagnostics_configurations = []
+}
diff --git a/modules/dataapplication/data.tf b/modules/dataapplication/data.tf
index 4f014d3d..41a74e92 100644
--- a/modules/dataapplication/data.tf
+++ b/modules/dataapplication/data.tf
@@ -74,3 +74,11 @@ data "azuread_service_principal" "service_principal_data_provider" {
display_name = each.value.service_principal_name
}
+
+data "azurerm_cosmosdb_sql_role_definition" "cosmosdb_sql_role_definition" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ resource_group_name = split("/", var.ai_foundry_account_details.cosmos_db.id)[4]
+ account_name = reverse(split("/", var.ai_foundry_account_details.cosmos_db.id))[0]
+ role_definition_id = "00000000-0000-0000-0000-000000000002"
+}
diff --git a/modules/dataapplication/locals.tf b/modules/dataapplication/locals.tf
index c98b56ec..3b6ab822 100644
--- a/modules/dataapplication/locals.tf
+++ b/modules/dataapplication/locals.tf
@@ -174,4 +174,24 @@ locals {
local.search_service_shared_default_private_links,
local.search_service_shared_ai_service_private_links
)
+
+ # AI Foundry locals
+ ai_foundry_project_internal_id = one(azapi_resource.ai_foundry_project[*].output.properties.internalId)
+ ai_foundry_project_workspace_id = "${substr(local.ai_foundry_project_internal_id, 0, 8)}-${substr(local.ai_foundry_project_internal_id, 8, 4)}-${substr(local.ai_foundry_project_internal_id, 12, 4)}-${substr(local.ai_foundry_project_internal_id, 16, 4)}-${substr(local.ai_foundry_project_internal_id, 20, 12)}"
+ ai_foundry_project_connection_openai = {
+ for key, value in var.ai_services :
+ key => {
+ id = module.ai_service[key].cognitive_account_id
+ target = module.ai_service[key].cognitive_account_endpoint
+ location = value.location
+ } if value.kind == "OpenAI"
+ }
+ ai_foundry_project_connection_openai_names = [
+ for key, value in local.ai_foundry_project_connection_openai :
+ azapi_resource.ai_foundry_project_connection_openai[key].name
+ ]
+ cosmosdb_account_database_name = "enterprise_memory"
+ cosmosdb_account_database_container_thread_message_name = "thread-message-store"
+ cosmosdb_account_database_container_system_thread_message_name = "system-thread-message-store"
+ cosmosdb_account_database_container_agent_entity_store_name = "agent-entity-store"
}
diff --git a/modules/dataapplication/roleassignments_admin.tf b/modules/dataapplication/roleassignments_admin.tf
index cfe5d6a4..2421543c 100644
--- a/modules/dataapplication/roleassignments_admin.tf
+++ b/modules/dataapplication/roleassignments_admin.tf
@@ -90,6 +90,27 @@ resource "azurerm_role_assignment" "role_assignment_search_service_contributor_a
principal_type = "Group"
}
+# AI Foundry role assignments
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_account_reader_admin" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment to ai foundry account."
+ scope = var.ai_foundry_account_details.ai_foundry_account.id
+ role_definition_name = "Reader"
+ principal_id = data.azuread_group.group_admin.object_id
+ principal_type = "Group"
+}
+
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_project_manager_admin" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment to ai foundry project."
+ scope = one(azapi_resource.ai_foundry_project[*].id)
+ role_definition_name = "Azure AI Project Manager"
+ principal_id = data.azuread_group.group_admin.object_id
+ principal_type = "Group"
+}
+
# Data factory role assignments
resource "azurerm_role_assignment" "role_assignment_data_factory_data_factory_contributor_admin" {
count = var.data_factory_details.enabled ? 1 : 0
diff --git a/modules/dataapplication/roleassignments_developer.tf b/modules/dataapplication/roleassignments_developer.tf
index ca40455e..c682a018 100644
--- a/modules/dataapplication/roleassignments_developer.tf
+++ b/modules/dataapplication/roleassignments_developer.tf
@@ -94,6 +94,27 @@ resource "azurerm_role_assignment" "role_assignment_search_service_contributor_d
principal_type = "Group"
}
+# AI Foundry role assignments
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_account_reader_developer" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled && var.developer_group_name != "" ? 1 : 0
+
+ description = "Role assignment to ai foundry account."
+ scope = var.ai_foundry_account_details.ai_foundry_account.id
+ role_definition_name = "Reader"
+ principal_id = one(data.azuread_group.group_developer[*].object_id)
+ principal_type = "Group"
+}
+
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_project_manager_developer" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled && var.developer_group_name != "" ? 1 : 0
+
+ description = "Role assignment to ai foundry project."
+ scope = one(azapi_resource.ai_foundry_project[*].id)
+ role_definition_name = "Azure AI Project Manager"
+ principal_id = one(data.azuread_group.group_developer[*].object_id)
+ principal_type = "Group"
+}
+
# Data factory role assignments
resource "azurerm_role_assignment" "role_assignment_data_factory_data_factory_contributor_developer" {
count = var.developer_group_name != "" && var.data_factory_details.enabled ? 1 : 0
diff --git a/modules/dataapplication/roleassignments_msi_aifoundry.tf b/modules/dataapplication/roleassignments_msi_aifoundry.tf
new file mode 100644
index 00000000..a1fd1f12
--- /dev/null
+++ b/modules/dataapplication/roleassignments_msi_aifoundry.tf
@@ -0,0 +1,199 @@
+# Capability Host Role Assignments
+resource "azurerm_role_assignment" "role_assignment_storage_account_aifoundry_blob_data_contributor_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment for storage write operations."
+ scope = var.ai_foundry_account_details.storage_account.id
+ role_definition_name = "Storage Blob Data Contributor"
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+}
+
+resource "azurerm_role_assignment" "role_assignment_storage_account_aifoundry_blob_data_owner_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment for storage write operations."
+ scope = var.ai_foundry_account_details.storage_account.id
+ role_definition_name = "Storage Blob Data Owner"
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+ condition_version = "2.0"
+ condition = <<-EOT
+ (
+ (
+ !(ActionMatches{'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/tags/read'})
+ AND
+ !(ActionMatches{'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/filter/action'})
+ AND
+ !(ActionMatches{'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/tags/write'})
+ )
+ OR
+ (
+ @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringStartsWithIgnoreCase '${local.ai_foundry_project_workspace_id}'
+ AND
+ @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringLikeIgnoreCase '*-azureml-agent'
+ )
+ )
+ EOT
+}
+
+resource "azurerm_role_assignment" "role_assignment_cosmosdb_account_operator_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment for cosmos write operations."
+ scope = var.ai_foundry_account_details.cosmos_db.id
+ role_definition_name = "Cosmos DB Operator"
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+}
+
+resource "azurerm_cosmosdb_sql_role_assignment" "cosmosdb_sql_role_assignment_thread_message_store_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ resource_group_name = split("/", var.ai_foundry_account_details.cosmos_db.id)[4]
+ account_name = reverse(split("/", var.ai_foundry_account_details.cosmos_db.id))[0]
+ role_definition_id = one(data.azurerm_cosmosdb_sql_role_definition.cosmosdb_sql_role_definition[*].id)
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ scope = "${var.ai_foundry_account_details.cosmos_db.id}/dbs/${local.cosmosdb_account_database_name}/colls/${local.ai_foundry_project_workspace_id}-${local.cosmosdb_account_database_container_thread_message_name}"
+
+ depends_on = [
+ azapi_resource.ai_foundry_project_capability_hosts,
+ ]
+}
+
+resource "azurerm_cosmosdb_sql_role_assignment" "cosmosdb_sql_role_assignment_system_thread_message_store_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ resource_group_name = split("/", var.ai_foundry_account_details.cosmos_db.id)[4]
+ account_name = reverse(split("/", var.ai_foundry_account_details.cosmos_db.id))[0]
+ role_definition_id = one(data.azurerm_cosmosdb_sql_role_definition.cosmosdb_sql_role_definition[*].id)
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ scope = "${var.ai_foundry_account_details.cosmos_db.id}/dbs/${local.cosmosdb_account_database_name}/colls/${local.ai_foundry_project_workspace_id}-${local.cosmosdb_account_database_container_system_thread_message_name}"
+
+ depends_on = [
+ azapi_resource.ai_foundry_project_capability_hosts,
+ ]
+}
+
+resource "azurerm_cosmosdb_sql_role_assignment" "cosmosdb_sql_role_assignment_agent_entity_store_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ resource_group_name = split("/", var.ai_foundry_account_details.cosmos_db.id)[4]
+ account_name = reverse(split("/", var.ai_foundry_account_details.cosmos_db.id))[0]
+ role_definition_id = one(data.azurerm_cosmosdb_sql_role_definition.cosmosdb_sql_role_definition[*].id)
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ scope = "${var.ai_foundry_account_details.cosmos_db.id}/dbs/${local.cosmosdb_account_database_name}/colls/${local.ai_foundry_project_workspace_id}-${local.cosmosdb_account_database_container_agent_entity_store_name}"
+
+ depends_on = [
+ azapi_resource.ai_foundry_project_capability_hosts,
+ ]
+}
+
+resource "azurerm_role_assignment" "role_assignment_search_service_account_search_index_data_contributor_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment for ai search write operations."
+ scope = var.ai_foundry_account_details.search_service.id
+ role_definition_name = "Search Index Data Contributor"
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+}
+
+resource "azurerm_role_assignment" "role_assignment_search_service_account_search_service_contributor_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment for ai search write operations."
+ scope = var.ai_foundry_account_details.search_service.id
+ role_definition_name = "Search Service Contributor"
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+}
+
+# Resource group role assignments
+
+# Key vault role assignments
+
+# Databricks role assignments
+
+# AI service role assignments
+resource "azurerm_role_assignment" "role_assignment_ai_service_ai_foundry_project" {
+ for_each = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? var.ai_services : {}
+
+ description = "Role assignment to the ai services."
+ scope = module.ai_service[each.key].cognitive_account_id
+ role_definition_name = local.ai_service_kind_role_map_write[each.value.kind]
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+}
+
+# AI search service role assignment
+resource "azurerm_role_assignment" "role_assignment_search_service_index_data_contributor_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled && var.search_service_details.enabled ? 1 : 0
+
+ description = "Role assignment to create or manage objects in AI Search."
+ scope = one(module.ai_search[*].search_service_id)
+ role_definition_name = "Search Index Data Contributor"
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+}
+
+resource "azurerm_role_assignment" "role_assignment_search_service_contributor_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled && var.search_service_details.enabled ? 1 : 0
+
+ description = "Role assignment to load documents and run indexing jobs in AI Search."
+ scope = one(module.ai_search[*].search_service_id)
+ role_definition_name = "Search Service Contributor"
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+}
+
+# Storage Role Assignments
+resource "azurerm_role_assignment" "role_assignment_storage_container_provider_blob_data_contributor_ai_foundry_project" {
+ for_each = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? var.data_provider_details : {}
+
+ description = "Role assignment to provider storage account container to read and write data."
+ scope = azurerm_storage_container.storage_container_provider[each.key].id
+ role_definition_name = "Storage Blob Data Contributor"
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+}
+
+resource "azurerm_role_assignment" "role_assignment_storage_container_raw_blob_data_contributor_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment to raw storage account container to read and write data."
+ scope = azurerm_storage_container.storage_container_raw.id
+ role_definition_name = "Storage Blob Data Contributor"
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+}
+
+resource "azurerm_role_assignment" "role_assignment_storage_container_enriched_blob_data_contributor_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment to enriched storage account container to read and write data."
+ scope = azurerm_storage_container.storage_container_enriched.id
+ role_definition_name = "Storage Blob Data Contributor"
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+}
+
+resource "azurerm_role_assignment" "role_assignment_storage_container_curated_blob_data_contributor_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment to curated storage account container to read and write data."
+ scope = azurerm_storage_container.storage_container_curated.id
+ role_definition_name = "Storage Blob Data Contributor"
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+}
+
+resource "azurerm_role_assignment" "role_assignment_storage_container_workspace_blob_data_contributor_ai_foundry_project" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment to workspace storage account container to read and write data."
+ scope = azurerm_storage_container.storage_container_workspace.id
+ role_definition_name = "Storage Blob Data Contributor"
+ principal_id = one(azapi_resource.ai_foundry_project[*].identity[0].principal_id)
+ principal_type = "ServicePrincipal"
+}
diff --git a/modules/dataapplication/roleassignments_msi_databricksaccessconnector.tf b/modules/dataapplication/roleassignments_msi_databricksaccessconnector.tf
index 70bb0d6d..d975b8f9 100644
--- a/modules/dataapplication/roleassignments_msi_databricksaccessconnector.tf
+++ b/modules/dataapplication/roleassignments_msi_databricksaccessconnector.tf
@@ -81,6 +81,27 @@ resource "azurerm_role_assignment" "role_assignment_search_service_contributor_a
principal_type = "ServicePrincipal"
}
+# AI Foundry role assignments
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_account_reader_accessconnector" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment to ai foundry account."
+ scope = var.ai_foundry_account_details.ai_foundry_account.id
+ role_definition_name = "Reader"
+ principal_id = module.databricks_access_connector.databricks_access_connector_principal_id
+ principal_type = "ServicePrincipal"
+}
+
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_project_manager_accessconnector" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment to ai foundry project."
+ scope = one(azapi_resource.ai_foundry_project[*].id)
+ role_definition_name = "Azure AI Project Manager"
+ principal_id = module.databricks_access_connector.databricks_access_connector_principal_id
+ principal_type = "ServicePrincipal"
+}
+
# Storage Role Assignments
resource "azurerm_role_assignment" "role_assignment_storage_account_provider_event_subscription_contributor_accessconnector" {
description = "Role assignment to provider storage account to create event triggers."
diff --git a/modules/dataapplication/roleassignments_msi_datafactory.tf b/modules/dataapplication/roleassignments_msi_datafactory.tf
index 3b393cf9..813fa86a 100644
--- a/modules/dataapplication/roleassignments_msi_datafactory.tf
+++ b/modules/dataapplication/roleassignments_msi_datafactory.tf
@@ -93,6 +93,27 @@ resource "azurerm_role_assignment" "role_assignment_search_service_contributor_d
principal_type = "ServicePrincipal"
}
+# AI Foundry role assignments
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_account_reader_datafactory" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled && var.data_factory_details.enabled ? 1 : 0
+
+ description = "Role assignment to ai foundry account."
+ scope = var.ai_foundry_account_details.ai_foundry_account.id
+ role_definition_name = "Reader"
+ principal_id = one(module.data_factory[*].data_factory_principal_id)
+ principal_type = "ServicePrincipal"
+}
+
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_project_manager_datafactory" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled && var.data_factory_details.enabled ? 1 : 0
+
+ description = "Role assignment to ai foundry project."
+ scope = one(azapi_resource.ai_foundry_project[*].id)
+ role_definition_name = "Azure AI Project Manager"
+ principal_id = one(module.data_factory[*].data_factory_principal_id)
+ principal_type = "ServicePrincipal"
+}
+
# Storage Role Assignments
resource "azurerm_role_assignment" "role_assignment_storage_account_provider_event_subscription_contributor_datafactory" {
count = var.data_factory_details.enabled ? 1 : 0
diff --git a/modules/dataapplication/roleassignments_reader.tf b/modules/dataapplication/roleassignments_reader.tf
index dc1ea6ec..7646c60e 100644
--- a/modules/dataapplication/roleassignments_reader.tf
+++ b/modules/dataapplication/roleassignments_reader.tf
@@ -57,6 +57,27 @@ resource "azurerm_role_assignment" "role_assignment_databricks_workspace_reader_
# AI search service role assignment
+# AI Foundry role assignments
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_account_reader_reader" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled && var.reader_group_name != "" ? 1 : 0
+
+ description = "Role assignment to ai foundry account."
+ scope = var.ai_foundry_account_details.ai_foundry_account.id
+ role_definition_name = "Reader"
+ principal_id = one(data.azuread_group.group_reader[*].object_id)
+ principal_type = "Group"
+}
+
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_project_reader_reader" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled && var.reader_group_name != "" ? 1 : 0
+
+ description = "Role assignment to ai foundry project."
+ scope = one(azapi_resource.ai_foundry_project[*].id)
+ role_definition_name = "Reader"
+ principal_id = one(data.azuread_group.group_reader[*].object_id)
+ principal_type = "Group"
+}
+
# Data factory role assignments
# Fabric role assignments
diff --git a/modules/dataapplication/roleassignments_service_principal.tf b/modules/dataapplication/roleassignments_service_principal.tf
index 9ca3c194..b0014da2 100644
--- a/modules/dataapplication/roleassignments_service_principal.tf
+++ b/modules/dataapplication/roleassignments_service_principal.tf
@@ -93,6 +93,27 @@ resource "azurerm_role_assignment" "role_assignment_search_service_contributor_s
principal_type = "ServicePrincipal"
}
+# AI Foundry role assignments
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_account_reader_service_principal" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled && var.service_principal_name != "" ? 1 : 0
+
+ description = "Role assignment to ai foundry account."
+ scope = var.ai_foundry_account_details.ai_foundry_account.id
+ role_definition_name = "Reader"
+ principal_id = one(data.azuread_service_principal.service_principal[*].object_id)
+ principal_type = "ServicePrincipal"
+}
+
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_project_manager_service_principal" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled && var.service_principal_name != "" ? 1 : 0
+
+ description = "Role assignment to ai foundry project."
+ scope = one(azapi_resource.ai_foundry_project[*].id)
+ role_definition_name = "Azure AI Project Manager"
+ principal_id = one(data.azuread_service_principal.service_principal[*].object_id)
+ principal_type = "ServicePrincipal"
+}
+
# Data factory role assignments
resource "azurerm_role_assignment" "role_assignment_data_factory_data_factory_contributor_service_principal" {
count = var.service_principal_name != "" && var.data_factory_details.enabled ? 1 : 0
diff --git a/modules/dataapplication/roleassignments_uai.tf b/modules/dataapplication/roleassignments_uai.tf
index e73c760e..d4aa2b36 100644
--- a/modules/dataapplication/roleassignments_uai.tf
+++ b/modules/dataapplication/roleassignments_uai.tf
@@ -81,6 +81,27 @@ resource "azurerm_role_assignment" "role_assignment_search_service_contributor_u
principal_type = "ServicePrincipal"
}
+# AI Foundry role assignments
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_account_reader_uai" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment to ai foundry account."
+ scope = var.ai_foundry_account_details.ai_foundry_account.id
+ role_definition_name = "Reader"
+ principal_id = module.user_assigned_identity.user_assigned_identity_principal_id
+ principal_type = "ServicePrincipal"
+}
+
+resource "azurerm_role_assignment" "role_assignment_ai_foundry_project_manager_uai" {
+ count = var.ai_foundry_project_details.enabled && var.ai_foundry_account_details.enabled ? 1 : 0
+
+ description = "Role assignment to ai foundry project."
+ scope = one(azapi_resource.ai_foundry_project[*].id)
+ role_definition_name = "Azure AI Project Manager"
+ principal_id = module.user_assigned_identity.user_assigned_identity_principal_id
+ principal_type = "ServicePrincipal"
+}
+
# Data factory role assignments
resource "azurerm_role_assignment" "role_assignment_data_factory_data_factory_contributor_uai" {
count = var.data_factory_details.enabled ? 1 : 0
diff --git a/modules/dataapplication/variables.tf b/modules/dataapplication/variables.tf
index 0610fc4a..b84b4d29 100644
--- a/modules/dataapplication/variables.tf
+++ b/modules/dataapplication/variables.tf
@@ -164,7 +164,7 @@ variable "search_service_details" {
sku = optional(string, "standard")
semantic_search_sku = optional(string, "standard")
partition_count = optional(number, 1)
- replica_count = optional(string, 1)
+ replica_count = optional(number, 1)
})
sensitive = false
nullable = false
@@ -207,6 +207,42 @@ variable "fabric_workspace_details" {
default = {}
}
+variable "ai_foundry_account_details" {
+ description = "Specifies the ai foundry account details."
+ type = object({
+ enabled = optional(bool, false)
+ ai_foundry_account = optional(object({
+ id = optional(string, "")
+ }), {})
+ search_service = optional(object({
+ id = optional(string, "")
+ target = optional(string, "")
+ }), {})
+ cosmos_db = optional(object({
+ id = optional(string, "")
+ target = optional(string, "")
+ }), {})
+ storage_account = optional(object({
+ id = optional(string, "")
+ target = optional(string, "")
+ }), {})
+ })
+ sensitive = false
+ nullable = false
+ default = {}
+}
+
+variable "ai_foundry_project_details" {
+ description = "Specifies the ai foundry project details."
+ type = object({
+ enabled = optional(bool, true)
+ # tbd
+ })
+ sensitive = false
+ nullable = false
+ default = {}
+}
+
variable "storage_dependencies" {
description = "Specifies a list of dependencies for storage resources."
type = list(bool)
@@ -224,6 +260,17 @@ variable "zone_redundancy_enabled" {
}
# Logging and monitoring variables
+variable "log_analytics_workspace_id" {
+ description = "Specifies the resource ID of a log analytics workspace for all diagnostic logs."
+ type = string
+ sensitive = false
+ default = ""
+ validation {
+ condition = length(split("/", var.log_analytics_workspace_id)) == 9 || var.log_analytics_workspace_id == ""
+ error_message = "Please specify a valid resource ID."
+ }
+}
+
variable "diagnostics_configurations" {
description = "Specifies the diagnostic configuration for the service."
type = list(object({
diff --git a/modules/databrickscore/locals.tf b/modules/databrickscore/locals.tf
index 6b54f9a8..ebfe5425 100644
--- a/modules/databrickscore/locals.tf
+++ b/modules/databrickscore/locals.tf
@@ -2,15 +2,15 @@ locals {
# General locals
prefix = "${lower(var.prefix)}-${var.environment}-core"
system_schema_names = [
- "access",
- # "billing", # billing system schema can only be enabled by Databricks
- "compute",
- "lakeflow",
+ # "access", # access system schema is automatically enabled by Databricks
+ # "billing", # billing system schema is automatically enabled by Databricks
+ # "compute", # compute system schema is automatically enabled by Databricks
+ # "lakeflow", # lakeflow system schema is automatically enabled by Databricks
# "lineage", # lineage system schema can only be enabled by Databricks
- "marketplace",
+ # "marketplace", # marketplace system schema can only be enabled by Databricks
# "query", # query system schema can only be enabled by Databricks
- "serving",
- "storage",
+ # "serving", # serving system schema is automatically enabled by Databricks
+ # "storage", # storage system schema is automatically enabled by Databricks
]
# Databricks locals
diff --git a/modules/databricksdataapplication/externallocation.tf b/modules/databricksdataapplication/externallocation.tf
index d392ad43..683bce60 100644
--- a/modules/databricksdataapplication/externallocation.tf
+++ b/modules/databricksdataapplication/externallocation.tf
@@ -69,3 +69,20 @@ resource "databricks_external_location" "external_location_workspace" {
skip_validation = false
url = "abfss://${local.storage_container_workspace.storage_container_name}@${local.storage_container_workspace.storage_account_name}.dfs.core.windows.net/"
}
+
+resource "databricks_workspace_binding" "workspace_binding_external_location_provider" {
+ for_each = merge([
+ for key, value in var.data_provider_details : {
+ for item in value.databricks_catalog.workspace_binding_catalog :
+ "${key}-${item}" => {
+ key = key
+ workspace_binding_catalog_workspace_id = item
+ } if value.databricks_catalog.enabled
+ }
+ ]...)
+
+ binding_type = "BINDING_TYPE_READ_WRITE"
+ securable_name = databricks_external_location.external_location_provider[each.value.key].name
+ securable_type = "external_location"
+ workspace_id = each.value.workspace_binding_catalog_workspace_id
+}
diff --git a/modules/databricksdataapplication/roleassignment_admin.tf b/modules/databricksdataapplication/roleassignment_admin.tf
index 1032b694..6e9c03e6 100644
--- a/modules/databricksdataapplication/roleassignment_admin.tf
+++ b/modules/databricksdataapplication/roleassignment_admin.tf
@@ -33,7 +33,7 @@ resource "databricks_grant" "grant_catalog_provider_admin" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
@@ -74,7 +74,7 @@ resource "databricks_grant" "grant_catalog_internal_admin" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
@@ -115,7 +115,7 @@ resource "databricks_grant" "grant_catalog_published_admin" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
diff --git a/modules/databricksdataapplication/roleassignment_developer.tf b/modules/databricksdataapplication/roleassignment_developer.tf
index 35b45390..7d5939c7 100644
--- a/modules/databricksdataapplication/roleassignment_developer.tf
+++ b/modules/databricksdataapplication/roleassignment_developer.tf
@@ -37,7 +37,7 @@ resource "databricks_grant" "grant_catalog_provider_developer" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
@@ -80,7 +80,7 @@ resource "databricks_grant" "grant_catalog_internal_developer" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
@@ -123,7 +123,7 @@ resource "databricks_grant" "grant_catalog_published_developer" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
diff --git a/modules/databricksdataapplication/roleassignment_service_principal.tf b/modules/databricksdataapplication/roleassignment_service_principal.tf
index b798edfb..eb8e57ed 100644
--- a/modules/databricksdataapplication/roleassignment_service_principal.tf
+++ b/modules/databricksdataapplication/roleassignment_service_principal.tf
@@ -37,7 +37,7 @@ resource "databricks_grant" "grant_catalog_provider_service_principal" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
@@ -80,7 +80,7 @@ resource "databricks_grant" "grant_catalog_internal_service_principal" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
@@ -123,7 +123,7 @@ resource "databricks_grant" "grant_catalog_published_service_principal" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
diff --git a/modules/databricksdataapplication/roleassignment_service_principal_datafactory.tf b/modules/databricksdataapplication/roleassignment_service_principal_datafactory.tf
index faa85ed7..cb0d9da7 100644
--- a/modules/databricksdataapplication/roleassignment_service_principal_datafactory.tf
+++ b/modules/databricksdataapplication/roleassignment_service_principal_datafactory.tf
@@ -37,7 +37,7 @@ resource "databricks_grant" "grant_catalog_provider_service_principal_data_facto
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
@@ -80,7 +80,7 @@ resource "databricks_grant" "grant_catalog_internal_service_principal_data_facto
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
@@ -123,7 +123,7 @@ resource "databricks_grant" "grant_catalog_published_service_principal_data_fact
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
diff --git a/modules/databricksdataapplication/roleassignment_service_principal_terraform_plan.tf b/modules/databricksdataapplication/roleassignment_service_principal_terraform_plan.tf
index 8cc751ac..e257ec37 100644
--- a/modules/databricksdataapplication/roleassignment_service_principal_terraform_plan.tf
+++ b/modules/databricksdataapplication/roleassignment_service_principal_terraform_plan.tf
@@ -26,7 +26,7 @@ resource "databricks_grant" "grant_catalog_provider_service_principal_terraform_
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
# "EXECUTE",
@@ -65,7 +65,7 @@ resource "databricks_grant" "grant_catalog_internal_service_principal_terraform_
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
# "EXECUTE",
@@ -104,7 +104,7 @@ resource "databricks_grant" "grant_catalog_published_service_principal_terraform
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
# "EXECUTE",
@@ -134,7 +134,7 @@ resource "databricks_grant" "grant_external_location_provider_service_principal_
privileges = [
# General
# "ALL_PRIVILIGES", # Use specific permissions instead of allowing all permissions by default
- # "MANAGE", # Only allow system assigned permissions at catalog level and enforce permissions at lower levels
+ "MANAGE", # Required to read workspace binding
# Metadata
"BROWSE",
diff --git a/modules/databricksdataapplication/roleassignment_uai.tf b/modules/databricksdataapplication/roleassignment_uai.tf
index fc799748..62801dae 100644
--- a/modules/databricksdataapplication/roleassignment_uai.tf
+++ b/modules/databricksdataapplication/roleassignment_uai.tf
@@ -33,7 +33,7 @@ resource "databricks_grant" "grant_catalog_provider_uai" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
@@ -74,7 +74,7 @@ resource "databricks_grant" "grant_catalog_internal_uai" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
@@ -115,7 +115,7 @@ resource "databricks_grant" "grant_catalog_published_uai" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
diff --git a/modules/databricksdataapplication/roleassignments_data_provider.tf b/modules/databricksdataapplication/roleassignments_data_provider.tf
index d8d4c4eb..e0f0486b 100644
--- a/modules/databricksdataapplication/roleassignments_data_provider.tf
+++ b/modules/databricksdataapplication/roleassignments_data_provider.tf
@@ -10,7 +10,7 @@ resource "databricks_grant" "grant_catalog_provider_data_provider_service_princi
]...)
catalog = databricks_catalog.catalog_provider[each.value.key].id
- principal = data.databricks_service_principal.service_principal_data_provider[each.key].application_id # each.value.service_principal_name
+ principal = data.databricks_service_principal.service_principal_data_provider[each.key].application_id
privileges = [
# General
# "ALL_PRIVILIGES", # Use specific permissions instead of allowing all permissions by default
@@ -23,7 +23,7 @@ resource "databricks_grant" "grant_catalog_provider_data_provider_service_princi
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
@@ -45,6 +45,41 @@ resource "databricks_grant" "grant_catalog_provider_data_provider_service_princi
]
}
+resource "databricks_grant" "grant_external_location_provider_data_provider_service_principal" {
+ for_each = merge([
+ for key, value in var.data_provider_details : {
+ for service_principal_name in value.service_principal_names :
+ "${key}-${service_principal_name}" => {
+ key = key
+ service_principal_name = service_principal_name
+ } if value.databricks_catalog.enabled
+ }
+ ]...)
+
+ external_location = databricks_external_location.external_location_provider[each.value.key].id
+ principal = data.databricks_service_principal.service_principal_data_provider[each.key].application_id
+ privileges = [
+ # General
+ # "ALL_PRIVILIGES", # Use specific permissions instead of allowing all permissions by default
+ # "MANAGE", # Only allow system assigned permissions at catalog level and enforce permissions at lower levels
+
+ # Metadata
+ "BROWSE",
+
+ # Read
+ "READ_FILES",
+
+ # Edit
+ "WRITE_FILES",
+
+ # Create
+ "CREATE_EXTERNAL_TABLE",
+ "CREATE_EXTERNAL_VOLUME",
+ "CREATE_FOREIGN_SECURABLE",
+ "CREATE_MANAGED_STORAGE",
+ ]
+}
+
resource "databricks_grant" "grant_catalog_provider_data_provider_group" {
for_each = merge([
for key, value in var.data_provider_details : {
@@ -57,7 +92,7 @@ resource "databricks_grant" "grant_catalog_provider_data_provider_group" {
]...)
catalog = databricks_catalog.catalog_provider[each.value.key].id
- principal = data.databricks_group.group_data_provider[each.key].display_name # each.value.group_name
+ principal = data.databricks_group.group_data_provider[each.key].display_name
privileges = [
# General
# "ALL_PRIVILIGES", # Use specific permissions instead of allowing all permissions by default
@@ -70,7 +105,7 @@ resource "databricks_grant" "grant_catalog_provider_data_provider_group" {
# Metadata
"BROWSE",
- # "APPLY_TAG", # Only allow system assigned tags at catalog level
+ "APPLY_TAG",
# Read
"EXECUTE",
@@ -91,3 +126,38 @@ resource "databricks_grant" "grant_catalog_provider_data_provider_group" {
"CREATE_VOLUME",
]
}
+
+resource "databricks_grant" "grant_external_location_provider_data_provider_group" {
+ for_each = merge([
+ for key, value in var.data_provider_details : {
+ for group_name in value.group_names :
+ "${key}-${group_name}" => {
+ key = key
+ group_name = group_name
+ } if value.databricks_catalog.enabled
+ }
+ ]...)
+
+ external_location = databricks_external_location.external_location_provider[each.value.key].id
+ principal = data.databricks_group.group_data_provider[each.key].display_name
+ privileges = [
+ # General
+ # "ALL_PRIVILIGES", # Use specific permissions instead of allowing all permissions by default
+ # "MANAGE", # Only allow system assigned permissions at catalog level and enforce permissions at lower levels
+
+ # Metadata
+ "BROWSE",
+
+ # Read
+ "READ_FILES",
+
+ # Edit
+ "WRITE_FILES",
+
+ # Create
+ "CREATE_EXTERNAL_TABLE",
+ "CREATE_EXTERNAL_VOLUME",
+ "CREATE_FOREIGN_SECURABLE",
+ "CREATE_MANAGED_STORAGE",
+ ]
+}
diff --git a/modules/platform/locals.tf b/modules/platform/locals.tf
index 261b2e29..107f5beb 100644
--- a/modules/platform/locals.tf
+++ b/modules/platform/locals.tf
@@ -81,6 +81,32 @@ locals {
serviceEndpoints = []
}
}
+ subnet_aifoundry = {
+ name = "AiFoundrySubnet"
+ properties = {
+ addressPrefix = var.subnet_cidr_range_aifoundry
+ defaultOutboundAccess = false
+ delegations = [
+ {
+ name = "AppDelegation"
+ properties = {
+ serviceName = "Microsoft.App/environments"
+ }
+ }
+ ]
+ ipAllocations = []
+ networkSecurityGroup = {
+ id = data.azurerm_network_security_group.network_security_group.id
+ }
+ privateEndpointNetworkPolicies = "Enabled"
+ privateLinkServiceNetworkPolicies = "Enabled"
+ routeTable = {
+ id = data.azurerm_route_table.route_table.id
+ }
+ serviceEndpointPolicies = []
+ serviceEndpoints = []
+ }
+ }
subnet_engineering_private = {
name = "EngineeringPrivateSubnet"
properties = {
@@ -208,3 +234,40 @@ locals {
}
]
}
+
+locals {
+ # Calculate subnet list
+ # Databricks Consumption enabled?
+ subnets_databricks_consumption = var.databricks_workspace_consumption_enabled ? flatten([
+ [
+ local.subnet_storage,
+ local.subnet_consumption,
+ local.subnet_fabric,
+ local.subnet_engineering_private,
+ local.subnet_engineering_public,
+ local.subnet_consumption_private,
+ local.subnet_consumption_public,
+ ],
+ local.subnets_private_endpoint_applications,
+ ]) : flatten([
+ [
+ local.subnet_storage,
+ local.subnet_consumption,
+ local.subnet_fabric,
+ local.subnet_engineering_private,
+ local.subnet_engineering_public,
+ ],
+ local.subnets_private_endpoint_applications,
+ ])
+
+ # AI Foundry enabled?
+ subnets_aifoundry = var.aifoundry_enabled ? concat(
+ local.subnets_databricks_consumption,
+ [
+ local.subnet_aifoundry
+ ]
+ ) : local.subnets_databricks_consumption
+
+ # Final subnet list
+ subnets = local.subnets_aifoundry
+}
diff --git a/modules/platform/network.tf b/modules/platform/network.tf
index 2baa894b..19f34403 100644
--- a/modules/platform/network.tf
+++ b/modules/platform/network.tf
@@ -4,27 +4,7 @@ resource "azapi_update_resource" "virtual_network" {
body = {
properties = {
- subnets = var.databricks_workspace_consumption_enabled ? flatten([
- [
- local.subnet_storage,
- local.subnet_consumption,
- local.subnet_fabric,
- local.subnet_engineering_private,
- local.subnet_engineering_public,
- local.subnet_consumption_private,
- local.subnet_consumption_public,
- ],
- local.subnets_private_endpoint_applications,
- ]) : flatten([
- [
- local.subnet_storage,
- local.subnet_consumption,
- local.subnet_fabric,
- local.subnet_engineering_private,
- local.subnet_engineering_public,
- ],
- local.subnets_private_endpoint_applications,
- ])
+ subnets = local.subnets
}
}
diff --git a/modules/platform/outputs.tf b/modules/platform/outputs.tf
index 91cd9483..6465e969 100644
--- a/modules/platform/outputs.tf
+++ b/modules/platform/outputs.tf
@@ -16,6 +16,12 @@ output "subnet_id_fabric" {
value = "${azapi_update_resource.virtual_network.id}/subnets/${local.subnet_fabric.name}"
}
+output "subnet_id_aifoundry" {
+ description = "Specifies the ai foundry subnet id."
+ sensitive = false
+ value = "${azapi_update_resource.virtual_network.id}/subnets/${local.subnet_aifoundry.name}"
+}
+
output "subnet_id_engineering_private" {
description = "Specifies the private consumption subnet id."
sensitive = false
diff --git a/modules/platform/variables.tf b/modules/platform/variables.tf
index 19b1ef08..5d268713 100644
--- a/modules/platform/variables.tf
+++ b/modules/platform/variables.tf
@@ -94,6 +94,17 @@ variable "subnet_cidr_range_fabric" {
}
}
+variable "subnet_cidr_range_aifoundry" {
+ description = "Specifies the cidr ranges of the ai foundry subnet used for the Data Landing Zone."
+ type = string
+ sensitive = false
+ default = ""
+ validation {
+ condition = var.subnet_cidr_range_aifoundry == "" || try(cidrnetmask(var.subnet_cidr_range_aifoundry), "invalid") != "invalid"
+ error_message = "Please specify a valid CIDR range for the ai foundry subnet."
+ }
+}
+
variable "subnet_cidr_range_engineering_private" {
description = "Specifies the cidr ranges of the engineering private subnet used for the Data Landing Zone."
type = string
@@ -157,3 +168,11 @@ variable "databricks_workspace_consumption_enabled" {
nullable = false
default = false
}
+
+variable "aifoundry_enabled" {
+ description = "Specifies whether the ai foundry deployment is enabled."
+ type = bool
+ sensitive = false
+ nullable = false
+ default = false
+}
diff --git a/schemas/app.schema.json b/schemas/app.schema.json
index 95e998ba..898e2e14 100644
--- a/schemas/app.schema.json
+++ b/schemas/app.schema.json
@@ -531,6 +531,18 @@
"required": [],
"additionalProperties": false
},
+ "ai_foundry_project": {
+ "description": "Specifies the ai foundry project configuration for the app.",
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "description": "Specifies whether ai foundry project should be enabled.",
+ "type": "boolean"
+ }
+ },
+ "required": [],
+ "additionalProperties": false
+ },
"data_factory": {
"description": "Specifies the data factory section of the application.",
"type": "object",
diff --git a/tests/e2e/data-applications/app001.yml b/tests/e2e/data-applications/app001.yml
index 5c7b91a4..c16a6393 100644
--- a/tests/e2e/data-applications/app001.yml
+++ b/tests/e2e/data-applications/app001.yml
@@ -11,7 +11,7 @@ identity:
network:
private_endpoint_subnet:
- cidr_range: "10.2.1.64/27"
+ cidr_range: "192.168.1.96/27"
repository:
type: github
@@ -55,7 +55,7 @@ ai_services:
kind: FormRecognizer
sku: S0
aoai:
- location: swedencentral
+ location: eastus2
kind: OpenAI
sku: S0
@@ -66,9 +66,12 @@ ai_search:
partition_count: 1
replica_count: 1
+ai_foundry_project:
+ enabled: true
+
databricks:
sql_endpoints:
- dw01:
+ se01:
auto_stop_mins: 60
enable_serverless_compute: false
cluster_size: "2X-Small"
diff --git a/tests/e2e/local.tf b/tests/e2e/local.tf
index b9e28f55..edffb1d7 100644
--- a/tests/e2e/local.tf
+++ b/tests/e2e/local.tf
@@ -2,9 +2,12 @@ locals {
# General locals
resource_providers_to_register = [
"Microsoft.Authorization",
+ "Microsoft.App",
"Microsoft.CognitiveServices",
+ "Microsoft.ContainerService",
"Microsoft.Databricks",
"Microsoft.DataFactory",
+ "Microsoft.DocumentDB",
"Microsoft.Insights",
"Microsoft.KeyVault",
"Microsoft.ManagedIdentity",
diff --git a/tests/e2e/main.tf b/tests/e2e/main.tf
index 0557a459..374e7c22 100644
--- a/tests/e2e/main.tf
+++ b/tests/e2e/main.tf
@@ -31,6 +31,7 @@ module "data_landing_zone" {
databricks_compliance_security_profile_standards = var.databricks_compliance_security_profile_standards
databricks_workspace_binding_catalog = var.databricks_workspace_binding_catalog
fabric_capacity_details = var.fabric_capacity_details
+ ai_foundry_account_details = var.ai_foundry_account_details
# HA/DR variables
zone_redundancy_enabled = var.zone_redundancy_enabled
@@ -56,8 +57,10 @@ module "data_landing_zone" {
private_dns_zone_id_databricks = var.private_dns_zone_id_databricks
private_dns_zone_id_cognitive_account = var.private_dns_zone_id_cognitive_account
private_dns_zone_id_open_ai = var.private_dns_zone_id_open_ai
+ private_dns_zone_id_ai_services = var.private_dns_zone_id_ai_services
private_dns_zone_id_data_factory = var.private_dns_zone_id_data_factory
private_dns_zone_id_search_service = var.private_dns_zone_id_search_service
+ private_dns_zone_id_cosmos_sql = var.private_dns_zone_id_cosmos_sql
# Customer-managed key variables
customer_managed_key = var.customer_managed_key
diff --git a/tests/e2e/providers.tf b/tests/e2e/providers.tf
index 911c10ca..81e145e4 100644
--- a/tests/e2e/providers.tf
+++ b/tests/e2e/providers.tf
@@ -30,7 +30,7 @@ provider "azurerm" {
permanently_delete_on_destroy = true
}
resource_group {
- prevent_deletion_if_contains_resources = true
+ prevent_deletion_if_contains_resources = false
}
}
}
diff --git a/tests/e2e/terraform.tf b/tests/e2e/terraform.tf
index 8826da04..1acb3662 100644
--- a/tests/e2e/terraform.tf
+++ b/tests/e2e/terraform.tf
@@ -16,11 +16,11 @@ terraform {
}
fabric = {
source = "microsoft/fabric"
- version = "1.2.0"
+ version = "1.3.0"
}
databricks = {
source = "databricks/databricks"
- version = "1.83.0"
+ version = "1.84.0"
}
time = {
source = "hashicorp/time"
diff --git a/tests/e2e/variables.tf b/tests/e2e/variables.tf
index 72578287..29983d5e 100644
--- a/tests/e2e/variables.tf
+++ b/tests/e2e/variables.tf
@@ -169,6 +169,25 @@ variable "fabric_capacity_details" {
}
}
+variable "ai_foundry_account_details" {
+ description = "Specifies the ai foundry configuration."
+ type = object({
+ enabled = optional(bool, false)
+ search_service = optional(object({
+ sku = optional(string, "basic")
+ semantic_search_sku = optional(string, "standard")
+ partition_count = optional(number, 1)
+ replica_count = optional(number, 1)
+ }), {})
+ cosmos_db = optional(object({
+ consistency_level = optional(string, "Session")
+ }), {})
+ })
+ sensitive = false
+ nullable = false
+ default = {}
+}
+
# HA/DR variables
variable "zone_redundancy_enabled" {
description = "Specifies whether zone-redundancy should be enabled for all resources."
@@ -254,6 +273,7 @@ variable "subnet_cidr_ranges" {
storage_subnet = string
consumption_subnet = string
fabric_subnet = string
+ aifoundry_subnet = optional(string, "")
databricks_engineering_private_subnet = string
databricks_engineering_public_subnet = string
databricks_consumption_private_subnet = optional(string, "")
@@ -273,6 +293,10 @@ variable "subnet_cidr_ranges" {
condition = try(cidrnetmask(var.subnet_cidr_ranges.fabric_subnet), "invalid") != "invalid"
error_message = "Please specify a valid CIDR range for the fabric subnet."
}
+ validation {
+ condition = var.subnet_cidr_ranges.fabric_subnet == "" || try(cidrnetmask(var.subnet_cidr_ranges.fabric_subnet), "invalid") != "invalid"
+ error_message = "Please specify a valid CIDR range for the ai foundry subnet."
+ }
validation {
condition = try(cidrnetmask(var.subnet_cidr_ranges.databricks_engineering_private_subnet), "invalid") != "invalid"
error_message = "Please specify a valid CIDR range for the databricks engineering subnet."
@@ -373,6 +397,17 @@ variable "private_dns_zone_id_open_ai" {
}
}
+variable "private_dns_zone_id_ai_services" {
+ description = "Specifies the resource ID of the private DNS zone for Azure Foundry (AI Services). Not required if DNS A-records get created via Azure Policy."
+ type = string
+ sensitive = false
+ default = ""
+ validation {
+ condition = var.private_dns_zone_id_ai_services == "" || (length(split("/", var.private_dns_zone_id_ai_services)) == 9 && endswith(var.private_dns_zone_id_ai_services, "privatelink.services.ai.azure.com"))
+ error_message = "Please specify a valid resource ID for the private DNS Zone."
+ }
+}
+
variable "private_dns_zone_id_data_factory" {
description = "Specifies the resource ID of the private DNS zone for Azure Data Factory. Not required if DNS A-records get created via Azure Policy."
type = string
@@ -395,6 +430,17 @@ variable "private_dns_zone_id_search_service" {
}
}
+variable "private_dns_zone_id_cosmos_sql" {
+ description = "Specifies the resource ID of the private DNS zone for cosmos db sql. Not required if DNS A-records get created via Azure Policy."
+ type = string
+ sensitive = false
+ default = ""
+ validation {
+ condition = var.private_dns_zone_id_cosmos_sql == "" || (length(split("/", var.private_dns_zone_id_cosmos_sql)) == 9 && endswith(var.private_dns_zone_id_cosmos_sql, "privatelink.documents.azure.com"))
+ error_message = "Please specify a valid resource ID for the private DNS Zone."
+ }
+}
+
# Customer-managed key variables
variable "customer_managed_key" {
description = "Specifies the customer managed key configurations."
diff --git a/tests/e2e/vars.tfvars b/tests/e2e/vars.tfvars
index 60cf05bc..25cfa3ba 100644
--- a/tests/e2e/vars.tfvars
+++ b/tests/e2e/vars.tfvars
@@ -1,7 +1,7 @@
# General variables
-location = "northeurope"
+location = "eastus2"
environment = "int"
-prefix = "mydlz01"
+prefix = "mydz01"
tags = {}
# Service
@@ -12,7 +12,7 @@ data_application_file_variables = {}
databricks_cluster_policy_library_path = "./databricks-cluster-policies"
databricks_cluster_policy_file_variables = {}
databricks_account_id = "515f13c1-53bb-48fb-a2c9-75e3f5d943f5"
-databricks_network_connectivity_config_name = "ncc-northeurope-test"
+databricks_network_connectivity_config_name = "ncc-eastus2-test"
databricks_network_policy_details = {
allowed_internet_destinations = [
{
@@ -35,28 +35,32 @@ fabric_capacity_details = {
admin_emails = []
sku = "F2"
}
+ai_foundry_account_details = {
+ enabled = true
+}
# HA/DR variables
zone_redundancy_enabled = false
# Logging variables
-log_analytics_workspace_id = ""
+log_analytics_workspace_id = "/subscriptions/e82c5267-9dc4-4f45-ac13-abdd5e130d27/resourceGroups/ptt-dev-logging-rg/providers/Microsoft.OperationalInsights/workspaces/ptt-dev-log001"
# Identity variables
service_principal_name_terraform_plan = "ptt-dev-uai001-dlz-tfplan"
# Network variables
-vnet_id = "/subscriptions/9842be63-c8c0-4647-a5d1-0c5e7f8bbb25/resourceGroups/ptt-dev-networking-rg/providers/Microsoft.Network/virtualNetworks/spoke-ptt-dev-vnet001"
-nsg_id = "/subscriptions/9842be63-c8c0-4647-a5d1-0c5e7f8bbb25/resourceGroups/ptt-dev-networking-rg/providers/Microsoft.Network/networkSecurityGroups/ptt-dev-default-nsg001"
-route_table_id = "/subscriptions/9842be63-c8c0-4647-a5d1-0c5e7f8bbb25/resourceGroups/ptt-dev-networking-rg/providers/Microsoft.Network/routeTables/ptt-dev-default-rt001"
+vnet_id = "/subscriptions/9842be63-c8c0-4647-a5d1-0c5e7f8bbb25/resourceGroups/ptt-dev-networking-eus2-rg/providers/Microsoft.Network/virtualNetworks/spoke-ptt-dev-eus2-vnet001"
+nsg_id = "/subscriptions/9842be63-c8c0-4647-a5d1-0c5e7f8bbb25/resourceGroups/ptt-dev-networking-eus2-rg/providers/Microsoft.Network/networkSecurityGroups/ptt-dev-defaul-eus2-nsg001"
+route_table_id = "/subscriptions/9842be63-c8c0-4647-a5d1-0c5e7f8bbb25/resourceGroups/ptt-dev-networking-eus2-rg/providers/Microsoft.Network/routeTables/ptt-dev-eus2-default-rt001"
subnet_cidr_ranges = {
- storage_subnet = "10.2.0.0/27"
- consumption_subnet = "10.2.0.32/28"
- fabric_subnet = "10.2.0.48/28"
- databricks_engineering_private_subnet = "10.2.0.64/26"
- databricks_engineering_public_subnet = "10.2.0.128/26"
- # databricks_consumption_private_subnet = "10.2.0.192/26"
- # databricks_consumption_public_subnet = "10.2.1.0/26"
+ storage_subnet = "192.168.0.0/27"
+ consumption_subnet = "192.168.0.32/28"
+ fabric_subnet = "192.168.0.48/28"
+ databricks_engineering_private_subnet = "192.168.0.64/26"
+ databricks_engineering_public_subnet = "192.168.0.128/26"
+ # databricks_consumption_private_subnet = "192.168.0.192/26"
+ # databricks_consumption_public_subnet = "192.168.1.0/26"
+ aifoundry_subnet = "192.168.1.64/27"
}
# DNS variables
@@ -67,8 +71,10 @@ private_dns_zone_id_vault = "/subscriptions/e82c5267-9dc4-4f45-ac13-
private_dns_zone_id_databricks = "/subscriptions/e82c5267-9dc4-4f45-ac13-abdd5e130d27/resourceGroups/ptt-dev-privatedns-rg/providers/Microsoft.Network/privateDnsZones/privatelink.azuredatabricks.net"
private_dns_zone_id_cognitive_account = "/subscriptions/e82c5267-9dc4-4f45-ac13-abdd5e130d27/resourceGroups/ptt-dev-privatedns-rg/providers/Microsoft.Network/privateDnsZones/privatelink.cognitiveservices.azure.com"
private_dns_zone_id_open_ai = "/subscriptions/e82c5267-9dc4-4f45-ac13-abdd5e130d27/resourceGroups/ptt-dev-privatedns-rg/providers/Microsoft.Network/privateDnsZones/privatelink.openai.azure.com"
+private_dns_zone_id_ai_services = "/subscriptions/e82c5267-9dc4-4f45-ac13-abdd5e130d27/resourceGroups/ptt-dev-privatedns-rg/providers/Microsoft.Network/privateDnsZones/privatelink.services.ai.azure.com"
private_dns_zone_id_data_factory = "/subscriptions/e82c5267-9dc4-4f45-ac13-abdd5e130d27/resourceGroups/ptt-dev-privatedns-rg/providers/Microsoft.Network/privateDnsZones/privatelink.datafactory.azure.net"
private_dns_zone_id_search_service = "/subscriptions/e82c5267-9dc4-4f45-ac13-abdd5e130d27/resourceGroups/ptt-dev-privatedns-rg/providers/Microsoft.Network/privateDnsZones/privatelink.search.windows.net"
+private_dns_zone_id_cosmos_sql = "/subscriptions/e82c5267-9dc4-4f45-ac13-abdd5e130d27/resourceGroups/ptt-dev-privatedns-rg/providers/Microsoft.Network/privateDnsZones/privatelink.documents.azure.com"
# Customer-managed key variables
customer_managed_key = null
diff --git a/variables.tf b/variables.tf
index 1e9daad9..1885ad7d 100644
--- a/variables.tf
+++ b/variables.tf
@@ -169,6 +169,25 @@ variable "fabric_capacity_details" {
}
}
+variable "ai_foundry_account_details" {
+ description = "Specifies the ai foundry configuration."
+ type = object({
+ enabled = optional(bool, false)
+ search_service = optional(object({
+ sku = optional(string, "basic")
+ semantic_search_sku = optional(string, "standard")
+ partition_count = optional(number, 1)
+ replica_count = optional(number, 1)
+ }), {})
+ cosmos_db = optional(object({
+ consistency_level = optional(string, "Session")
+ }), {})
+ })
+ sensitive = false
+ nullable = false
+ default = {}
+}
+
# HA/DR variables
variable "zone_redundancy_enabled" {
description = "Specifies whether zone-redundancy should be enabled for all resources."
@@ -254,6 +273,7 @@ variable "subnet_cidr_ranges" {
storage_subnet = string
consumption_subnet = string
fabric_subnet = string
+ aifoundry_subnet = optional(string, "")
databricks_engineering_private_subnet = string
databricks_engineering_public_subnet = string
databricks_consumption_private_subnet = optional(string, "")
@@ -273,6 +293,10 @@ variable "subnet_cidr_ranges" {
condition = try(cidrnetmask(var.subnet_cidr_ranges.fabric_subnet), "invalid") != "invalid"
error_message = "Please specify a valid CIDR range for the fabric subnet."
}
+ validation {
+ condition = var.subnet_cidr_ranges.fabric_subnet == "" || try(cidrnetmask(var.subnet_cidr_ranges.fabric_subnet), "invalid") != "invalid"
+ error_message = "Please specify a valid CIDR range for the ai foundry subnet."
+ }
validation {
condition = try(cidrnetmask(var.subnet_cidr_ranges.databricks_engineering_private_subnet), "invalid") != "invalid"
error_message = "Please specify a valid CIDR range for the databricks engineering subnet."
@@ -373,6 +397,17 @@ variable "private_dns_zone_id_open_ai" {
}
}
+variable "private_dns_zone_id_ai_services" {
+ description = "Specifies the resource ID of the private DNS zone for Azure Foundry (AI Services). Not required if DNS A-records get created via Azure Policy."
+ type = string
+ sensitive = false
+ default = ""
+ validation {
+ condition = var.private_dns_zone_id_ai_services == "" || (length(split("/", var.private_dns_zone_id_ai_services)) == 9 && endswith(var.private_dns_zone_id_ai_services, "privatelink.services.ai.azure.com"))
+ error_message = "Please specify a valid resource ID for the private DNS Zone."
+ }
+}
+
variable "private_dns_zone_id_data_factory" {
description = "Specifies the resource ID of the private DNS zone for Azure Data Factory. Not required if DNS A-records get created via Azure Policy."
type = string
@@ -395,6 +430,17 @@ variable "private_dns_zone_id_search_service" {
}
}
+variable "private_dns_zone_id_cosmos_sql" {
+ description = "Specifies the resource ID of the private DNS zone for cosmos db sql. Not required if DNS A-records get created via Azure Policy."
+ type = string
+ sensitive = false
+ default = ""
+ validation {
+ condition = var.private_dns_zone_id_cosmos_sql == "" || (length(split("/", var.private_dns_zone_id_cosmos_sql)) == 9 && endswith(var.private_dns_zone_id_cosmos_sql, "privatelink.documents.azure.com"))
+ error_message = "Please specify a valid resource ID for the private DNS Zone."
+ }
+}
+
# Customer-managed key variables
variable "customer_managed_key" {
description = "Specifies the customer managed key configurations."