diff --git a/scenarios/secure-baseline-multitenant/terraform/README.md b/scenarios/secure-baseline-multitenant/terraform/README.md index 05e8aafe..ca0991ab 100644 --- a/scenarios/secure-baseline-multitenant/terraform/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/README.md @@ -65,6 +65,7 @@ deployment_options = { deploy_sql_database = true deploy_app_config = true deploy_vm = true + deploy_openai = true } # Optionally deploy a Github runner, DevOps agent, or both to the VM. @@ -260,4 +261,3 @@ Connect to the VM using the local VM admin credentials and run `dsregcmd /status ``` If the VM is AAD joined, try to login in with the Azure AD credentials again after a few minutes. If it's not AAD joined, attempt to re-install the VM extension or manually enroll the VM to AAD by following the steps in Edge: open Edge and click "Sign in to sync data", select "Work or school account", and then press OK on "Allow my organization to manage my device". It takes a few minutes for the policies to be applied, device scanned and confirmed as secure to access corporate resources. You will know that the process is complete. - diff --git a/scenarios/secure-baseline-multitenant/terraform/hub/Parameters/uat.tfvars b/scenarios/secure-baseline-multitenant/terraform/hub/Parameters/uat.tfvars index eb0bd4ff..95d28bed 100644 --- a/scenarios/secure-baseline-multitenant/terraform/hub/Parameters/uat.tfvars +++ b/scenarios/secure-baseline-multitenant/terraform/hub/Parameters/uat.tfvars @@ -14,4 +14,5 @@ deployment_options = { deploy_sql_database = true deploy_app_config = true deploy_vm = true + deploy_openai = true } diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/Parameters/uat.tfvars b/scenarios/secure-baseline-multitenant/terraform/spoke/Parameters/uat.tfvars index fc3084bd..08e7367a 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/Parameters/uat.tfvars +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/Parameters/uat.tfvars @@ -23,14 +23,14 @@ vm_aad_admin_object_id = "bda41c64-1493-4d8d-b4b5-7135159d4884" # "AppSvcLZA Azu # vm_admin_password = "**************" ## These settings are used for peering the spoke to the hub. Fill in the appropriate settings for your environment -# hub_settings = { -# rg_name = "rg-hub-scenario1-wus3" -# vnet_name = "vnet-hub-scenario1-wus3" +hub_settings = { + rg_name = "rg-hub-scenario1-wus3" + vnet_name = "vnet-hub-scenario1-wus3" -# firewall = { -# private_ip = "10.242.0.4" -# } -# } + firewall = { + private_ip = "10.242.0.4" + } +} ## Toggle deployment of optional features and services for the Landing Zone deployment_options = { @@ -42,6 +42,7 @@ deployment_options = { deploy_sql_database = true deploy_app_config = true deploy_vm = false + deploy_openai = true } ## Optionally deploy a Github runner, DevOps agent, or both to the VM. diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md index 2d93240a..f772148c 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/README.md +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/README.md @@ -14,7 +14,7 @@ | Name | Version | |------|---------| | [azurecaf](#provider\_azurecaf) | 1.2.26 | -| [azurerm](#provider\_azurerm) | 3.67.0 | +| [azurerm](#provider\_azurerm) | 3.75.0 | | [random](#provider\_random) | 3.5.1 | | [terraform](#provider\_terraform) | n/a | @@ -28,6 +28,7 @@ | [frontdoor](#module\_frontdoor) | ../../../shared/terraform-modules/frontdoor | n/a | | [key\_vault](#module\_key\_vault) | ../../../shared/terraform-modules/key-vault | n/a | | [network](#module\_network) | ../../../shared/terraform-modules/network | n/a | +| [openai](#module\_openai) | ../../../shared/terraform-modules/cognitive-services/openai | n/a | | [private\_dns\_zones](#module\_private\_dns\_zones) | ../../../shared/terraform-modules/private-dns-zone | n/a | | [redis\_cache](#module\_redis\_cache) | ../../../shared/terraform-modules/redis | n/a | | [sql\_database](#module\_sql\_database) | ../../../shared/terraform-modules/sql-database | n/a | @@ -58,11 +59,11 @@ | [aad\_admin\_group\_name](#input\_aad\_admin\_group\_name) | The name of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | | [aad\_admin\_group\_object\_id](#input\_aad\_admin\_group\_object\_id) | The object ID of the Azure AD group that should be granted SQL Admin permissions to the SQL Server | `string` | n/a | yes | | [application\_name](#input\_application\_name) | The name of your application | `string` | `"sec-baseline-1-spoke"` | no | -| [appsvc\_options](#input\_appsvc\_options) | The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": []
}
}
| no | +| [appsvc\_options](#input\_appsvc\_options) | The options for the app service |
object({
service_plan = object({
os_type = string
sku_name = string
worker_count = optional(number)
zone_redundant = optional(bool)
})
web_app = object({
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
})
|
{
"service_plan": {
"os_type": "Windows",
"sku_name": "S1"
},
"web_app": {
"application_stack": {
"current_stack": "dotnet",
"dotnet_version": "6.0"
},
"slots": []
}
}
| no | | [appsvc\_subnet\_cidr](#input\_appsvc\_subnet\_cidr) | The CIDR block for the subnet. | `list(string)` |
[
"10.240.0.0/26"
]
| no | | [bastion\_subnet\_cidr](#input\_bastion\_subnet\_cidr) | [Optional] The CIDR block(s) for the bastion subnet. Defaults to 10.242.0.64/26 | `list(string)` |
[
"10.242.0.64/26"
]
| no | | [bastion\_subnet\_name](#input\_bastion\_subnet\_name) | [Optional] Name of the subnet to deploy bastion resource to. Defaults to 'AzureBastionSubnet' | `string` | `"AzureBastionSubnet"` | no | -| [deployment\_options](#input\_deployment\_options) | Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
})
|
{
"deploy_app_config": true,
"deploy_bastion": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | +| [deployment\_options](#input\_deployment\_options) | Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache. |
object({
enable_waf = bool
enable_egress_lockdown = bool
enable_diagnostic_settings = bool
deploy_bastion = bool
deploy_redis = bool
deploy_sql_database = bool
deploy_app_config = bool
deploy_vm = bool
deploy_openai = bool
})
|
{
"deploy_app_config": true,
"deploy_bastion": true,
"deploy_openai": true,
"deploy_redis": true,
"deploy_sql_database": true,
"deploy_vm": true,
"enable_diagnostic_settings": true,
"enable_egress_lockdown": true,
"enable_waf": true
}
| no | | [devops\_settings](#input\_devops\_settings) | The settings for the Azure DevOps agent or GitHub runner |
object({
github_runner = optional(object({
repository_url = string
token = string
}))

devops_agent = optional(object({
organization_url = string
token = string
}))
})
|
{
"devops_agent": null,
"github_runner": null
}
| no | | [devops\_subnet\_cidr](#input\_devops\_subnet\_cidr) | [Optional] The CIDR block for the subnet. Defaults to 10.240.10.128/16 | `list(string)` |
[
"10.240.10.128/26"
]
| no | | [environment](#input\_environment) | The environment (dev, qa, staging, prod) | `string` | `"dev"` | no | diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/_locals.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/_locals.tf index c024f527..199aad07 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/_locals.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/_locals.tf @@ -1,25 +1,40 @@ - locals { deployment_name = "sec-baseline-1-spoke" # used in spoke-network.tf - private_dns_zones = [ - { - name : "privatelink.azurewebsites.net" - records : [] - }, { - name : "privatelink.database.windows.net" - records : [] - }, { - name : "privatelink.azconfig.io" - records : [] - }, { - name : "privatelink.vaultcore.azure.net" - records : [] - }, { - name : "privatelink.redis.cache.windows.net" - records : [] - } + private_dns_zones = [for each in + [ + { + name : "privatelink.azurewebsites.net" + records : [] + enabled : true + }, + { + name : "privatelink.vaultcore.azure.net" + records : [] + enabled : true + }, + { + name : "privatelink.database.windows.net" + records : [] + enabled : var.deployment_options.deploy_sql_database + }, + { + name : "privatelink.azconfig.io" + records : [] + enabled : var.deployment_options.deploy_app_config + }, + { + name : "privatelink.redis.cache.windows.net" + records : [] + enabled : var.deployment_options.deploy_redis + }, + { + name : "privatelink.openai.azure.com" + records : [] + enabled : var.deployment_options.deploy_openai + } + ] : each if each.enabled ] provisioned_dns_zones = { for i, dns_zone in module.private_dns_zones : dns_zone.name => dns_zone.dns_zone } diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf new file mode 100644 index 00000000..bf7e25b8 --- /dev/null +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/ai.tf @@ -0,0 +1,55 @@ +module "openai" { + count = var.deployment_options.deploy_openai ? 1 : 0 + + source = "../../../shared/terraform-modules/cognitive-services/openai" + + global_settings = local.global_settings + resource_group_name = azurerm_resource_group.spoke.name + location = azurerm_resource_group.spoke.location + + deployment = { + "text-embedding-ada-002" = { + name = "text-embedding-ada-002" + model_format = "OpenAI" + model_name = "text-embedding-ada-002" + model_version = "2" + scale_type = "Standard" + } + "gpt-35-turbo" = { + name = "gpt-35-turbo" + model_format = "OpenAI" + model_name = "gpt-35-turbo" + model_version = "0613" + scale_type = "Standard" + } + } + + pe_private_link_subnet_id = module.network.subnets["privateLink"].id + private_dns_zone = local.provisioned_dns_zones["privatelink.openai.azure.com"] + + network_acls = [ + { + default_action = "Deny" + virtual_network_rules = [ + { + subnet_id = module.network.subnets["serverFarm"].id + ignore_missing_vnet_service_endpoint = true + }, + { + subnet_id = module.network.subnets["ingress"].id + ignore_missing_vnet_service_endpoint = true + }, + { + subnet_id = module.network.subnets["devops"].id + ignore_missing_vnet_service_endpoint = true + }, + { + subnet_id = module.network.subnets["privateLink"].id + ignore_missing_vnet_service_endpoint = true + } + ] + } + ] + + tags = local.base_tags +} diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf index 1d0b85fc..7bc0bf0e 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/app.tf @@ -66,52 +66,52 @@ module "sql_database" { private_dns_zone = local.provisioned_dns_zones["privatelink.database.windows.net"] } -module "app_configuration" { - count = var.deployment_options.deploy_app_config ? 1 : 0 - - source = "../../../shared/terraform-modules/app-configuration" +module "key_vault" { + source = "../../../shared/terraform-modules/key-vault" resource_group = azurerm_resource_group.spoke.name application_name = var.application_name environment = var.environment location = var.location - unique_id = random_integer.unique_id.result tenant_id = var.tenant_id + unique_id = random_integer.unique_id.result + sku_name = "standard" private_link_subnet_id = module.network.subnets["privateLink"].id global_settings = local.global_settings tags = local.base_tags - data_reader_identities = [ + secret_reader_identities = [ azurerm_user_assigned_identity.reader.principal_id ] - data_owner_identities = [ + secret_officer_identities = [ azurerm_user_assigned_identity.contributor.principal_id ] - - private_dns_zone = local.provisioned_dns_zones["privatelink.azconfig.io"] + private_dns_zone = local.provisioned_dns_zones["privatelink.vaultcore.azure.net"] } -module "key_vault" { - source = "../../../shared/terraform-modules/key-vault" +module "app_configuration" { + count = var.deployment_options.deploy_app_config ? 1 : 0 + + source = "../../../shared/terraform-modules/app-configuration" resource_group = azurerm_resource_group.spoke.name application_name = var.application_name environment = var.environment location = var.location - tenant_id = var.tenant_id unique_id = random_integer.unique_id.result - sku_name = "standard" + tenant_id = var.tenant_id private_link_subnet_id = module.network.subnets["privateLink"].id global_settings = local.global_settings tags = local.base_tags - secret_reader_identities = [ + data_reader_identities = [ azurerm_user_assigned_identity.reader.principal_id ] - secret_officer_identities = [ + data_owner_identities = [ azurerm_user_assigned_identity.contributor.principal_id ] - private_dns_zone = local.provisioned_dns_zones["privatelink.vaultcore.azure.net"] + + private_dns_zone = local.provisioned_dns_zones["privatelink.azconfig.io"] } module "redis_cache" { diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/main.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/main.tf index ae84e4ae..405f87f4 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/main.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/main.tf @@ -1,5 +1,7 @@ terraform { - required_version = ">=1.3" # must be greater than or equal to 1.2 for OIDC + # must be greater than or equal to 1.2 for OIDC + # must be greater than or equal to 1.3 for OpenAI + required_version = ">=1.3" required_providers { azurerm = { diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf index 24dca64c..8c10272e 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/network.tf @@ -69,7 +69,7 @@ module "private_dns_zones" { global_settings = local.global_settings dns_zone_name = local.private_dns_zones[count.index].name - dns_records = local.private_dns_zones[count.index].records + dns_records = lookup(local.private_dns_zones[count.index], "records", []) vnet_links = [ data.azurerm_virtual_network.hub.id ] diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/shared.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/shared.tf index 5bac59e0..054c347c 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/shared.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/shared.tf @@ -16,7 +16,7 @@ module "devops_vm" { resource_group = azurerm_resource_group.spoke.name vm_name = "devops" location = var.location - vm_subnet_id = module.network.subnets[index(module.network.subnets.*.name, "devops")].id + vm_subnet_id = module.network.subnets["devops"].id admin_username = var.vm_admin_username admin_password = var.vm_admin_password aad_admin_username = var.vm_aad_admin_username diff --git a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf index d8ba7742..a54ac729 100644 --- a/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf +++ b/scenarios/secure-baseline-multitenant/terraform/spoke/variables.tf @@ -132,20 +132,20 @@ variable "private_link_subnet_cidr" { default = ["10.240.11.0/24"] } -# variable "hub_settings" { -# type = object({ -# rg_name = string -# vnet_name = string +variable "hub_settings" { + type = object({ + rg_name = string + vnet_name = string -# firewall = object({ -# private_ip = optional(string) -# }) -# }) + firewall = object({ + private_ip = optional(string) + }) + }) -# description = "The settings for the hub virtual network." + description = "The settings for the hub virtual network." -# default = null -# } + default = null +} variable "vm_admin_username" { type = string @@ -179,6 +179,7 @@ variable "deployment_options" { deploy_sql_database = bool deploy_app_config = bool deploy_vm = bool + deploy_openai = bool }) description = "Opt-in settings for the deployment: enable WAF in Front Door, deploy Azure Firewall and UDRs in the spoke network to force outbound traffic to the Azure Firewall, deploy Redis Cache." @@ -192,6 +193,7 @@ variable "deployment_options" { deploy_sql_database = true deploy_app_config = true deploy_vm = true + deploy_openai = true } } @@ -209,6 +211,8 @@ variable "appsvc_options" { application_stack = object({ current_stack = string # required for windows dotnet_version = optional(string) + docker_image = optional(string) # linux only + docker_image_tag = optional(string) # linux only php_version = optional(string) node_version = optional(string) java_version = optional(string) @@ -217,9 +221,6 @@ variable "appsvc_options" { java_server = optional(string) # linux only java_server_version = optional(string) # linux only go_version = optional(string) # linux only - docker_image = optional(string) # linux only - docker_image_tag = optional(string) # linux only - go_version = optional(string) # linux only ruby_version = optional(string) # linux only }) }) diff --git a/scenarios/shared/terraform-modules/app-service/README.md b/scenarios/shared/terraform-modules/app-service/README.md index 5aaa8e49..50893e8d 100644 --- a/scenarios/shared/terraform-modules/app-service/README.md +++ b/scenarios/shared/terraform-modules/app-service/README.md @@ -10,7 +10,7 @@ No requirements. | Name | Version | |------|---------| | [azurecaf](#provider\_azurecaf) | 1.2.26 | -| [azurerm](#provider\_azurerm) | 3.66.0 | +| [azurerm](#provider\_azurerm) | 3.75.0 | ## Modules @@ -45,7 +45,7 @@ No requirements. | [resource\_group](#input\_resource\_group) | The name of the resource group where all resources in this example should be created. | `string` | n/a | yes | | [service\_plan\_options](#input\_service\_plan\_options) | The options for the app service |
object({
os_type = string
sku_name = string
app_service_environment_id = optional(string)
worker_count = optional(number)
zone_redundant = optional(bool)
})
|
{
"os_type": "Windows",
"sku_name": "S1"
}
| no | | [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | -| [webapp\_options](#input\_webapp\_options) | The options for the app service |
object({
slots = optional(list(string))

application_stack = optional(object({
current_stack = optional(string) # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
}))
})
|
{
"application_stack": {},
"slots": []
}
| no | +| [webapp\_options](#input\_webapp\_options) | The options for the app service |
object({
slots = optional(list(string))

application_stack = optional(object({
current_stack = optional(string) # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
}))
})
|
{
"application_stack": {},
"slots": []
}
| no | ## Outputs diff --git a/scenarios/shared/terraform-modules/app-service/linux-web-app/README.md b/scenarios/shared/terraform-modules/app-service/linux-web-app/README.md index 2d799866..932a27fa 100644 --- a/scenarios/shared/terraform-modules/app-service/linux-web-app/README.md +++ b/scenarios/shared/terraform-modules/app-service/linux-web-app/README.md @@ -9,8 +9,8 @@ No requirements. | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.24 | -| [azurerm](#provider\_azurerm) | 3.52.0 | +| [azurecaf](#provider\_azurecaf) | 1.2.26 | +| [azurerm](#provider\_azurerm) | 3.75.0 | ## Modules @@ -48,7 +48,7 @@ No requirements. | [service\_plan\_resource](#input\_service\_plan\_resource) | The service plan resource where the web application will be created | `any` | n/a | yes | | [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | | [web\_app\_name](#input\_web\_app\_name) | The name of the web application | `string` | n/a | yes | -| [webapp\_options](#input\_webapp\_options) | The options for the app service |
object({
instrumentation_key = string
ai_connection_string = string
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
| n/a | yes | +| [webapp\_options](#input\_webapp\_options) | The options for the app service |
object({
instrumentation_key = string
ai_connection_string = string
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
| n/a | yes | ## Outputs diff --git a/scenarios/shared/terraform-modules/app-service/linux-web-app/module.tf b/scenarios/shared/terraform-modules/app-service/linux-web-app/module.tf index e4dabb5c..d9c5614d 100644 --- a/scenarios/shared/terraform-modules/app-service/linux-web-app/module.tf +++ b/scenarios/shared/terraform-modules/app-service/linux-web-app/module.tf @@ -91,20 +91,22 @@ resource "azurerm_monitor_diagnostic_setting" "this" { enabled_log { category_group = "allLogs" - retention_policy { - days = 0 - enabled = false - } + ## `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention + # retention_policy { + # days = 0 + # enabled = false + # } } metric { category = "AllMetrics" enabled = false - retention_policy { - days = 0 - enabled = false - } + ## `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention + # retention_policy { + # days = 0 + # enabled = false + # } } } diff --git a/scenarios/shared/terraform-modules/app-service/linux-web-app/variables.tf b/scenarios/shared/terraform-modules/app-service/linux-web-app/variables.tf index 76a5a41a..c92f4ce0 100644 --- a/scenarios/shared/terraform-modules/app-service/linux-web-app/variables.tf +++ b/scenarios/shared/terraform-modules/app-service/linux-web-app/variables.tf @@ -105,7 +105,6 @@ variable "webapp_options" { python_version = optional(string) # linux only java_server = optional(string) # linux only java_server_version = optional(string) # linux only - go_version = optional(string) # linux only docker_image = optional(string) # linux only docker_image_tag = optional(string) # linux only go_version = optional(string) # linux only diff --git a/scenarios/shared/terraform-modules/app-service/variables.tf b/scenarios/shared/terraform-modules/app-service/variables.tf index 5e681463..655dd2f3 100644 --- a/scenarios/shared/terraform-modules/app-service/variables.tf +++ b/scenarios/shared/terraform-modules/app-service/variables.tf @@ -102,7 +102,6 @@ variable "webapp_options" { python_version = optional(string) # linux only java_server = optional(string) # linux only java_server_version = optional(string) # linux only - go_version = optional(string) # linux only docker_image = optional(string) # linux only docker_image_tag = optional(string) # linux only go_version = optional(string) # linux only diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md b/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md index 42e18395..dab6e024 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/README.md @@ -9,8 +9,8 @@ No requirements. | Name | Version | |------|---------| -| [azurecaf](#provider\_azurecaf) | 1.2.24 | -| [azurerm](#provider\_azurerm) | 3.52.0 | +| [azurecaf](#provider\_azurecaf) | 1.2.26 | +| [azurerm](#provider\_azurerm) | 3.75.0 | ## Modules @@ -48,7 +48,7 @@ No requirements. | [service\_plan\_resource](#input\_service\_plan\_resource) | The service plan resource where the web application will be created | `any` | n/a | yes | | [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | | [web\_app\_name](#input\_web\_app\_name) | The name of the web application | `string` | n/a | yes | -| [webapp\_options](#input\_webapp\_options) | The options for the app service |
object({
instrumentation_key = string
ai_connection_string = string
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
go_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
| n/a | yes | +| [webapp\_options](#input\_webapp\_options) | The options for the app service |
object({
instrumentation_key = string
ai_connection_string = string
slots = list(string)

application_stack = object({
current_stack = string # required for windows
dotnet_version = optional(string)
php_version = optional(string)
node_version = optional(string)
java_version = optional(string)
python = optional(bool) # windows only
python_version = optional(string) # linux only
java_server = optional(string) # linux only
java_server_version = optional(string) # linux only
docker_image = optional(string) # linux only
docker_image_tag = optional(string) # linux only
go_version = optional(string) # linux only
ruby_version = optional(string) # linux only
})
})
| n/a | yes | ## Outputs diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf b/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf index e589ca6c..ebf704f2 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/module.tf @@ -82,20 +82,22 @@ resource "azurerm_monitor_diagnostic_setting" "this" { enabled_log { category_group = "AllLogs" - retention_policy { - days = 0 - enabled = false - } + ## `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention + # retention_policy { + # days = 0 + # enabled = false + # } } metric { category = "AllMetrics" enabled = false - retention_policy { - days = 0 - enabled = false - } + ## `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention + # retention_policy { + # days = 0 + # enabled = false + # } } } diff --git a/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf b/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf index 3219a6bc..2fd517b5 100644 --- a/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf +++ b/scenarios/shared/terraform-modules/app-service/windows-web-app/variables.tf @@ -105,7 +105,6 @@ variable "webapp_options" { python_version = optional(string) # linux only java_server = optional(string) # linux only java_server_version = optional(string) # linux only - go_version = optional(string) # linux only docker_image = optional(string) # linux only docker_image_tag = optional(string) # linux only go_version = optional(string) # linux only diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/README.md b/scenarios/shared/terraform-modules/cognitive-services/openai/README.md new file mode 100644 index 00000000..14258195 --- /dev/null +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/README.md @@ -0,0 +1,61 @@ +# openai + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3.0 | +| [azurerm](#requirement\_azurerm) | ~> 3.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azurecaf](#provider\_azurecaf) | 1.2.26 | +| [azurerm](#provider\_azurerm) | 3.72.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azurecaf_name.caf_name_akv](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_cognitive_account.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_account) | resource | +| [azurerm_cognitive_deployment.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_deployment) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [application\_name](#input\_application\_name) | Name of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | +| [custom\_subdomain\_name](#input\_custom\_subdomain\_name) | The subdomain name used for token-based authentication. Changing this forces a new resource to be created. Leave this variable as default would use a default name with random suffix. | `string` | `null` | no | +| [customer\_managed\_key](#input\_customer\_managed\_key) | type = object({
key\_vault\_key\_id = (Required) The ID of the Key Vault Key which should be used to Encrypt the data in this OpenAI Account.
identity\_client\_id = (Optional) The Client ID of the User Assigned Identity that has access to the key. This property only needs to be specified when there're multiple identities attached to the OpenAI Account.
}) |
object({
key_vault_key_id = string
identity_client_id = optional(string)
})
| `null` | no | +| [deployment](#input\_deployment) | type = map(object({
name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created.
cognitive\_account\_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created.
model = {
model\_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI.
model\_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created.
model\_version = (Required) The version of Cognitive Services Account Deployment model.
}
scale = {
scale\_type = (Required) Deployment scale type. Possible value is Standard. Changing this forces a new resource to be created.
}
rai\_policy\_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created.
})) |
map(object({
name = string
model_format = string
model_name = string
model_version = string
scale_type = string
rai_policy_name = optional(string)
}))
| `{}` | no | +| [dynamic\_throttling\_enabled](#input\_dynamic\_throttling\_enabled) | Determines whether or not dynamic throttling is enabled. If set to `true`, dynamic throttling will be enabled. If set to `false`, dynamic throttling will not be enabled. | `bool` | `null` | no | +| [environment](#input\_environment) | Environment of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | +| [fqdns](#input\_fqdns) | List of FQDNs allowed for the Cognitive Account. | `list(string)` | `null` | no | +| [global\_settings](#input\_global\_settings) | Global settings for the naming convention module. | `any` | n/a | yes | +| [identity](#input\_identity) | type = object({
type = (Required) The type of the Identity. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned`.
identity\_ids = (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this OpenAI Account.
}) |
object({
type = string
identity_ids = optional(list(string))
})
| `null` | no | +| [local\_auth\_enabled](#input\_local\_auth\_enabled) | Whether local authentication methods is enabled for the Cognitive Account. Defaults to `true`. | `bool` | `true` | no | +| [location](#input\_location) | Azure OpenAI deployment region. Set this variable to `null` would use resource group's location. | `string` | n/a | yes | +| [network\_acls](#input\_network\_acls) | type = set(object({
default\_action = (Required) The Default Action to use when no rules match from ip\_rules / virtual\_network\_rules. Possible values are `Allow` and `Deny`.
ip\_rules = (Optional) One or more IP Addresses, or CIDR Blocks which should be able to access the Cognitive Account.
virtual\_network\_rules = optional(set(object({
subnet\_id = (Required) The ID of a Subnet which should be able to access the OpenAI Account.
ignore\_missing\_vnet\_service\_endpoint = (Optional) Whether ignore missing vnet service endpoint or not. Default to `false`.
})))
})) |
set(object({
default_action = string
ip_rules = optional(set(string))
virtual_network_rules = optional(set(object({
subnet_id = string
ignore_missing_vnet_service_endpoint = optional(bool, false)
})))
}))
| `null` | no | +| [outbound\_network\_access\_restricted](#input\_outbound\_network\_access\_restricted) | Whether outbound network access is restricted for the Cognitive Account. Defaults to `false`. | `bool` | `false` | no | +| [public\_network\_access\_enabled](#input\_public\_network\_access\_enabled) | Whether public network access is allowed for the Cognitive Account. Defaults to `false`. | `bool` | `false` | no | +| [resource\_group\_name](#input\_resource\_group\_name) | Name of the azure resource group to use. The resource group must exist. | `string` | n/a | yes | +| [sku\_name](#input\_sku\_name) | Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S0`, `S`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, `P2`, `E0` and `DC0`. Default to `S0`. | `string` | `"S0"` | no | +| [storage](#input\_storage) | type = list(object({
storage\_account\_id = (Required) Full resource id of a Microsoft.Storage resource.
identity\_client\_id = (Optional) The client ID of the managed identity associated with the storage resource.
})) |
list(object({
storage_account_id = string
identity_client_id = optional(string)
}))
| `[]` | no | +| [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [openai\_endpoint](#output\_openai\_endpoint) | The endpoint used to connect to the Cognitive Service Account. | +| [openai\_primary\_key](#output\_openai\_primary\_key) | The primary access key for the Cognitive Service Account. | +| [openai\_secondary\_key](#output\_openai\_secondary\_key) | The secondary access key for the Cognitive Service Account. | +| [openai\_subdomain](#output\_openai\_subdomain) | The subdomain used to connect to the Cognitive Service Account. | + diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/direct-module.tf.bak b/scenarios/shared/terraform-modules/cognitive-services/openai/direct-module.tf.bak new file mode 100644 index 00000000..5a042738 --- /dev/null +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/direct-module.tf.bak @@ -0,0 +1,32 @@ +# This is a "placeholder module" awaiting for some tagging configuration to be integrated into the openai module project + +module "openai" { + source = "Azure/openai/azurerm" + version = "0.1.1" + + resource_group_name = var.resource_group_name + location = var.location + + application_name = var.application_name + account_name = azurecaf_name.caf_name_akv.result + + sku_name = var.sku_name + + storage = var.storage + deployment = var.deployment + + network_acls = var.network_acls + private_endpoint = var.private_endpoint + public_network_access_enabled = var.public_network_access_enabled + custom_subdomain_name = var.custom_subdomain_name + dynamic_throttling_enabled = var.dynamic_throttling_enabled + fqdns = var.fqdns + local_auth_enabled = var.local_auth_enabled + outbound_network_access_restricted = var.outbound_network_access_restricted + + customer_managed_key = var.customer_managed_key + identity = var.identity + + default_tags_enabled = var.tracing_tags_prefix + tracing_tags_enabled = var.tracing_tags_enabled +} diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/main.tf b/scenarios/shared/terraform-modules/cognitive-services/openai/main.tf new file mode 100644 index 00000000..4a4ebfe0 --- /dev/null +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/main.tf @@ -0,0 +1,20 @@ +terraform { + required_version = ">= 1.3.0" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~> 3.0" + } + azurecaf = { + source = "aztfmod/azurecaf" + } + } +} + +locals { + module_tag = { + "module" = basename(abspath(path.module)) + } + tags = merge(local.module_tag, var.tags) +} \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/module.tf b/scenarios/shared/terraform-modules/cognitive-services/openai/module.tf new file mode 100644 index 00000000..75c67de7 --- /dev/null +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/module.tf @@ -0,0 +1,85 @@ +resource "azurecaf_name" "caf_name_oai" { + name = var.application_name + resource_type = "azurerm_cognitive_account" + prefixes = var.global_settings.prefixes + suffixes = var.global_settings.suffixes + random_length = var.global_settings.random_length + clean_input = true + passthrough = var.global_settings.passthrough + + use_slug = var.global_settings.use_slug +} + + +resource "azurerm_cognitive_account" "this" { + kind = "OpenAI" + location = var.location + name = azurecaf_name.caf_name_oai.result + resource_group_name = var.resource_group_name + sku_name = var.sku_name + custom_subdomain_name = coalesce(var.custom_subdomain_name, azurecaf_name.caf_name_oai.result) + dynamic_throttling_enabled = var.dynamic_throttling_enabled + fqdns = var.fqdns + local_auth_enabled = var.local_auth_enabled + outbound_network_access_restricted = var.outbound_network_access_restricted + public_network_access_enabled = var.public_network_access_enabled + + dynamic "customer_managed_key" { + for_each = var.customer_managed_key != null ? [var.customer_managed_key] : [] + content { + key_vault_key_id = customer_managed_key.value.key_vault_key_id + identity_client_id = customer_managed_key.value.identity_client_id + } + } + dynamic "identity" { + for_each = var.identity != null ? [var.identity] : [] + content { + type = identity.value.type + identity_ids = identity.value.identity_ids + } + } + + dynamic "network_acls" { + for_each = var.network_acls != null ? var.network_acls : [] + content { + default_action = network_acls.value.default_action + ip_rules = lookup(network_acls.value, "ip_rules", null) + + dynamic "virtual_network_rules" { + for_each = lookup(network_acls.value, "virtual_network_rules", []) + content { + subnet_id = virtual_network_rules.value.subnet_id + ignore_missing_vnet_service_endpoint = virtual_network_rules.value.ignore_missing_vnet_service_endpoint + } + } + } + } + + dynamic "storage" { + for_each = var.storage + content { + storage_account_id = storage.value.storage_account_id + identity_client_id = storage.value.identity_client_id + } + } + + tags = local.tags +} + +resource "azurerm_cognitive_deployment" "this" { + for_each = var.deployment + + cognitive_account_id = azurerm_cognitive_account.this.id + name = each.value.name + rai_policy_name = each.value.rai_policy_name + + model { + format = each.value.model_format + name = each.value.model_name + version = each.value.model_version + } + scale { + type = each.value.scale_type + } +} + diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/outputs.tf b/scenarios/shared/terraform-modules/cognitive-services/openai/outputs.tf new file mode 100644 index 00000000..8c91739d --- /dev/null +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/outputs.tf @@ -0,0 +1,22 @@ +output "openai_endpoint" { + description = "The endpoint used to connect to the Cognitive Service Account." + value = azurerm_cognitive_account.this.endpoint +} + +output "openai_primary_key" { + description = "The primary access key for the Cognitive Service Account." + sensitive = true + value = azurerm_cognitive_account.this.primary_access_key +} + +output "openai_secondary_key" { + description = "The secondary access key for the Cognitive Service Account." + sensitive = true + value = azurerm_cognitive_account.this.secondary_access_key +} + +output "openai_subdomain" { + description = "The subdomain used to connect to the Cognitive Service Account." + value = azurerm_cognitive_account.this.custom_subdomain_name +} + diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/private-endpoint.tf b/scenarios/shared/terraform-modules/cognitive-services/openai/private-endpoint.tf new file mode 100644 index 00000000..7aacf6e5 --- /dev/null +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/private-endpoint.tf @@ -0,0 +1,95 @@ +resource "azurecaf_name" "priv_endpoint" { + name = var.application_name + resource_type = "azurerm_private_endpoint" +} + +module "private_endpoint" { + source = "../../private-endpoint" + + name = azurecaf_name.priv_endpoint.result + resource_group = var.resource_group_name + location = var.location + subnet_id = var.pe_private_link_subnet_id + private_connection_resource_id = azurerm_cognitive_account.this.id + + subresource_names = var.pe_subresource + + private_dns_zone = var.private_dns_zone +} + +# locals { +# private_dns_zone_id = length(var.private_endpoint) > 0 ? try(azurerm_private_dns_zone.dns_zone[0].id, data.azurerm_private_dns_zone.dns_zone[0].id) : null +# private_dns_zone_name = length(var.private_endpoint) > 0 ? try(azurerm_private_dns_zone.dns_zone[0].name, data.azurerm_private_dns_zone.dns_zone[0].name) : null +# } + +# resource "azurerm_private_endpoint" "this" { +# for_each = var.private_endpoint + +# location = data.azurerm_resource_group.pe_vnet_rg[each.key].location +# name = each.value.name +# resource_group_name = data.azurerm_resource_group.pe_vnet_rg[each.key].name +# subnet_id = data.azurerm_subnet.pe_subnet[each.key].id +# tags = merge(local.tags, (/**/ (var.tracing_tags_enabled ? { for k, v in /**/ { +# avm_git_commit = "c8b6b17b0b28a2aa54a3e734b9bd0a0d0ef5c267" +# avm_git_file = "privateendpoint.tf" +# avm_git_last_modified_at = "2023-05-04 10:08:08" +# avm_git_org = "Azure" +# avm_git_repo = "terraform-azurerm-openai" +# avm_yor_trace = "165734a0-e538-423c-a70a-c13ca973ad6f" +# } /**/ : replace(k, "avm_", var.tracing_tags_prefix) => v } : {}) /**/)) + +# private_service_connection { +# is_manual_connection = each.value.is_manual_connection +# name = each.value.private_service_connection_name +# private_connection_resource_id = azurerm_cognitive_account.this.id +# subresource_names = var.pe_subresource +# } +# dynamic "private_dns_zone_group" { +# for_each = each.value.private_dns_entry_enabled ? ["private_dns_zone_group"] : [] + +# content { +# name = local.private_dns_zone_name +# private_dns_zone_ids = [local.private_dns_zone_id] +# } +# } +# } + +# data "azurerm_private_dns_zone" "dns_zone" { +# count = length(var.private_endpoint) > 0 && var.private_dns_zone != null ? 1 : 0 + +# name = var.private_dns_zone.name +# resource_group_name = var.private_dns_zone.resource_group_name +# } + +# resource "azurerm_private_dns_zone" "dns_zone" { +# count = length(var.private_endpoint) > 0 && var.private_dns_zone == null ? 1 : 0 + +# name = "privatelink.openai.azure.com" +# resource_group_name = data.azurerm_resource_group.this.name +# tags = merge(local.tags, (/**/ (var.tracing_tags_enabled ? { for k, v in /**/ { +# avm_git_commit = "0dfe2497a0421d4c7abd975088122ab600ce7c3d" +# avm_git_file = "privateendpoint.tf" +# avm_git_last_modified_at = "2023-05-09 12:30:19" +# avm_git_org = "Azure" +# avm_git_repo = "terraform-azurerm-openai" +# avm_yor_trace = "db697883-e7b7-40fe-9de4-0995b2827243" +# } /**/ : replace(k, "avm_", var.tracing_tags_prefix) => v } : {}) /**/)) +# } + +# resource "azurerm_private_dns_zone_virtual_network_link" "dns_zone_link" { +# for_each = var.private_endpoint + +# name = each.value.dns_zone_virtual_network_link_name +# private_dns_zone_name = local.private_dns_zone_name +# resource_group_name = data.azurerm_resource_group.this.name +# virtual_network_id = data.azurerm_virtual_network.vnet[each.key].id +# registration_enabled = false +# tags = merge(local.tags, (/**/ (var.tracing_tags_enabled ? { for k, v in /**/ { +# avm_git_commit = "c8b6b17b0b28a2aa54a3e734b9bd0a0d0ef5c267" +# avm_git_file = "privateendpoint.tf" +# avm_git_last_modified_at = "2023-05-04 10:08:08" +# avm_git_org = "Azure" +# avm_git_repo = "terraform-azurerm-openai" +# avm_yor_trace = "4f710a67-53d3-47fa-8c7f-b44e0e7cfe7f" +# } /**/ : replace(k, "avm_", var.tracing_tags_prefix) => v } : {}) /**/)) +# } \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf b/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf new file mode 100644 index 00000000..44f9a7e0 --- /dev/null +++ b/scenarios/shared/terraform-modules/cognitive-services/openai/variables.tf @@ -0,0 +1,308 @@ +variable "location" { + type = string + description = "Azure OpenAI deployment region. Set this variable to `null` would use resource group's location." +} + +variable "resource_group_name" { + type = string + description = "Name of the azure resource group to use. The resource group must exist." +} + +variable "application_name" { + type = string + default = "" + description = "Name of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`." +} + +variable "custom_subdomain_name" { + type = string + default = null + description = "The subdomain name used for token-based authentication. Changing this forces a new resource to be created. Leave this variable as default would use a default name with random suffix." +} + +variable "customer_managed_key" { + type = object({ + key_vault_key_id = string + identity_client_id = optional(string) + }) + default = null + description = <<-DESCRIPTION + type = object({ + key_vault_key_id = (Required) The ID of the Key Vault Key which should be used to Encrypt the data in this OpenAI Account. + identity_client_id = (Optional) The Client ID of the User Assigned Identity that has access to the key. This property only needs to be specified when there're multiple identities attached to the OpenAI Account. + }) + DESCRIPTION +} + +variable "default_tags_enabled" { + type = bool + default = false + description = "Determines whether or not default tags are applied to resources. If set to true, tags will be applied. If set to false, tags will not be applied." + nullable = false +} + +variable "deployment" { + type = map(object({ + name = string + model_format = string + model_name = string + model_version = string + scale_type = string + rai_policy_name = optional(string) + })) + default = {} + description = <<-DESCRIPTION + type = map(object({ + name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created. + cognitive_account_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created. + model = { + model_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI. + model_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. + model_version = (Required) The version of Cognitive Services Account Deployment model. + } + scale = { + scale_type = (Required) Deployment scale type. Possible value is Standard. Changing this forces a new resource to be created. + } + rai_policy_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created. + })) + DESCRIPTION + nullable = false +} + +variable "dynamic_throttling_enabled" { + type = bool + default = null + description = "Determines whether or not dynamic throttling is enabled. If set to `true`, dynamic throttling will be enabled. If set to `false`, dynamic throttling will not be enabled." +} + +variable "environment" { + type = string + default = "" + description = "Environment of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`." +} + +variable "fqdns" { + type = list(string) + default = null + description = "List of FQDNs allowed for the Cognitive Account." +} + +variable "identity" { + type = object({ + type = string + identity_ids = optional(list(string)) + }) + default = { + type = "SystemAssigned" + } + description = <<-DESCRIPTION + type = object({ + type = (Required) The type of the Identity. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned`. + identity_ids = (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this OpenAI Account. + }) + DESCRIPTION +} + +variable "local_auth_enabled" { + type = bool + default = true + description = "Whether local authentication methods is enabled for the Cognitive Account. Defaults to `true`." +} + +variable "network_acls" { + type = set(object({ + default_action = string + ip_rules = optional(set(string)) + virtual_network_rules = optional(set(object({ + subnet_id = string + ignore_missing_vnet_service_endpoint = optional(bool, false) + }))) + })) + default = null + description = <<-DESCRIPTION + type = set(object({ + default_action = (Required) The Default Action to use when no rules match from ip_rules / virtual_network_rules. Possible values are `Allow` and `Deny`. + ip_rules = (Optional) One or more IP Addresses, or CIDR Blocks which should be able to access the Cognitive Account. + virtual_network_rules = optional(set(object({ + subnet_id = (Required) The ID of a Subnet which should be able to access the OpenAI Account. + ignore_missing_vnet_service_endpoint = (Optional) Whether ignore missing vnet service endpoint or not. Default to `false`. + }))) + })) + DESCRIPTION +} + +variable "outbound_network_access_restricted" { + type = bool + default = false + description = "Whether outbound network access is restricted for the Cognitive Account. Defaults to `false`." +} + +variable "pe_subresource" { + type = list(string) + default = ["account"] + description = "A list of subresource names which the Private Endpoint is able to connect to. `subresource_names` corresponds to `group_id`. Possible values are detailed in the product [documentation](https://docs.microsoft.com/azure/private-link/private-endpoint-overview#private-link-resource) in the `Subresources` column. Changing this forces a new resource to be created." +} + +variable "pe_private_link_subnet_id" { + type = string + description = "The ID of the Subnet which the Private Endpoint should be created in. Changing this forces a new resource to be created." +} + +variable "private_dns_zone" { + type = object({ + id = string + name = string + resource_group_name = string + }) + description = "The Private DNS Zone you'd like to use." +} + +# variable "private_dns_zone" { +# type = object({ +# name = string +# resource_group_name = optional(string) +# }) +# default = null +# description = <<-DESCRIPTION +# A map of object that represents the existing Private DNS Zone you'd like to use. Leave this variable as default would create a new Private DNS Zone. +# type = object({ +# name = "(Required) The name of the Private DNS Zone." +# resource_group_name = "(Optional) The Name of the Resource Group where the Private DNS Zone exists. If the Name of the Resource Group is not provided, the first Private DNS Zone from the list of Private DNS Zones in your subscription that matches `name` will be returned." +# } +# DESCRIPTION +# } + +# variable "private_endpoint" { +# type = map(object({ +# name = string +# vnet_rg_name = string +# vnet_name = string +# subnet_name = string +# dns_zone_virtual_network_link_name = optional(string, "dns_zone_link") +# private_dns_entry_enabled = optional(bool, false) +# private_service_connection_name = optional(string, "privateserviceconnection") +# is_manual_connection = optional(bool, false) +# })) +# default = {} +# description = <<-DESCRIPTION +# A map of objects that represent the configuration for a private endpoint." +# type = map(object({ +# name = (Required) Specifies the Name of the Private Endpoint. Changing this forces a new resource to be created. +# vnet_rg_name = (Required) Specifies the name of the Resource Group where the Private Endpoint's Virtual Network Subnet exists. Changing this forces a new resource to be created. +# vnet_name = (Required) Specifies the name of the Virtual Network where the Private Endpoint's Subnet exists. Changing this forces a new resource to be created. +# subnet_name = (Required) Specifies the name of the Subnet which Private IP Addresses will be allocated for this Private Endpoint. Changing this forces a new resource to be created. +# dns_zone_virtual_network_link_name = (Optional) The name of the Private DNS Zone Virtual Network Link. Changing this forces a new resource to be created. Default to `dns_zone_link`. +# private_dns_entry_enabled = (Optional) Whether or not to create a `private_dns_zone_group` block for the Private Endpoint. Default to `false`. +# private_service_connection_name = (Optional) Specifies the Name of the Private Service Connection. Changing this forces a new resource to be created. Default to `privateserviceconnection`. +# is_manual_connection = (Optional) Does the Private Endpoint require Manual Approval from the remote resource owner? Changing this forces a new resource to be created. Default to `false`. +# })) +# DESCRIPTION +# nullable = false +# } + +variable "public_network_access_enabled" { + type = bool + default = false + description = "Whether public network access is allowed for the Cognitive Account. Defaults to `false`." +} + +variable "sku_name" { + type = string + default = "S0" + description = "Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S0`, `S`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, `P2`, `E0` and `DC0`. Default to `S0`." +} + +variable "storage" { + type = list(object({ + storage_account_id = string + identity_client_id = optional(string) + })) + default = [] + description = <<-DESCRIPTION + type = list(object({ + storage_account_id = (Required) Full resource id of a Microsoft.Storage resource. + identity_client_id = (Optional) The client ID of the managed identity associated with the storage resource. + })) + DESCRIPTION + nullable = false +} + +variable "diagnostic_setting" { + type = map(object({ + name = string + log_analytics_workspace_id = optional(string) + log_analytics_destination_type = optional(string) + eventhub_name = optional(string) + eventhub_authorization_rule_id = optional(string) + storage_account_id = optional(string) + partner_solution_id = optional(string) + audit_log_retention_policy = optional(object({ + enabled = optional(bool, true) + days = optional(number, 7) + })) + request_response_log_retention_policy = optional(object({ + enabled = optional(bool, true) + days = optional(number, 7) + })) + trace_log_retention_policy = optional(object({ + enabled = optional(bool, true) + days = optional(number, 7) + })) + metric_retention_policy = optional(object({ + enabled = optional(bool, true) + days = optional(number, 7) + })) + })) + default = {} + description = <<-DESCRIPTION + A map of objects that represent the configuration for a diagnostic setting." + type = map(object({ + name = (Required) Specifies the name of the diagnostic setting. Changing this forces a new resource to be created. + log_analytics_workspace_id = (Optional) (Optional) Specifies the resource id of an Azure Log Analytics workspace where diagnostics data should be sent. + log_analytics_destination_type = (Optional) Possible values are `AzureDiagnostics` and `Dedicated`. When set to Dedicated, logs sent to a Log Analytics workspace will go into resource specific tables, instead of the legacy `AzureDiagnostics` table. + eventhub_name = (Optional) Specifies the name of the Event Hub where diagnostics data should be sent. + eventhub_authorization_rule_id = (Optional) Specifies the resource id of an Event Hub Namespace Authorization Rule used to send diagnostics data. + storage_account_id = (Optional) Specifies the resource id of an Azure storage account where diagnostics data should be sent. + partner_solution_id = (Optional) The resource id of the market partner solution where diagnostics data should be sent. For potential partner integrations, click to learn more about partner integration. + audit_log_retention_policy = (Optional) Specifies the retention policy for the audit log. This is a block with the following properties: + enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number. + days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number. + request_response_log_retention_policy = (Optional) Specifies the retention policy for the request response log. This is a block with the following properties: + enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number. + days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number. + trace_log_retention_policy = (Optional) Specifies the retention policy for the trace log. This is a block with the following properties: + enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number. + days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number. + metric_retention_policy = (Optional) Specifies the retention policy for the metric. This is a block with the following properties: + enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number. + days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number. + })) +DESCRIPTION + nullable = false +} + +# tflint-ignore: terraform_unused_declarations +variable "tracing_tags_enabled" { + type = bool + default = false + description = "Whether enable tracing tags that generated by BridgeCrew Yor." + nullable = false +} + +# tflint-ignore: terraform_unused_declarations +variable "tracing_tags_prefix" { + type = string + default = "avm_" + description = "Default prefix for generated tracing tags" + nullable = false +} + +variable "global_settings" { + description = "Global settings for the naming convention module." +} + +variable "tags" { + description = "A mapping of tags to assign to the resource." + type = map(string) + default = {} +} diff --git a/scenarios/shared/terraform-modules/firewall/module.tf b/scenarios/shared/terraform-modules/firewall/module.tf index 33baf1e5..18350893 100644 --- a/scenarios/shared/terraform-modules/firewall/module.tf +++ b/scenarios/shared/terraform-modules/firewall/module.tf @@ -66,20 +66,22 @@ resource "azurerm_monitor_diagnostic_setting" "this" { enabled_log { category_group = "allLogs" - retention_policy { - days = 0 - enabled = false - } + ## `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention + # retention_policy { + # days = 0 + # enabled = false + # } } metric { category = "AllMetrics" enabled = false - retention_policy { - days = 0 - enabled = false - } + ## `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention + # retention_policy { + # days = 0 + # enabled = false + # } } depends_on = [ diff --git a/scenarios/shared/terraform-modules/frontdoor/module.tf b/scenarios/shared/terraform-modules/frontdoor/module.tf index 76a61567..7a2906e6 100644 --- a/scenarios/shared/terraform-modules/frontdoor/module.tf +++ b/scenarios/shared/terraform-modules/frontdoor/module.tf @@ -28,23 +28,26 @@ resource "azurerm_monitor_diagnostic_setting" "this" { enabled_log { category_group = "allLogs" - retention_policy { - days = 0 - enabled = false - } + ## `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention + # retention_policy { + # days = 0 + # enabled = false + # } } metric { category = "AllMetrics" enabled = false - retention_policy { - days = 0 - enabled = false - } + ## `retention_policy` has been deprecated in favor of `azurerm_storage_management_policy` resource - to learn more https://aka.ms/diagnostic_settings_log_retention + # retention_policy { + # days = 0 + # enabled = false + # } } } + locals { endpoint_uris = { for endpoint in module.endpoint : diff --git a/scenarios/shared/terraform-modules/openai/README.md b/scenarios/shared/terraform-modules/openai/README.md new file mode 100644 index 00000000..14258195 --- /dev/null +++ b/scenarios/shared/terraform-modules/openai/README.md @@ -0,0 +1,61 @@ +# openai + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3.0 | +| [azurerm](#requirement\_azurerm) | ~> 3.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azurecaf](#provider\_azurecaf) | 1.2.26 | +| [azurerm](#provider\_azurerm) | 3.72.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azurecaf_name.caf_name_akv](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs/resources/name) | resource | +| [azurerm_cognitive_account.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_account) | resource | +| [azurerm_cognitive_deployment.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_deployment) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [application\_name](#input\_application\_name) | Name of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | +| [custom\_subdomain\_name](#input\_custom\_subdomain\_name) | The subdomain name used for token-based authentication. Changing this forces a new resource to be created. Leave this variable as default would use a default name with random suffix. | `string` | `null` | no | +| [customer\_managed\_key](#input\_customer\_managed\_key) | type = object({
key\_vault\_key\_id = (Required) The ID of the Key Vault Key which should be used to Encrypt the data in this OpenAI Account.
identity\_client\_id = (Optional) The Client ID of the User Assigned Identity that has access to the key. This property only needs to be specified when there're multiple identities attached to the OpenAI Account.
}) |
object({
key_vault_key_id = string
identity_client_id = optional(string)
})
| `null` | no | +| [deployment](#input\_deployment) | type = map(object({
name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created.
cognitive\_account\_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created.
model = {
model\_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI.
model\_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created.
model\_version = (Required) The version of Cognitive Services Account Deployment model.
}
scale = {
scale\_type = (Required) Deployment scale type. Possible value is Standard. Changing this forces a new resource to be created.
}
rai\_policy\_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created.
})) |
map(object({
name = string
model_format = string
model_name = string
model_version = string
scale_type = string
rai_policy_name = optional(string)
}))
| `{}` | no | +| [dynamic\_throttling\_enabled](#input\_dynamic\_throttling\_enabled) | Determines whether or not dynamic throttling is enabled. If set to `true`, dynamic throttling will be enabled. If set to `false`, dynamic throttling will not be enabled. | `bool` | `null` | no | +| [environment](#input\_environment) | Environment of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`. | `string` | `""` | no | +| [fqdns](#input\_fqdns) | List of FQDNs allowed for the Cognitive Account. | `list(string)` | `null` | no | +| [global\_settings](#input\_global\_settings) | Global settings for the naming convention module. | `any` | n/a | yes | +| [identity](#input\_identity) | type = object({
type = (Required) The type of the Identity. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned`.
identity\_ids = (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this OpenAI Account.
}) |
object({
type = string
identity_ids = optional(list(string))
})
| `null` | no | +| [local\_auth\_enabled](#input\_local\_auth\_enabled) | Whether local authentication methods is enabled for the Cognitive Account. Defaults to `true`. | `bool` | `true` | no | +| [location](#input\_location) | Azure OpenAI deployment region. Set this variable to `null` would use resource group's location. | `string` | n/a | yes | +| [network\_acls](#input\_network\_acls) | type = set(object({
default\_action = (Required) The Default Action to use when no rules match from ip\_rules / virtual\_network\_rules. Possible values are `Allow` and `Deny`.
ip\_rules = (Optional) One or more IP Addresses, or CIDR Blocks which should be able to access the Cognitive Account.
virtual\_network\_rules = optional(set(object({
subnet\_id = (Required) The ID of a Subnet which should be able to access the OpenAI Account.
ignore\_missing\_vnet\_service\_endpoint = (Optional) Whether ignore missing vnet service endpoint or not. Default to `false`.
})))
})) |
set(object({
default_action = string
ip_rules = optional(set(string))
virtual_network_rules = optional(set(object({
subnet_id = string
ignore_missing_vnet_service_endpoint = optional(bool, false)
})))
}))
| `null` | no | +| [outbound\_network\_access\_restricted](#input\_outbound\_network\_access\_restricted) | Whether outbound network access is restricted for the Cognitive Account. Defaults to `false`. | `bool` | `false` | no | +| [public\_network\_access\_enabled](#input\_public\_network\_access\_enabled) | Whether public network access is allowed for the Cognitive Account. Defaults to `false`. | `bool` | `false` | no | +| [resource\_group\_name](#input\_resource\_group\_name) | Name of the azure resource group to use. The resource group must exist. | `string` | n/a | yes | +| [sku\_name](#input\_sku\_name) | Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S0`, `S`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, `P2`, `E0` and `DC0`. Default to `S0`. | `string` | `"S0"` | no | +| [storage](#input\_storage) | type = list(object({
storage\_account\_id = (Required) Full resource id of a Microsoft.Storage resource.
identity\_client\_id = (Optional) The client ID of the managed identity associated with the storage resource.
})) |
list(object({
storage_account_id = string
identity_client_id = optional(string)
}))
| `[]` | no | +| [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [openai\_endpoint](#output\_openai\_endpoint) | The endpoint used to connect to the Cognitive Service Account. | +| [openai\_primary\_key](#output\_openai\_primary\_key) | The primary access key for the Cognitive Service Account. | +| [openai\_secondary\_key](#output\_openai\_secondary\_key) | The secondary access key for the Cognitive Service Account. | +| [openai\_subdomain](#output\_openai\_subdomain) | The subdomain used to connect to the Cognitive Service Account. | + diff --git a/scenarios/shared/terraform-modules/openai/main.tf b/scenarios/shared/terraform-modules/openai/main.tf new file mode 100644 index 00000000..4a4ebfe0 --- /dev/null +++ b/scenarios/shared/terraform-modules/openai/main.tf @@ -0,0 +1,20 @@ +terraform { + required_version = ">= 1.3.0" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~> 3.0" + } + azurecaf = { + source = "aztfmod/azurecaf" + } + } +} + +locals { + module_tag = { + "module" = basename(abspath(path.module)) + } + tags = merge(local.module_tag, var.tags) +} \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/openai/module.tf b/scenarios/shared/terraform-modules/openai/module.tf new file mode 100644 index 00000000..0ba5d222 --- /dev/null +++ b/scenarios/shared/terraform-modules/openai/module.tf @@ -0,0 +1,81 @@ +resource "azurecaf_name" "caf_name_akv" { + name = var.application_name + resource_type = "azurerm_cognitive_account" + prefixes = var.global_settings.prefixes + suffixes = var.global_settings.suffixes + random_length = var.global_settings.random_length + clean_input = true + passthrough = var.global_settings.passthrough + + use_slug = var.global_settings.use_slug +} + +resource "azurerm_cognitive_account" "this" { + kind = "OpenAI" + location = var.location + name = azurecaf_name.caf_name_akv.result + resource_group_name = var.resource_group_name + sku_name = var.sku_name + custom_subdomain_name = var.custom_subdomain_name + dynamic_throttling_enabled = var.dynamic_throttling_enabled + fqdns = var.fqdns + local_auth_enabled = var.local_auth_enabled + outbound_network_access_restricted = var.outbound_network_access_restricted + public_network_access_enabled = var.public_network_access_enabled + + dynamic "customer_managed_key" { + for_each = var.customer_managed_key != null ? [var.customer_managed_key] : [] + content { + key_vault_key_id = customer_managed_key.value.key_vault_key_id + identity_client_id = customer_managed_key.value.identity_client_id + } + } + dynamic "identity" { + for_each = var.identity != null ? [var.identity] : [] + content { + type = identity.value.type + identity_ids = identity.value.identity_ids + } + } + dynamic "network_acls" { + for_each = var.network_acls != null ? [var.network_acls] : [] + content { + default_action = network_acls.value.default_action + ip_rules = network_acls.value.ip_rules + + dynamic "virtual_network_rules" { + for_each = network_acls.value.virtual_network_rules != null ? network_acls.value.virtual_network_rules : [] + content { + subnet_id = virtual_network_rules.value.subnet_id + ignore_missing_vnet_service_endpoint = virtual_network_rules.value.ignore_missing_vnet_service_endpoint + } + } + } + } + dynamic "storage" { + for_each = var.storage + content { + storage_account_id = storage.value.storage_account_id + identity_client_id = storage.value.identity_client_id + } + } + + tags = local.tags +} + +resource "azurerm_cognitive_deployment" "this" { + for_each = var.deployment + + cognitive_account_id = azurerm_cognitive_account.this.id + name = each.value.name + rai_policy_name = each.value.rai_policy_name + + model { + format = each.value.model_format + name = each.value.model_name + version = each.value.model_version + } + scale { + type = each.value.scale_type + } +} \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/openai/outputs.tf b/scenarios/shared/terraform-modules/openai/outputs.tf new file mode 100644 index 00000000..8c91739d --- /dev/null +++ b/scenarios/shared/terraform-modules/openai/outputs.tf @@ -0,0 +1,22 @@ +output "openai_endpoint" { + description = "The endpoint used to connect to the Cognitive Service Account." + value = azurerm_cognitive_account.this.endpoint +} + +output "openai_primary_key" { + description = "The primary access key for the Cognitive Service Account." + sensitive = true + value = azurerm_cognitive_account.this.primary_access_key +} + +output "openai_secondary_key" { + description = "The secondary access key for the Cognitive Service Account." + sensitive = true + value = azurerm_cognitive_account.this.secondary_access_key +} + +output "openai_subdomain" { + description = "The subdomain used to connect to the Cognitive Service Account." + value = azurerm_cognitive_account.this.custom_subdomain_name +} + diff --git a/scenarios/shared/terraform-modules/openai/variables.tf b/scenarios/shared/terraform-modules/openai/variables.tf new file mode 100644 index 00000000..2dca2f2d --- /dev/null +++ b/scenarios/shared/terraform-modules/openai/variables.tf @@ -0,0 +1,294 @@ +variable "location" { + type = string + description = "Azure OpenAI deployment region. Set this variable to `null` would use resource group's location." +} + +variable "resource_group_name" { + type = string + description = "Name of the azure resource group to use. The resource group must exist." +} + +variable "application_name" { + type = string + default = "" + description = "Name of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`." +} + +variable "custom_subdomain_name" { + type = string + default = null + description = "The subdomain name used for token-based authentication. Changing this forces a new resource to be created. Leave this variable as default would use a default name with random suffix." +} + +variable "customer_managed_key" { + type = object({ + key_vault_key_id = string + identity_client_id = optional(string) + }) + default = null + description = <<-DESCRIPTION + type = object({ + key_vault_key_id = (Required) The ID of the Key Vault Key which should be used to Encrypt the data in this OpenAI Account. + identity_client_id = (Optional) The Client ID of the User Assigned Identity that has access to the key. This property only needs to be specified when there're multiple identities attached to the OpenAI Account. + }) + DESCRIPTION +} + + +variable "deployment" { + type = map(object({ + name = string + model_format = string + model_name = string + model_version = string + scale_type = string + rai_policy_name = optional(string) + })) + default = {} + description = <<-DESCRIPTION + type = map(object({ + name = (Required) The name of the Cognitive Services Account Deployment. Changing this forces a new resource to be created. + cognitive_account_id = (Required) The ID of the Cognitive Services Account. Changing this forces a new resource to be created. + model = { + model_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI. + model_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. + model_version = (Required) The version of Cognitive Services Account Deployment model. + } + scale = { + scale_type = (Required) Deployment scale type. Possible value is Standard. Changing this forces a new resource to be created. + } + rai_policy_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created. + })) + DESCRIPTION + nullable = false +} + +variable "dynamic_throttling_enabled" { + type = bool + default = null + description = "Determines whether or not dynamic throttling is enabled. If set to `true`, dynamic throttling will be enabled. If set to `false`, dynamic throttling will not be enabled." +} + +variable "environment" { + type = string + default = "" + description = "Environment of the application. A corresponding tag would be created on the created resources if `var.default_tags_enabled` is `true`." +} + +variable "fqdns" { + type = list(string) + default = null + description = "List of FQDNs allowed for the Cognitive Account." +} + +variable "identity" { + type = object({ + type = string + identity_ids = optional(list(string)) + }) + default = null + description = <<-DESCRIPTION + type = object({ + type = (Required) The type of the Identity. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned`. + identity_ids = (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this OpenAI Account. + }) + DESCRIPTION +} + +variable "local_auth_enabled" { + type = bool + default = true + description = "Whether local authentication methods is enabled for the Cognitive Account. Defaults to `true`." +} + +variable "network_acls" { + type = set(object({ + default_action = string + ip_rules = optional(set(string)) + virtual_network_rules = optional(set(object({ + subnet_id = string + ignore_missing_vnet_service_endpoint = optional(bool, false) + }))) + })) + default = null + description = <<-DESCRIPTION + type = set(object({ + default_action = (Required) The Default Action to use when no rules match from ip_rules / virtual_network_rules. Possible values are `Allow` and `Deny`. + ip_rules = (Optional) One or more IP Addresses, or CIDR Blocks which should be able to access the Cognitive Account. + virtual_network_rules = optional(set(object({ + subnet_id = (Required) The ID of a Subnet which should be able to access the OpenAI Account. + ignore_missing_vnet_service_endpoint = (Optional) Whether ignore missing vnet service endpoint or not. Default to `false`. + }))) + })) + DESCRIPTION +} + +variable "outbound_network_access_restricted" { + type = bool + default = false + description = "Whether outbound network access is restricted for the Cognitive Account. Defaults to `false`." +} + +variable "public_network_access_enabled" { + type = bool + default = false + description = "Whether public network access is allowed for the Cognitive Account. Defaults to `false`." +} + +variable "sku_name" { + type = string + default = "S0" + description = "Specifies the SKU Name for this Cognitive Service Account. Possible values are `F0`, `F1`, `S0`, `S`, `S1`, `S2`, `S3`, `S4`, `S5`, `S6`, `P0`, `P1`, `P2`, `E0` and `DC0`. Default to `S0`." +} + +variable "storage" { + type = list(object({ + storage_account_id = string + identity_client_id = optional(string) + })) + default = [] + description = <<-DESCRIPTION + type = list(object({ + storage_account_id = (Required) Full resource id of a Microsoft.Storage resource. + identity_client_id = (Optional) The client ID of the managed identity associated with the storage resource. + })) + DESCRIPTION + nullable = false +} + +variable "global_settings" { + description = "Global settings for the naming convention module." +} + +variable "tags" { + description = "A mapping of tags to assign to the resource." + type = map(string) + default = {} +} + +# variable "default_tags_enabled" { +# type = bool +# default = false +# description = "Determines whether or not default tags are applied to resources. If set to true, tags will be applied. If set to false, tags will not be applied." +# nullable = false +# } +# variable "pe_subresource" { +# type = list(string) +# default = ["account"] +# description = "A list of subresource names which the Private Endpoint is able to connect to. `subresource_names` corresponds to `group_id`. Possible values are detailed in the product [documentation](https://docs.microsoft.com/azure/private-link/private-endpoint-overview#private-link-resource) in the `Subresources` column. Changing this forces a new resource to be created." +# } + + +# variable "private_dns_zone" { +# type = object({ +# name = string +# resource_group_name = optional(string) +# }) +# default = null +# description = <<-DESCRIPTION +# A map of object that represents the existing Private DNS Zone you'd like to use. Leave this variable as default would create a new Private DNS Zone. +# type = object({ +# name = "(Required) The name of the Private DNS Zone." +# resource_group_name = "(Optional) The Name of the Resource Group where the Private DNS Zone exists. If the Name of the Resource Group is not provided, the first Private DNS Zone from the list of Private DNS Zones in your subscription that matches `name` will be returned." +# } +# DESCRIPTION +# } +# variable "diagnostic_setting" { +# type = map(object({ +# name = string +# log_analytics_workspace_id = optional(string) +# log_analytics_destination_type = optional(string) +# eventhub_name = optional(string) +# eventhub_authorization_rule_id = optional(string) +# storage_account_id = optional(string) +# partner_solution_id = optional(string) +# audit_log_retention_policy = optional(object({ +# enabled = optional(bool, true) +# days = optional(number, 7) +# })) +# request_response_log_retention_policy = optional(object({ +# enabled = optional(bool, true) +# days = optional(number, 7) +# })) +# trace_log_retention_policy = optional(object({ +# enabled = optional(bool, true) +# days = optional(number, 7) +# })) +# metric_retention_policy = optional(object({ +# enabled = optional(bool, true) +# days = optional(number, 7) +# })) +# })) +# default = {} +# description = <<-DESCRIPTION +# A map of objects that represent the configuration for a diagnostic setting." +# type = map(object({ +# name = (Required) Specifies the name of the diagnostic setting. Changing this forces a new resource to be created. +# log_analytics_workspace_id = (Optional) (Optional) Specifies the resource id of an Azure Log Analytics workspace where diagnostics data should be sent. +# log_analytics_destination_type = (Optional) Possible values are `AzureDiagnostics` and `Dedicated`. When set to Dedicated, logs sent to a Log Analytics workspace will go into resource specific tables, instead of the legacy `AzureDiagnostics` table. +# eventhub_name = (Optional) Specifies the name of the Event Hub where diagnostics data should be sent. +# eventhub_authorization_rule_id = (Optional) Specifies the resource id of an Event Hub Namespace Authorization Rule used to send diagnostics data. +# storage_account_id = (Optional) Specifies the resource id of an Azure storage account where diagnostics data should be sent. +# partner_solution_id = (Optional) The resource id of the market partner solution where diagnostics data should be sent. For potential partner integrations, click to learn more about partner integration. +# audit_log_retention_policy = (Optional) Specifies the retention policy for the audit log. This is a block with the following properties: +# enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number. +# days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number. +# request_response_log_retention_policy = (Optional) Specifies the retention policy for the request response log. This is a block with the following properties: +# enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number. +# days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number. +# trace_log_retention_policy = (Optional) Specifies the retention policy for the trace log. This is a block with the following properties: +# enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number. +# days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number. +# metric_retention_policy = (Optional) Specifies the retention policy for the metric. This is a block with the following properties: +# enabled = (Optional) Specifies whether the retention policy is enabled. If enabled, `days` must be a positive number. +# days = (Optional) Specifies the number of days to retain trace logs. If `enabled` is set to `true`, this value must be set to a positive number. +# })) +# DESCRIPTION +# nullable = false +# } + +# # tflint-ignore: terraform_unused_declarations +# variable "tracing_tags_enabled" { +# type = bool +# default = false +# description = "Whether enable tracing tags that generated by BridgeCrew Yor." +# nullable = false +# } + +# # tflint-ignore: terraform_unused_declarations +# variable "tracing_tags_prefix" { +# type = string +# default = "avm_" +# description = "Default prefix for generated tracing tags" +# nullable = false +# } + + + +# variable "private_endpoint" { +# type = map(object({ +# name = string +# vnet_rg_name = string +# vnet_name = string +# subnet_name = string +# dns_zone_virtual_network_link_name = optional(string, "dns_zone_link") +# private_dns_entry_enabled = optional(bool, false) +# private_service_connection_name = optional(string, "privateserviceconnection") +# is_manual_connection = optional(bool, false) +# })) +# default = {} +# description = <<-DESCRIPTION +# A map of objects that represent the configuration for a private endpoint." +# type = map(object({ +# name = (Required) Specifies the Name of the Private Endpoint. Changing this forces a new resource to be created. +# vnet_rg_name = (Required) Specifies the name of the Resource Group where the Private Endpoint's Virtual Network Subnet exists. Changing this forces a new resource to be created. +# vnet_name = (Required) Specifies the name of the Virtual Network where the Private Endpoint's Subnet exists. Changing this forces a new resource to be created. +# subnet_name = (Required) Specifies the name of the Subnet which Private IP Addresses will be allocated for this Private Endpoint. Changing this forces a new resource to be created. +# dns_zone_virtual_network_link_name = (Optional) The name of the Private DNS Zone Virtual Network Link. Changing this forces a new resource to be created. Default to `dns_zone_link`. +# private_dns_entry_enabled = (Optional) Whether or not to create a `private_dns_zone_group` block for the Private Endpoint. Default to `false`. +# private_service_connection_name = (Optional) Specifies the Name of the Private Service Connection. Changing this forces a new resource to be created. Default to `privateserviceconnection`. +# is_manual_connection = (Optional) Does the Private Endpoint require Manual Approval from the remote resource owner? Changing this forces a new resource to be created. Default to `false`. +# })) +# DESCRIPTION +# nullable = false +# } \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/private-endpoint/README.md b/scenarios/shared/terraform-modules/private-endpoint/README.md index 9319f210..90dcab06 100644 --- a/scenarios/shared/terraform-modules/private-endpoint/README.md +++ b/scenarios/shared/terraform-modules/private-endpoint/README.md @@ -29,11 +29,12 @@ No modules. | [location](#input\_location) | The location of the resource group where the private endpoint will be created | `string` | n/a | yes | | [name](#input\_name) | The name of the private endpoint | `string` | n/a | yes | | [private\_connection\_resource\_id](#input\_private\_connection\_resource\_id) | The id of the resource to which the private endpoint will be connected | `string` | n/a | yes | -| [private\_dns\_records](#input\_private\_dns\_records) | The dns records to be created for the private endpoint | `list(string)` | n/a | yes | +| [private\_dns\_records](#input\_private\_dns\_records) | The dns records to be created for the private endpoint | `list(string)` | `[]` | no | | [private\_dns\_zone](#input\_private\_dns\_zone) | The private dns zone id where the app service will be integrated |
object({
id = string
name = string
resource_group_name = string
})
| n/a | yes | | [resource\_group](#input\_resource\_group) | The name of the resource group where the private endpoint will be created | `string` | n/a | yes | | [subnet\_id](#input\_subnet\_id) | The id of the subnet where the private endpoint will be created | `string` | n/a | yes | | [subresource\_names](#input\_subresource\_names) | The subresource names of the resource to which the private endpoint will be connected | `list(string)` | n/a | yes | +| [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no | | [ttl](#input\_ttl) | The time to live of the dns records | `number` | `300` | no | ## Outputs diff --git a/scenarios/shared/terraform-modules/private-endpoint/main.tf b/scenarios/shared/terraform-modules/private-endpoint/main.tf index cb76bdba..132b090b 100644 --- a/scenarios/shared/terraform-modules/private-endpoint/main.tf +++ b/scenarios/shared/terraform-modules/private-endpoint/main.tf @@ -1,24 +1,14 @@ - -resource "azurerm_private_endpoint" "this" { - name = var.name - resource_group_name = var.resource_group - location = var.location - subnet_id = var.subnet_id - - private_service_connection { - name = var.name - private_connection_resource_id = var.private_connection_resource_id - subresource_names = var.subresource_names - is_manual_connection = false +terraform { + required_providers { + azurecaf = { + source = "aztfmod/azurecaf" + } } } -resource "azurerm_private_dns_a_record" "this" { - count = length(var.private_dns_records) - - name = var.private_dns_records[count.index] - zone_name = var.private_dns_zone.name - resource_group_name = var.private_dns_zone.resource_group_name - ttl = var.ttl - records = [azurerm_private_endpoint.this.private_service_connection[0].private_ip_address] +locals { + module_tag = { + "module" = basename(abspath(path.module)) + } + tags = merge(local.module_tag, var.tags) } \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/private-endpoint/module.tf b/scenarios/shared/terraform-modules/private-endpoint/module.tf new file mode 100644 index 00000000..ef153253 --- /dev/null +++ b/scenarios/shared/terraform-modules/private-endpoint/module.tf @@ -0,0 +1,28 @@ + +resource "azurerm_private_endpoint" "this" { + name = var.name + resource_group_name = var.resource_group + location = var.location + subnet_id = var.subnet_id + + private_service_connection { + name = var.name + private_connection_resource_id = var.private_connection_resource_id + subresource_names = var.subresource_names + is_manual_connection = false + } + + tags = local.tags +} + +resource "azurerm_private_dns_a_record" "this" { + count = length(var.private_dns_records) + + name = var.private_dns_records[count.index] + zone_name = var.private_dns_zone.name + resource_group_name = var.private_dns_zone.resource_group_name + ttl = var.ttl + records = [azurerm_private_endpoint.this.private_service_connection[0].private_ip_address] + + tags = local.tags +} \ No newline at end of file diff --git a/scenarios/shared/terraform-modules/private-endpoint/variables.tf b/scenarios/shared/terraform-modules/private-endpoint/variables.tf index 67f4de38..6ab45646 100644 --- a/scenarios/shared/terraform-modules/private-endpoint/variables.tf +++ b/scenarios/shared/terraform-modules/private-endpoint/variables.tf @@ -31,6 +31,7 @@ variable "subresource_names" { variable "private_dns_records" { type = list(string) description = "The dns records to be created for the private endpoint" + default = [] } variable "private_dns_zone" { @@ -47,4 +48,10 @@ variable "ttl" { type = number description = "The time to live of the dns records" default = 300 +} + +variable "tags" { + description = "A mapping of tags to assign to the resource." + type = map(string) + default = {} } \ No newline at end of file