main.tf of keyvault : ************************ locals { key_vault_name = coalesce(var.key_vault_name, module.solution_settings.key_vault_name) resource_group_name = coalesce(var.resource_group_name, var.solution_settings["resource_group_name"]) } data "azurerm_virtual_network" "vnet" { name = var.solution_settings["virtual_network_name"] resource_group_name = var.solution_settings["vnet_resource_group_name"] } data "azurerm_subnet" "akv_subnet" { name = var.akv_subnet resource_group_name = var.solution_settings["vnet_resource_group_name"] virtual_network_name = data.azurerm_virtual_network.vnet.name } resource "azurerm_key_vault" "datalib_key_vault" { name = local.key_vault_name resource_group_name = local.resource_group_name location = module.solution_settings.location tenant_id = var.azure_tenant_id public_network_access_enabled = !var.solution_settings["highly_confidential"] soft_delete_retention_days = 7 purge_protection_enabled = true sku_name = "standard" tags = var.tags enabled_for_deployment = "true" enabled_for_disk_encryption = "true" enabled_for_template_deployment = "false" network_acls { bypass = "AzureServices" default_action = "Deny" virtual_network_subnet_ids = [] } } resource "azurerm_monitor_diagnostic_setting" "akv-monitor" { name = "${local.key_vault_name}log" target_resource_id = azurerm_key_vault.datalib_key_vault.id log_analytics_workspace_id = var.log_analytics_id lifecycle { ignore_changes = [ # Ignore changes to tags, e.g. because a management agent # updates these based on some ruleset managed elsewhere. log_analytics_workspace_id, log, metric ] } log { category = "AuditEvent" enabled = true } } resource "azurerm_key_vault_access_policy" "acs_prov" { key_vault_id = azurerm_key_vault.datalib_key_vault.id tenant_id = var.azure_tenant_id object_id = data.azuread_group.prov_group.id depends_on = [azurerm_key_vault.datalib_key_vault] secret_permissions = [ "Set", "Get", "List", "Delete", "Recover", ] key_permissions = [ "Get", "List", "Delete", "Update", "Create", "WrapKey", "UnwrapKey", "Rotate", "GetRotationPolicy", "SetRotationPolicy" ] certificate_permissions = [ ] } resource "azurerm_private_endpoint" "pe1" { name = "${local.key_vault_name}pe" location = var.solution_settings["location"] resource_group_name = local.resource_group_name subnet_id = data.azurerm_subnet.akv_subnet.id private_service_connection { name = "${local.key_vault_name}psc" is_manual_connection = false private_connection_resource_id = azurerm_key_vault.datalib_key_vault.id subresource_names = ["vault"] } lifecycle { ignore_changes = [ network_interface, subnet_id ] } } ------------------------------------------------------------------------------------------------------------------------------------------------------------------ variables.tf of keyvault : *************************** variable "solution_settings" { type = any validation { condition = contains(["1.0.0"], var.solution_settings.schema.version) error_message = "Unsupported schema version." } default = "globalmm" } variable "tags" { type = map(any) default = { "Environment" : "Dev", "Program" : "TEST", "Project" : "TESTPROJ", "Application" : "Data Blocks", "Product" : "TESTPRO", } } variable "resource_group_name" { description = "Name of the RG to deploy the Databricks instance to. If left to default, will set to the RG in solution_settings." type = string default = "" } variable "key_vault_name" { description = "The name of the keyvault. If none given it will generate a default using the key_vault_suffix variable." type = string default = "" } variable "akv_subnet" { description = "Name of the AKV subnet for Key vault Private endpoint" type = string default = "" } variable "azure_tenant_id" { description = "Azure tenant ID" type = string default = "***" } variable "log_analytics_id" { description = "the id of the log analytics to integrate with, use the log analytics module to get this result." type = string nullable = false default = "/subscriptions/subid/resourcegroups/rgname/providers/microsoft.operationalinsights/workspaces/loganalyticsname" } ------------------------------------------------------------------------------------------------------------------------------------------------------------------ solutionsettings.tf: ********************** module "solution_settings" { source = "./solutionsettings" solution_name = "testmm" location = "westeurope" resource_group_name = "testrg" key_vault_name = "kvtest01" environment = "dev" devops_tenant_id = "***" tags = { "Environment" : "Dev", "Program" : "TEST", "Project" : "TESTPROJ", "Application" : "Data Blocks", "Product" : "TESTPRO", "CMDB" : "test", "Charge Reference" : "0000000000", "Charge Type" : "Cost Center", "Region" : "Europe", "Segment" : "Testsegment", "Banner" : "TESTBANNER", "Function" : "Demand", "Business Owner" : "***@***.com", "Product Owner" : "***@***.com", "Application Architect" : "***@***.com", "Data Classification" : "Internal Use Only", "Application Category" : "Class C", "Infrastructure Tier" : "Class C", "Expiry" : "9999-12-31", "Created" : "2023-06-01", "Created By" : "Terraform", "Parent" : "TESTHUB", "Brand" : "", "Business Unit" : "test", "Charge Reference" : "0000000000", "CMDB" : "", "Data Classification" : "Confidential", "Data Sensitivity" : "Non-PII", "Division" : "", "Infrastructure Tier" : "", "Parent" : "", "Product" : "", "Product Owner" : "", "Program" : "TEST", "Project" : "TESTProject", "Region" : "Global", "Support Model" : "***@***.com", "Updated" : "2023-06-01" } vnet_name = "testvnet" vnet_rg = "testrg" } ------------------------------------------------------------------------------------------------------------------------------------------------------------------ Module Solutionsettings: main.tf of solution settings : ****************************** locals { location_ab_dict = { westus = "wus" eastus = "eus" centralus = "cus" eastus2 = "eus2" centraluseuap = "usia" southcentralus = "scus" northcentralus = "ncus" northeurope = "neu" westeurope = "weu" eastasia = "eas" southeastasia = "sea" japanwest = "japw" japaneast = "jape" southindia = "sin" westindia = "win" centralindia = "cin" australiaeast = "aue" australiasoutheast = "ause" uksouth = "uks" ukwest = "ukw" } solution_name_short = replace(var.solution_name, " ", "-") location_abrv = local.location_ab_dict[var.location] name_prefix = lower("${substr(replace(lower(var.solution_name), "/[-| _]/", ""), 0, 13)}${local.location_abrv}${var.environment}") key_vault_name = "${local.name_prefix}akv" app_insights_name = "${local.name_prefix}ai" app_insights_secret_name = "${local.name_prefix}aik" log_analytics_name = "${local.name_prefix}law" sa_name = "${local.name_prefix}sa" } data "azurerm_client_config" "current" {} ------------------------------------------------------------------------------------------------------------------------------------------------------------------ variables.tf of solution settings module : ********************************************** variable "solution_name" { description = "Solution name" type = map(any) validation { condition = can(regex("^[A-Za-z][\\w]{1,23}[A-Za-z0-9]$", var.solution_name)) error_message = "Can include up to 25 alphanumeric characters (including underscope). Should start with letter and end with letter or digit." } } variable "location" { type = string } variable "resource_group_name" { type = string } variable "key_vault_name" { description = "The name of the keyvault. If none given it will generate a default using the key_vault_suffix variable." type = string default = "" } variable "environment" { description = "Environment" type = string validation { condition = contains(["dev", "prod"], var.environment) error_message = "Valid environments are: dev, prod." } } variable "devops_tenant_id" { type = string description = "Azure Devops tenant Id" default = "***" } variable "tags" { type = map(any) description = "a dictionary (map) of tags to propagate" validation { condition = can([for item in ["Environment", "Brand", "Program", "Project", "Application", "Product", "Charge Reference", "Charge Type", "Region", "Segment", "Division", "Function", "Business Owner", "Business Unit", "Product Owner", "Application Architect", "Data Classification", "Data Sensitivity", "Application Category", "Infrastructure Tier", "Support Model", "Expiry", "Created", "Created By", "Updated", "Parent"] : var.tags[item]]) error_message = "Following properties must be populated: 'Environment', 'Brand', 'Program', 'Project', 'Application', 'Product', 'CMDB', 'Charge Reference', 'Charge Type', 'Region', 'Segment', 'Division', 'Function', 'Business Owner', 'Business Unit', 'Product Owner', 'Application Architect', 'Data Classification', 'Data Sensitivity', 'Application Category', 'Infrastructure Tier', 'Support Model', 'Expiry', 'Created', 'Created By', 'Updated', 'Parent'." } validation { condition = contains(["Dev", "Prod"], var.tags["Environment"]) error_message = "'Environment' must be either 'Dev', 'Prod'." } validation { condition = contains(["Cost Center", "Internal Order"], var.tags["Charge Type"]) error_message = "'Charge Type' must be either 'Cost Center' or 'Internal Order'." } validation { condition = contains(["Global", "Asia Pacific", "Europe", "India", "Latin America", "North America", "CIS RU"], var.tags["Region"]) error_message = "'Region' must be either of: 'Global', 'Asia Pacific', 'Europe', 'India', 'Latin America', 'North America', 'CIS RU'." } validation { condition = contains(["Corporate", "Cross Segment", "Food", "* Global Services", "* Wrigley", "Multisales", "Petcare", "Other"], var.tags["Segment"]) error_message = "'Segment' must be either of: 'Corporate', 'Cross Segment', 'Food', '* Global Services', '', 'Multisales', '', 'Other'." } validation { condition = contains(["Commercial", "Corporate Strategy", "Cross MGS", "Finance", "Manufacturing", "Marketing", "P&O", "R&D", "Sales", "SCM", "Supply", "Demand"], var.tags["Function"]) error_message = "'Function' must be either of: 'Commercial', 'Corporate Strategy', 'Cross MGS', 'Finance', 'Manufacturing', 'Marketing', 'P&O', 'R&D', 'Sales', 'SCM', Supply', 'Demand'." } validation { condition = contains(["Highly Confidential", "Confidential", "Internal Use Only", "Non-Confidential"], var.tags["Data Classification"]) error_message = "'Data Classification' must be either of: 'Highly Confidential', 'Confidential', 'Internal Use Only', 'Non-Confidential'." } validation { condition = contains(["PII", "Non-PII", "UserID"], var.tags["Data Sensitivity"]) error_message = "'Data Sensitivity' must be either 'PII', 'Non-PII', or 'UserID'." } validation { condition = contains(["Manual", "Terraform", "Provisioning Tool"], var.tags["Created By"]) error_message = "'Created By' must be either of: 'Manual', 'Terraform', 'Provisioning Tool'." } validation { condition = substr(var.tags["Support Model"], -10, -1) == "@***.com" error_message = "'Support Model' must contain: @***.com'." } validation { condition = substr(var.tags["Application Architect"], -10, -1) == "@***.com" error_message = "'Application Architect' must contain: @***.com'." } validation { condition = substr(var.tags["Business Owner"], -10, -1) == "@***.com" error_message = "'Business Owner' must contain: @***.com'." } validation { condition = can(regex("^[0-9]{4}-[0-9]{2}-[0-9]{2}$", var.tags["Created"])) error_message = "'Created' must be of format: 'YYYY-MM-DD'." } validation { condition = can(regex("^[0-9]{4}-[0-9]{2}-[0-9]{2}$", var.tags["Expiry"])) error_message = "'Expiry' must be of format: 'YYYY-MM-DD'." } validation { condition = can(regex("^[0-9]{4}-[0-9]{2}-[0-9]{2}$", var.tags["Updated"])) error_message = "'Updated' must be of format: 'YYYY-MM-DD'." } validation { condition = length(var.tags["Application"]) > 0 error_message = "'Application' must contain text." } validation { condition = length(var.tags["Project"]) > 0 error_message = "'Project' must contain text." } validation { condition = length(var.tags["Application Category"]) > 0 error_message = "'Application Category' must contain text." } validation { condition = length(var.tags["Business Unit"]) > 0 error_message = "'Business Unit' must contain text." } validation { condition = length(var.tags["Charge Reference"]) > 0 error_message = "'Charge Reference' must contain text." } validation { condition = length(var.tags["Program"]) > 0 error_message = "'Program' must contain text." } } variable "vnet_name" { type = string description = "Name of the virtual network" } variable "vnet_rg" { type = string description = "Name of the resource group where the virtual network resides" }