diff --git a/code/terraform/locals.tf b/code/terraform/locals.tf index 31de744..544179b 100644 --- a/code/terraform/locals.tf +++ b/code/terraform/locals.tf @@ -134,5 +134,18 @@ locals { } } } - machine_learning_workspace_outbound_rules = merge(local.default_machine_learning_workspace_outbound_rules, var.search_service_enabled ? local.search_service_machine_learning_workspace_outbound_rules : {}) + open_ai_machine_learning_workspace_outbound_rules = { + "${var.open_ai_enabled ? azurerm_cognitive_account.cognitive_account[0].name : ""}-account" = { + type = "PrivateEndpoint" + category = "UserDefined" + status = "Active" + destination = { + serviceResourceId = var.open_ai_enabled ? azurerm_cognitive_account.cognitive_account[0].id : "" + subresourceTarget = "account" + sparkEnabled = true + sparkStatus = "Active" + } + } + } + machine_learning_workspace_outbound_rules = merge(local.default_machine_learning_workspace_outbound_rules, var.search_service_enabled ? local.search_service_machine_learning_workspace_outbound_rules : {}, var.open_ai_enabled ? local.open_ai_machine_learning_workspace_outbound_rules : {}) } diff --git a/code/terraform/machinelearningconnections.tf b/code/terraform/machinelearningconnections.tf index 4da5fe5..df7ee5e 100644 --- a/code/terraform/machinelearningconnections.tf +++ b/code/terraform/machinelearningconnections.tf @@ -19,3 +19,26 @@ resource "azapi_resource" "machine_learning_workspace_connection_search" { } }) } + +resource "azapi_resource" "machine_learning_workspace_connection_open_ai" { + count = var.open_ai_enabled ? 1 : 0 + + type = "Microsoft.MachineLearningServices/workspaces/connections@2023-06-01-preview" + name = azurerm_cognitive_account.cognitive_account[0].name + parent_id = azurerm_machine_learning_workspace.machine_learning_workspace.id + + body = jsonencode({ + properties = { + authType = "ApiKey" + category = "AzureOpenAI" + credentials = { + key = azurerm_cognitive_account.cognitive_account[0].primary_access_key + } + metadata = { + ApiVersion = "2023-07-01-preview" + ApiType = "azure" + } + target = "https://${azurerm_cognitive_account.cognitive_account[0].name}.openai.azure.com/" + } + }) +} diff --git a/code/terraform/openai.tf b/code/terraform/openai.tf new file mode 100644 index 0000000..c560c27 --- /dev/null +++ b/code/terraform/openai.tf @@ -0,0 +1,136 @@ +resource "azurerm_cognitive_account" "cognitive_account" { + count = var.open_ai_enabled ? 1 : 0 + + name = "${local.prefix}-cog001" + location = var.location + resource_group_name = data.azurerm_resource_group.resource_group.name + tags = var.tags + identity { + type = "SystemAssigned" + } + + custom_subdomain_name = "${local.prefix}-cog001" + dynamic_throttling_enabled = false + fqdns = [ + trimsuffix(replace(azurerm_storage_account.storage.primary_blob_endpoint, "https://", ""), "/") + ] + kind = "OpenAI" + local_auth_enabled = true + network_acls { + default_action = "Deny" + ip_rules = [] + } + outbound_network_access_restricted = true + public_network_access_enabled = false + sku_name = "S0" +} + +resource "azapi_resource" "cognitive_service_open_ai_model_ada" { + count = var.open_ai_enabled ? 1 : 0 + + type = "Microsoft.CognitiveServices/accounts/deployments@2023-05-01" + name = "text-embedding-ada-002" + parent_id = azurerm_cognitive_account.cognitive_account[0].id + + body = jsonencode({ + sku = { + name = "Standard" + capacity = 60 + } + properties = { + model = { + format = "OpenAI" + name = "text-embedding-ada-002" + version = "2" + } + raiPolicyName = "Microsoft.Default" + versionUpgradeOption = "OnceNewDefaultVersionAvailable" + } + }) +} + +resource "azapi_resource" "cognitive_service_open_ai_model_gtt_35" { + count = var.open_ai_enabled ? 1 : 0 + + type = "Microsoft.CognitiveServices/accounts/deployments@2023-05-01" + name = "gpt-35-turbo" + parent_id = azurerm_cognitive_account.cognitive_account[0].id + + body = jsonencode({ + sku = { + name = "Standard" + capacity = 60 + } + properties = { + model = { + format = "OpenAI" + name = "gpt-35-turbo" + version = "0301" + } + raiPolicyName = "Microsoft.Default" + versionUpgradeOption = "OnceNewDefaultVersionAvailable" + } + }) + + depends_on = [ + azapi_resource.cognitive_service_open_ai_model_ada + ] +} + +data "azurerm_monitor_diagnostic_categories" "diagnostic_categories_cognitive_service" { + count = var.open_ai_enabled ? 1 : 0 + + resource_id = azurerm_cognitive_account.cognitive_account[0].id +} + +resource "azurerm_monitor_diagnostic_setting" "diagnostic_setting_cognitive_service" { + count = var.open_ai_enabled ? 1 : 0 + + name = "logAnalytics" + target_resource_id = azurerm_cognitive_account.cognitive_account[0].id + log_analytics_workspace_id = azurerm_log_analytics_workspace.log_analytics_workspace.id + + dynamic "enabled_log" { + iterator = entry + for_each = data.azurerm_monitor_diagnostic_categories.diagnostic_categories_cognitive_service[0].log_category_groups + content { + category_group = entry.value + } + } + + dynamic "metric" { + iterator = entry + for_each = data.azurerm_monitor_diagnostic_categories.diagnostic_categories_cognitive_service[0].metrics + content { + category = entry.value + enabled = true + } + } +} + +resource "azurerm_private_endpoint" "cognitive_service_private_endpoint" { + count = var.open_ai_enabled ? 1 : 0 + + name = "${azurerm_cognitive_account.cognitive_account[0].name}-pe" + location = var.location + resource_group_name = azurerm_cognitive_account.cognitive_account[0].resource_group_name + tags = var.tags + + custom_network_interface_name = "${azurerm_cognitive_account.cognitive_account[0].name}-nic" + private_service_connection { + name = "${azurerm_cognitive_account.cognitive_account[0].name}-pe" + is_manual_connection = false + private_connection_resource_id = azurerm_cognitive_account.cognitive_account[0].id + subresource_names = ["account"] + } + subnet_id = var.subnet_id + dynamic "private_dns_zone_group" { + for_each = var.private_dns_zone_id_open_ai == "" ? [] : [1] + content { + name = "${azurerm_cognitive_account.cognitive_account[0].name}-arecord" + private_dns_zone_ids = [ + var.private_dns_zone_id_open_ai + ] + } + } +} diff --git a/code/terraform/variables.tf b/code/terraform/variables.tf index fcd5e7e..fbe0d45 100644 --- a/code/terraform/variables.tf +++ b/code/terraform/variables.tf @@ -59,6 +59,13 @@ variable "search_service_enabled" { default = false } +variable "open_ai_enabled" { + description = "Specifies whether Azure Open AI should be deployed." + type = bool + sensitive = false + default = false +} + variable "machine_learning_compute_clusters" { type = map(object({ vm_priority = optional(string, "Dedicated") @@ -195,6 +202,17 @@ variable "private_dns_zone_id_search_service" { } } +variable "private_dns_zone_id_open_ai" { + description = "Specifies the resource ID of the private DNS zone for Azure Open AI endpoints. 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 "data_platform_subscription_ids" { description = "Specifies the list of subscription IDs of your data platform." type = list(string) diff --git a/code/terraform/vars.dev.tfvars b/code/terraform/vars.dev.tfvars index 0f3d3a1..1e8eb56 100644 --- a/code/terraform/vars.dev.tfvars +++ b/code/terraform/vars.dev.tfvars @@ -4,6 +4,7 @@ prefix = "dpml" tags = {} resource_group_name = "myprod-dev-analytics-rg" subnet_id = "/subscriptions/8f171ff9-2b5b-4f0f-aed5-7fa360a1d094/resourceGroups/mycrp-prd-logic-network-rg/providers/Microsoft.Network/virtualNetworks/mycrp-prd-logic-vnet001/subnets/PeSubnet" +open_ai_enabled = false search_service_enabled = false machine_learning_compute_clusters = { # "cpu001" = {