Enterprise Tier Blueprint¶
The Enterprise tier adds governance, policy enforcement, and full observability on top of the Foundation tier. It is designed for organizations that need cost tracking, compliance posture, and audit-ready logging.
What This Tier Adds¶
| Addition | Description |
|---|---|
| Policy baseline | Enforces allowed locations, private endpoint requirements, and public PaaS denial |
| Cost-center tag | First-class cost_center variable for chargeback/showback |
| Full diagnostics | All resource IDs in diagnostics targets |
| Future hooks | Prepared for alerts, RBAC, and Defender modules |
What Is Not Included¶
compute/aks_private— disabled at this tier (see Regulated)- Customer-managed keys —
enable_cmk = false - Extended retention —
log_retention_daysstays at default
Recommended Variables¶
enable_policy_baseline = true
require_private_endpoints = true
deny_public_paas = true
cost_center = "CC-AI-0042"
log_retention_days = 90
enable_cmk = false
enable_aks = false
Reference Composition¶
# =============================================================================
# TenantZero AI -- Enterprise Tier Blueprint
# =============================================================================
#
# WHAT THIS IS
# ------------
# A reference composition for organizations that need governance, policy
# enforcement, and full observability on top of the Foundation tier.
# Copy this file into envs/<your-env>/ as a starting point.
#
# THIS FILE IS NOT EXECUTABLE. It is documentation that mirrors the real
# module composition pattern used by every TenantZero environment stack.
#
# WHAT FOUNDATION TIER INCLUDES (unchanged, copied here for completeness)
# -----------------------------------------------------------------------
# - Foundation : naming, tags, resource_group
# - Networking : vnet, private_dns
# - Security : key_vault + private endpoint
# - AI : openai + PE, ai_search + PE
# - Data : cosmos OR postgres + PE
# - Observability: log_analytics, diagnostics
#
# WHAT THIS TIER ADDS
# -------------------
# + governance/policy_baseline (enabled -- enforces location & PE posture)
# + Full diagnostics wiring (all resource IDs in diagnostics_targets)
# + cost_center tag (explicitly set for chargeback / showback)
# + Prepared hooks for future modules (alerts, RBAC, Defender for Cloud)
#
# WHAT IS NOT INCLUDED (see regulated.tf)
# ----------------------------------------
# - compute/aks_private (disabled at this tier)
# - Customer-managed keys (enable_cmk = false)
# - Extended retention (log_retention_days stays at 90)
#
# MODULE PATH CONVENTION
# ----------------------
# All source paths are relative to envs/<env>/ -- i.e. ../../modules/<ns>/<mod>
# =============================================================================
# -- Data source: current Azure context (tenant ID, subscription, etc.) -------
data "azurerm_client_config" "current" {}
# =============================================================================
# Local helpers
# =============================================================================
locals {
group_names = {
core = module.naming.resource_groups.core
net = module.naming.resource_groups.net
sec = module.naming.resource_groups.sec
obs = module.naming.resource_groups.obs
}
private_dns_zones = concat(
[
"privatelink.openai.azure.com",
"privatelink.vaultcore.azure.net",
"privatelink.search.windows.net",
],
var.data_profile == "cosmos"
? ["privatelink.documents.azure.com"]
: ["privatelink.postgres.database.azure.com"]
)
allowed_locations = length(var.allowed_locations) > 0 ? var.allowed_locations : [var.location]
cosmos_zone_id = try(module.private_dns.zone_ids["privatelink.documents.azure.com"], null)
postgres_zone_id = try(module.private_dns.zone_ids["privatelink.postgres.database.azure.com"], null)
postgres_subnet = try(module.networking.subnet_ids["snet-data"], module.networking.subnet_ids["snet-compute"])
# ---------------------------------------------------------------------------
# ENTERPRISE CHANGE: diagnostics_targets includes ALL deployed resources.
# Foundation tier covers openai, key_vault, ai_search, and one data store.
# Enterprise tier is identical today but the list is explicitly documented
# so that future modules (AKS, APIM, etc.) can be appended here.
# ---------------------------------------------------------------------------
diagnostics_targets = compact(concat(
[
module.openai.resource_id,
module.key_vault.resource_id,
module.ai_search.resource_id,
],
var.data_profile == "cosmos"
? [module.cosmos[0].resource_id]
: [module.postgres[0].resource_id]
# Future: append module.aks_private[0].cluster_id when compute is enabled
))
}
# =============================================================================
# 1. FOUNDATION -- naming, tags, resource groups
# =============================================================================
module "naming" {
source = "../../modules/foundation/naming"
client_name = var.client_name # e.g. "contoso"
env = var.env # e.g. "staging"
unique_suffix = var.unique_suffix # e.g. "x7q"
}
# ---------------------------------------------------------------------------
# ENTERPRISE CHANGE: cost_center is explicitly set for chargeback tracking.
# Foundation tier leaves this empty; Enterprise tier requires it.
# ---------------------------------------------------------------------------
module "tags" {
source = "../../modules/foundation/tags"
client_name = var.client_name
env = var.env
cost_center = var.cost_center # e.g. "CC-AI-0042" -- required at this tier
extra_tags = var.tags
}
module "resource_groups" {
source = "../../modules/foundation/resource_group"
groups = {
core = { name = local.group_names.core, location = var.location, tags = module.tags.common_tags }
net = { name = local.group_names.net, location = var.location, tags = module.tags.common_tags }
sec = { name = local.group_names.sec, location = var.location, tags = module.tags.common_tags }
obs = { name = local.group_names.obs, location = var.location, tags = module.tags.common_tags }
}
}
# =============================================================================
# 2. NETWORKING -- VNet, subnets, NSGs, private DNS zones
# =============================================================================
module "networking" {
source = "../../modules/networking/vnet"
name = module.naming.vnet_name
resource_group_name = module.resource_groups.names["net"]
location = var.location
vnet_cidr = var.vnet_cidr
subnets = var.subnets
nsg_rules = var.nsg_rules
tags = module.tags.common_tags
}
module "private_dns" {
source = "../../modules/networking/private_dns"
resource_group_name = module.resource_groups.names["net"]
vnet_id = module.networking.vnet_id
zones = local.private_dns_zones
tags = module.tags.common_tags
}
# =============================================================================
# 3. SECURITY -- Key Vault + private endpoint
# =============================================================================
module "key_vault" {
source = "../../modules/security/key_vault"
name = module.naming.key_vault_name
resource_group_name = module.resource_groups.names["sec"]
location = var.location
tenant_id = data.azurerm_client_config.current.tenant_id
tags = module.tags.common_tags
}
module "pe_key_vault" {
source = "../../modules/networking/private_endpoint"
name = "pe-${module.naming.key_vault_name}"
location = var.location
resource_group_name = module.resource_groups.names["sec"]
subnet_id = module.networking.subnet_ids["snet-pe"]
private_connection_resource_id = module.key_vault.resource_id
subresource_names = ["vault"]
private_dns_zone_ids = [module.private_dns.zone_ids["privatelink.vaultcore.azure.net"]]
tags = module.tags.common_tags
}
# -- CMK key (disabled in Enterprise; enabled in Regulated tier) ---------------
resource "azurerm_key_vault_key" "cmk" {
count = var.enable_cmk ? 1 : 0 # Enterprise: false | Regulated: true
name = "cmk-default"
key_vault_id = module.key_vault.resource_id
key_type = "RSA"
key_size = 2048
key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"]
depends_on = [module.pe_key_vault]
}
# =============================================================================
# 4. AI SERVICES -- Azure OpenAI + AI Search, each with private endpoints
# =============================================================================
module "openai" {
source = "../../modules/ai/openai"
name = module.naming.openai_name
resource_group_name = module.resource_groups.names["core"]
location = var.location
sku = var.openai_sku
public_network_access_enabled = false
deployments = var.models
tags = module.tags.common_tags
}
module "pe_openai" {
source = "../../modules/networking/private_endpoint"
name = "pe-${module.naming.openai_name}"
location = var.location
resource_group_name = module.resource_groups.names["core"]
subnet_id = module.networking.subnet_ids["snet-pe"]
private_connection_resource_id = module.openai.resource_id
subresource_names = ["account"]
private_dns_zone_ids = [module.private_dns.zone_ids["privatelink.openai.azure.com"]]
tags = module.tags.common_tags
}
module "ai_search" {
source = "../../modules/ai/ai_search"
name = module.naming.ai_search_name
resource_group_name = module.resource_groups.names["core"]
location = var.location
sku = var.search_sku
replicas = var.search_replicas
partitions = var.search_partitions
public_network_access_enabled = false
tags = module.tags.common_tags
}
module "pe_ai_search" {
source = "../../modules/networking/private_endpoint"
name = "pe-${module.naming.ai_search_name}"
location = var.location
resource_group_name = module.resource_groups.names["core"]
subnet_id = module.networking.subnet_ids["snet-pe"]
private_connection_resource_id = module.ai_search.resource_id
subresource_names = ["searchService"]
private_dns_zone_ids = [module.private_dns.zone_ids["privatelink.search.windows.net"]]
tags = module.tags.common_tags
}
# =============================================================================
# 5. DATA -- Cosmos DB -or- PostgreSQL
# =============================================================================
module "cosmos" {
count = var.data_profile == "cosmos" ? 1 : 0
source = "../../modules/data/cosmos"
name = module.naming.cosmos_name
resource_group_name = module.resource_groups.names["core"]
location = var.location
tags = module.tags.common_tags
}
module "pe_cosmos" {
count = var.data_profile == "cosmos" ? 1 : 0
source = "../../modules/networking/private_endpoint"
name = "pe-${module.naming.cosmos_name}"
location = var.location
resource_group_name = module.resource_groups.names["core"]
subnet_id = module.networking.subnet_ids["snet-pe"]
private_connection_resource_id = module.cosmos[0].resource_id
subresource_names = ["Sql"]
private_dns_zone_ids = [local.cosmos_zone_id]
tags = module.tags.common_tags
}
module "postgres" {
count = var.data_profile == "postgres" ? 1 : 0
source = "../../modules/data/postgres"
name = module.naming.postgres_name
resource_group_name = module.resource_groups.names["core"]
location = var.location
delegated_subnet_id = local.postgres_subnet
private_dns_zone_id = local.postgres_zone_id
tenant_id = data.azurerm_client_config.current.tenant_id
administrator_password = var.postgres_administrator_password
tags = module.tags.common_tags
}
# =============================================================================
# 6. OBSERVABILITY -- Log Analytics workspace + diagnostic settings
# =============================================================================
# Enterprise tier uses a longer retention (e.g. 90 days) compared to
# Foundation's 30-day default, and wires diagnostics to ALL deployed resources.
module "log_analytics" {
source = "../../modules/observability/log_analytics"
name = module.naming.log_analytics_name
resource_group_name = module.resource_groups.names["obs"]
location = var.location
log_retention_days = var.log_retention_days # Enterprise recommended: 90
tags = module.tags.common_tags
}
module "diagnostics" {
source = "../../modules/observability/diagnostics"
# ---------------------------------------------------------------------------
# ENTERPRISE CHANGE: Full diagnostics wiring.
# Every resource that supports diagnostic settings is included here.
# As new modules are added (AKS, APIM, etc.), append their resource_id
# to this list to maintain observability coverage.
# ---------------------------------------------------------------------------
target_resource_ids = local.diagnostics_targets
workspace_id = module.log_analytics.workspace_id
name_prefix = "diag-${module.naming.prefix}"
}
# =============================================================================
# 7. GOVERNANCE -- policy baseline (ENTERPRISE TIER ADDITION)
# =============================================================================
# The policy baseline enforces organizational standards at the Azure resource
# level. It is the primary addition in the Enterprise tier over Foundation.
#
# Policies deployed:
# - Allowed locations : restricts where resources can be created
# - Require private endpoints: audits resources missing PE connections
# - Deny public PaaS : blocks public network access on key services
#
# The scope defaults to the core resource group but can be elevated to
# subscription or management group level via var.policy_scope.
module "policy_baseline" {
count = var.enable_policy_baseline ? 1 : 0 # Enterprise: true
source = "../../modules/governance/policy_baseline"
scope = var.policy_scope != null ? var.policy_scope : module.resource_groups.ids["core"]
allowed_locations = local.allowed_locations
require_private_endpoints = var.require_private_endpoints # default: true
deny_public_paas = var.deny_public_paas # default: true
}
# =============================================================================
# MODULES NOT USED IN ENTERPRISE TIER
# =============================================================================
#
# The following modules exist but are disabled at the Enterprise tier:
#
# compute/aks_private --> enable_aks = false (see regulated.tf)
# CMK key creation --> enable_cmk = false (see regulated.tf)
#
# =============================================================================
# FUTURE ENTERPRISE-TIER MODULES (not yet implemented)
# =============================================================================
#
# The following capabilities are planned for future Enterprise tier releases:
#
# alerts/
# Azure Monitor alert rules for AI service health, latency, and error
# rates. Will consume log_analytics.workspace_id and wire action groups
# to email/webhook/PagerDuty targets.
#
# identity/rbac
# Managed identity creation + scoped role assignments. Will assign
# roles like "Cognitive Services OpenAI User" and "Key Vault Secrets
# Officer" to workload identities, eliminating shared-key usage.
#
# security/defender
# Microsoft Defender for Cloud plans enablement. Will activate
# Defender for Key Vault, Defender for Storage, and Defender for
# Resource Manager at the subscription level.
#
# Each of these will follow the same composition pattern: a self-contained
# module under modules/<namespace>/<module_name> with inputs wired from
# this root stack.