Terraform Reference
This page is automatically generated from Terraform source files. Run
./docs/generate-tf-docs.sh to update.
Complete reference documentation for all Terraform modules, variables, and outputs extracted directly from the source code.
Initial Setup
Network foundation: VNet, Bastion, Jumpbox, self-hosted runners.
AI Services Hub
AI platform: APIM, AI Foundry, tenants, key rotation, WAF.
Initial Setup (initial-setup/infra/)
Network foundation layer — VNet, Bastion, Jumpbox, and self-hosted GitHub runners. Deployed once per environment via initial-setup/infra/deploy-terraform.sh.
Root Module
-------------
Location: initial-setup/infra/
Variables
| Name | Type | Default | Description |
|---|---|---|---|
app_env |
string |
required |
Application environment (dev, test, prod) |
app_name |
string |
required |
Name of the application |
common_tags |
map(string) |
required |
Common tags to apply to all resources |
location |
string |
Canada |
Azure region for resources |
resource_group_name |
string |
required |
Name of the resource group |
subscription_id |
string |
required |
Azure subscription ID |
tenant_id |
string |
required |
Azure tenant ID |
use_oidc |
bool |
true |
Use OIDC for authentication |
vnet_address_space |
string |
required |
Address space for the virtual network, it is created by platform team |
vnet_name |
string |
required |
Name of the existing virtual network |
vnet_resource_group_name |
string |
required |
Resource group name where the virtual network exists |
client_id |
string |
required |
Azure client ID for the service principal |
github_runners_aca_enabled |
bool |
false |
Enable GitHub self-hosted runners on Azure Container Apps |
github_organization |
string |
required |
GitHub organization name (e.g., 'bcgov') |
github_repository |
string |
required |
GitHub repository name (e.g., 'ai-hub-tracking') |
github_runner_pat |
string |
required |
GitHub Personal Access Token with Administration:write permission for runner registration |
github_runners_container_cpu |
string |
1 |
CPU cores for each runner container |
github_runners_container_memory |
string |
2Gi |
Memory for each runner container (e.g., '4Gi') |
github_runners_max_count |
number |
4 |
Maximum number of concurrent runners |
github_runners_log_analytics_workspace_creation_enabled |
bool |
true |
Whether the GitHub runners module should create a Log Analytics workspace |
github_runners_log_analytics_workspace_id |
string |
null |
Existing Log Analytics workspace ID to use when workspace creation is disabled |
dev_address_spaces |
list(string) |
[] |
Address space for the dev environment of the vnet peering |
prod_address_spaces |
list(string) |
[] |
Address space for the prod environment of the vnet peering |
test_address_spaces |
list(string) |
[] |
Address space for the test environment of the vnet peering |
azure_proxy_image |
string |
required |
The image for the Azure Proxy container |
app_service_sku_name_azure_proxy |
string |
P0v4 |
The SKU name for the azure proxy App Service plan. |
enable_azure_proxy |
bool |
false |
Enable deployment of the Azure Proxy App Service |
enable_bastion |
bool |
false |
Enable deployment of the Azure Bastion host |
enable_jumpbox |
bool |
false |
Enable deployment of the Azure Jumpbox VM |
log_analytics_retention_days |
number |
30 |
Number of days to retain data in Log Analytics Workspace |
log_analytics_sku |
string |
PerGB2018 |
SKU for Log Analytics Workspace |
Outputs
| Name | Description |
|---|---|
jumpbox_vm_id |
ID of the jumpbox virtual machine |
jumpbox_vm_name |
Name of the jumpbox virtual machine |
jumpbox_admin_username |
Admin username for SSH access to jumpbox |
jumpbox_auto_shutdown_time |
Auto-shutdown time (PST) |
jumpbox_auto_start_schedule |
Auto-start schedule (PST) |
bastion_resource_id |
Resource ID of Azure Bastion |
bastion_fqdn |
FQDN of the Bastion service |
github_runners_environment_name |
Name of the Container App Environment for GitHub runners |
github_runners_job_name |
Name of the Container App Job running the GitHub runner |
github_runners_label |
The label to use in workflow runs-on to target these runners |
github_runners_acr_name |
Name of the Azure Container Registry for runner images |
proxy_url |
URL of the Azure Proxy service |
proxy_auth |
Authentication info for the Azure Proxy service |
Resources Created
azurerm_resource_group (main)
azure-proxy
Location: initial-setup/infra/modules/azure-proxy/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
app_env |
string |
required |
The deployment environment (e.g., dev, test, prod). |
app_name |
string |
required |
The base name of the application. Used for naming Azure resources. |
app_service_subnet_id |
string |
required |
The subnet ID for the App Service. |
appinsights_connection_string |
string |
required |
The Application Insights connection string for monitoring. |
appinsights_instrumentation_key |
string |
required |
The Application Insights instrumentation key. |
common_tags |
map(string) |
{} |
A map of tags to apply to resources. |
container_registry_url |
string |
https://ghcr.io |
The URL of the container registry to pull images from. |
location |
string |
required |
The Azure region where resources will be created. |
log_analytics_workspace_id |
string |
required |
The resource ID of the Log Analytics workspace for diagnostics. |
repo_name |
string |
required |
The repository name, used for resource naming. |
resource_group_name |
string |
required |
The name of the resource group in which to create resources. |
azure_proxy_image |
string |
required |
The image for the Azure DB Proxy container |
app_service_sku_name_azure_proxy |
string |
required |
The SKU name for the azure db proxy App Service plan. |
Outputs
| Name | Description |
|---|---|
proxy_url |
The URL of the Azure Proxy App Service |
proxy_auth |
The authentication string for the Azure Proxy |
Resources Created
azurerm_service_plan (azure_proxy_asp)random_uuid (proxy_chisel_username)random_password (proxy_chisel_password)azurerm_linux_web_app (azure_proxy)azurerm_monitor_diagnostic_setting (azure_proxy_diagnostics)
bastion
-----------------------------------------------------------------------------
Location: initial-setup/infra/modules/bastion/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
app_name |
string |
required |
Name of the application, used for resource naming |
resource_group_name |
string |
required |
Name of the resource group |
location |
string |
required |
Azure region for resources |
bastion_subnet_id |
string |
required |
Subnet ID for Azure Bastion (must be named AzureBastionSubnet) |
common_tags |
map(string) |
required |
Common tags to apply to all resources |
bastion_sku |
string |
Basic |
SKU for Azure Bastion (Basic or Standard) |
Outputs
| Name | Description |
|---|---|
bastion_resource_id |
Resource ID of the Azure Bastion host |
bastion_fqdn |
FQDN of the Bastion host |
public_ip_address |
Public IP address of the Bastion host |
Resources Created
azurerm_public_ip (bastion)azurerm_bastion_host (main)
github-runners-aca
-----------------------------------------------------------------------------
Location: initial-setup/infra/modules/github-runners-aca/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
enabled |
bool |
true |
Enable or disable the GitHub runners module |
postfix |
string |
required |
A postfix used to build default names for resources (e.g., 'ai-hub') |
location |
string |
required |
Azure region where resources will be deployed |
resource_group_name |
string |
required |
Name of the existing resource group |
common_tags |
map(string) |
{} |
Common tags to apply to all resources |
github_organization |
string |
required |
GitHub organization name (e.g., 'bcgov') |
github_repository |
string |
required |
GitHub repository name (e.g., 'ai-hub-tracking') |
github_runner_pat |
string |
required |
GitHub Personal Access Token with Administration:write permission for runner registration |
vnet_id |
string |
required |
ID of the existing virtual network |
container_app_subnet_id |
string |
required |
ID of the subnet for Container Apps (must be delegated to Microsoft.App/environments) |
private_endpoint_subnet_id |
string |
required |
ID of the subnet for private endpoints (used for ACR) |
container_cpu |
string |
1 |
CPU cores for each runner container (e.g., 1, 2, 4) |
container_memory |
string |
2Gi |
Memory for each runner container (e.g., '4Gi') |
max_runners |
number |
4 |
Maximum number of concurrent runners |
log_analytics_workspace_creation_enabled |
bool |
true |
Whether the AVM runner module should create a Log Analytics workspace |
log_analytics_workspace_id |
string |
null |
Existing Log Analytics workspace ID to use when creation is disabled |
Outputs
| Name | Description |
|---|---|
container_app_environment_name |
Name of the Container App Environment |
container_app_environment_id |
Resource ID of the Container App Environment |
container_app_job_name |
Name of the Container App Job running the GitHub runner |
container_app_job_id |
Resource ID of the Container App Job |
container_registry_name |
Name of the Azure Container Registry |
container_registry_login_server |
Login server URL of the Azure Container Registry |
runner_label |
The label to use in workflow runs-on to target these runners (use just 'self-hosted' for AVM-based runners) |
jumpbox
-----------------------------------------------------------------------------
Location: initial-setup/infra/modules/jumpbox/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
app_name |
string |
required |
Name of the application, used for resource naming |
resource_group_name |
string |
required |
Name of the resource group |
location |
string |
required |
Azure region for resources |
subnet_id |
string |
required |
Subnet ID for the jumpbox VM |
common_tags |
map(string) |
required |
Common tags to apply to all resources |
vm_size |
string |
Standard_B2als_v2 |
Size of the virtual machine (B2als_v2: 2 vCPU, 4 GB RAM - cost-effective for jumpbox) |
os_disk_type |
string |
Standard_LRS |
Storage account type for the OS disk |
os_disk_size_gb |
number |
64 |
Size of the OS disk in GB |
Outputs
| Name | Description |
|---|---|
vm_id |
ID of the jumpbox virtual machine |
vm_name |
Name of the jumpbox virtual machine |
private_ip_address |
Private IP address of the jumpbox VM |
admin_username |
Admin username for SSH access |
principal_id |
Principal ID of the VM's managed identity |
ssh_public_key_id |
Resource ID of the SSH public key in Azure |
ssh_private_key_path |
Local path to the SSH private key file |
automation_account_id |
ID of the Azure Automation Account for VM scheduling |
auto_shutdown_time |
Auto-shutdown time (PST) |
auto_start_schedule |
Auto-start schedule |
Resources Created
azapi_resource_action (ssh_public_key_gen)azapi_resource (ssh_public_key)random_string (admin_username)azurerm_network_interface (jumpbox)azurerm_linux_virtual_machine (jumpbox)local_sensitive_file (ssh_private_key)azurerm_dev_test_global_vm_shutdown_schedule (jumpbox)azurerm_automation_account (jumpbox)azurerm_automation_runbook (start_vm)azurerm_automation_schedule (weekday_start)azurerm_automation_job_schedule (start_vm)azurerm_role_assignment (automation_vm_contributor)
monitoring
Log Analytics Workspace
Location: initial-setup/infra/modules/monitoring/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
app_name |
string |
required |
Name of the application |
common_tags |
map(string) |
required |
Common tags to apply to all resources |
location |
string |
Canada |
Azure region for resources |
log_analytics_retention_days |
number |
30 |
Number of days to retain data in Log Analytics Workspace |
log_analytics_sku |
string |
PerGB2018 |
SKU for Log Analytics Workspace |
Outputs
| Name | Description |
|---|---|
appinsights_connection_string |
The Application Insights connection string. |
appinsights_instrumentation_key |
The Application Insights instrumentation key. |
log_analytics_workspace_id |
The resource ID of the Log Analytics workspace. |
log_analytics_workspace_key |
The primary shared key for the Log Analytics workspace. |
log_analytics_workspace_workspaceId |
The name of the Log Analytics workspace. |
Resources Created
azurerm_log_analytics_workspace (main)azurerm_application_insights (main)
network
Location: initial-setup/infra/modules/network/
Files (5)
locals.tfmain.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
apps_service_subnet_name |
string |
app-service-subnet |
Name of the subnet for App Services |
common_tags |
map(string) |
required |
Common tags to apply to all resources |
container_instance_subnet_name |
string |
container-instance-subnet |
Name of the subnet for container instances |
container_apps_subnet_name |
string |
container-apps-subnet |
Name of the subnet for Container Apps |
location |
string |
Canada |
Azure region for resources |
private_endpoint_subnet_name |
string |
privateendpoints-subnet |
Name of the subnet for private endpoints |
resource_group_name |
string |
required |
Name of the resource group |
vnet_address_space |
string |
required |
Address space for the virtual network, it is created by platform team |
vnet_name |
string |
required |
Name of the existing virtual network |
vnet_resource_group_name |
string |
required |
Resource group name where the virtual network exists |
web_subnet_name |
string |
web-subnet |
Name of the web subnet for APIM deployment |
apim_subnet_name |
string |
apim-subnet |
Name of the subnet for API Management |
jumpbox_subnet_name |
string |
jumpbox-subnet |
Name of the subnet for Jumpbox VM |
bastion_subnet_name |
string |
AzureBastionSubnet |
Name of the subnet for Azure Bastion (must be AzureBastionSubnet) |
dev_address_spaces |
list(string) |
[] |
Address space for the dev environment of the vnet peering |
prod_address_spaces |
list(string) |
[] |
Address space for the prod environment of the vnet peering |
test_address_spaces |
list(string) |
[] |
Address space for the test environment of the vnet peering |
Outputs
| Name | Description |
|---|---|
container_instance_subnet_id |
The subnet ID for the container instance. |
app_service_subnet_id |
The subnet ID for the App Service. |
private_endpoint_subnet_id |
The subnet ID for private endpoints. |
container_apps_subnet_id |
The subnet ID for Container Apps Environment. |
apim_subnet_id |
The subnet ID for API Management. |
jumpbox_subnet_id |
The subnet ID for the Jumpbox VM. |
bastion_subnet_id |
The subnet ID for Azure Bastion. |
dns_servers |
The DNS servers for the virtual network. |
vnet_id |
ID of the virtual network |
vnet_name |
Name of the virtual network |
Resources Created
azurerm_network_security_group (privateendpoints)azurerm_network_security_group (app_service)azurerm_network_security_group (container_instance)azurerm_network_security_group (container_apps)azurerm_network_security_group (apim)azapi_resource (privateendpoints_subnet)azapi_resource (app_service_subnet)azapi_resource (container_instance_subnet)azapi_resource (container_apps_subnet)azapi_resource (apim_subnet)azurerm_network_security_group (jumpbox)azapi_resource (jumpbox_subnet)azurerm_network_security_group (bastion)azapi_resource (bastion_subnet)
AI Services Hub (infra-ai-hub/)
Multi-tenant AI platform deployed via 5 isolated Terraform stacks with parallel execution. See ADR-013 for architecture rationale.
Stacks
Each stack has its own state file and backend configuration. Deployed in dependency order: shared → tenant (parallel) → foundry + apim + tenant-user-mgmt (parallel).
shared
Location: infra-ai-hub/stacks/shared/
Files (7)
backend.tflocals.tfmain.tfmonitoring.tfoutputs.tfproviders.tfvariables.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
app_env |
string |
required |
Application environment |
app_name |
string |
required |
Application name prefix |
location |
string |
required |
Azure region |
resource_group_name |
string |
required |
Shared resource group name |
common_tags |
map(string) |
{} |
Common tags |
subscription_id |
string |
required |
Azure subscription ID |
tenant_id |
string |
required |
Azure tenant ID |
client_id |
string |
required |
Azure client ID |
use_oidc |
bool |
true |
Use OIDC |
vnet_name |
string |
required |
Landing zone VNet name |
vnet_resource_group_name |
string |
required |
Landing zone VNet resource group |
source_vnet_address_space |
string |
required |
Source VNet address space |
subnet_allocation |
map(map(string)) |
required |
Explicit subnet allocation map. Outer key = address space CIDR, inner key = subnet name, inner value = full subnet CIDR. |
external_peered_projects |
map(object({ |
{} |
Map of external project names to their peered VNet config for direct APIM access (bypasses App Gateway). Key = project name, value = { cidrs = [...], priority = 4xx }. |
shared_config |
any |
required |
Shared environment config |
defender_enabled |
bool |
false |
Enable Defender for Cloud plans |
defender_resource_types |
map(object({ |
{} |
Defender for Cloud resource types and subplans |
backend_resource_group |
string |
required |
Backend resource group (unused by shared stack logic, accepted for command consistency) |
backend_storage_account |
string |
required |
Backend storage account (unused by shared stack logic, accepted for command consistency) |
backend_container_name |
string |
tfstate |
Backend container name (unused by shared stack logic, accepted for command consistency) |
monitoring_webhook_url |
string |
required |
Teams Power Automate webhook URL for hub alert notifications. Optional — provide this and/or alert_emails in shared_config.monitoring. Store in sensitive tfvars, never in source-controlled params. |
Outputs
| Name | Description |
|---|---|
resource_group_name |
No description |
resource_group_id |
No description |
private_endpoint_subnet_id |
No description |
private_endpoint_subnet_cidr |
No description |
private_endpoint_subnet_cidrs |
Sorted list of all PE subnet CIDRs in the pool (covers primary + overflow PE subnets) |
private_endpoint_subnet_ids_by_key |
Map of PE pool key to subnet resource ID |
private_endpoint_subnet_cidrs_by_key |
Map of PE pool key to CIDR string |
private_endpoint_nsg_id |
No description |
apim_subnet_id |
No description |
appgw_subnet_id |
No description |
aca_subnet_id |
ACA subnet ID for Container App Environment |
container_app_environment_id |
Container App Environment resource ID (null if not enabled) |
container_app_environment_name |
Container App Environment name (null if not enabled) |
ai_foundry_hub_id |
No description |
ai_foundry_hub_name |
No description |
ai_foundry_hub_endpoint |
No description |
ai_foundry_hub_principal_id |
No description |
log_analytics_workspace_id |
No description |
application_insights_id |
No description |
application_insights_connection_string |
No description |
application_insights_instrumentation_key |
No description |
language_service_id |
No description |
language_service_endpoint |
No description |
hub_key_vault_id |
No description |
hub_key_vault_name |
No description |
hub_key_vault_uri |
No description |
dns_zone_public_ip_id |
No description |
dns_zone_public_ip_address |
No description |
waf_policy_id |
No description |
app_gateway_id |
No description |
app_gateway_name |
No description |
appgw_url |
App Gateway frontend URL (https:// |
hub_alerts_action_group_id |
Resource ID of the hub monitoring action group. Referenced by the APIM stack to attach APIM resource health alerts. |
Resources Created
azurerm_resource_group (main)azurerm_cognitive_account (language_service)azurerm_private_endpoint (language_service)terraform_data (language_service_dns_wait)terraform_data (hub_kv_dns_wait)
tenant
Location: infra-ai-hub/stacks/tenant/
Files (6)
backend.tflocals.tfmain.tfoutputs.tfproviders.tfvariables.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
app_env |
string |
required |
No description |
location |
string |
required |
No description |
common_tags |
map(string) |
{} |
No description |
subscription_id |
string |
required |
No description |
tenant_id |
string |
required |
No description |
client_id |
string |
required |
No description |
use_oidc |
bool |
true |
No description |
shared_config |
any |
required |
No description |
tenants |
map(any) |
{} |
No description |
tenant_tags |
map(map(string)) |
{} |
Per-tenant tags (up to 20 key/value pairs each). Kept separate from var.tenants to avoid HCL structural type unification errors when different tenants have different tag keys. |
backend_resource_group |
string |
required |
No description |
backend_storage_account |
string |
required |
No description |
backend_container_name |
string |
tfstate |
No description |
Outputs
| Name | Description |
|---|---|
tenant_resource_groups |
No description |
tenant_key_vaults |
No description |
tenant_storage_accounts |
No description |
tenant_ai_search |
No description |
tenant_cosmos_db |
No description |
tenant_document_intelligence |
No description |
tenant_speech_services |
No description |
tenant_log_analytics |
No description |
tenant_enabled_resources |
No description |
foundry
Location: infra-ai-hub/stacks/foundry/
Files (6)
backend.tflocals.tfmain.tfoutputs.tfproviders.tfvariables.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
app_env |
string |
required |
No description |
location |
string |
required |
No description |
common_tags |
map(string) |
{} |
No description |
subscription_id |
string |
required |
No description |
tenant_id |
string |
required |
No description |
client_id |
string |
required |
No description |
use_oidc |
bool |
true |
No description |
shared_config |
any |
required |
No description |
tenants |
map(any) |
{} |
No description |
tenant_tags |
map(map(string)) |
{} |
Per-tenant tags (up to 20 key/value pairs each). Kept separate from var.tenants to avoid HCL structural type unification errors when different tenants have different tag keys. |
backend_resource_group |
string |
required |
No description |
backend_storage_account |
string |
required |
No description |
backend_container_name |
string |
tfstate |
No description |
Outputs
| Name | Description |
|---|---|
tenant_projects |
No description |
tenant_ai_model_deployments |
No description |
apim
Location: infra-ai-hub/stacks/apim/
Files (7)
backend.tflocals.tfmain.tfmonitoring.tfoutputs.tfproviders.tfvariables.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
app_env |
string |
required |
No description |
app_name |
string |
required |
No description |
location |
string |
required |
No description |
common_tags |
map(string) |
{} |
No description |
subscription_id |
string |
required |
No description |
tenant_id |
string |
required |
No description |
client_id |
string |
required |
No description |
use_oidc |
bool |
true |
No description |
shared_config |
any |
required |
No description |
tenants |
map(any) |
{} |
No description |
defender_enabled |
bool |
false |
No description |
apim_pe_subnet_key |
string |
null |
Optional PE subnet pool key for APIM private endpoint. When set, uses the mapped PE subnet instead of primary. APIM is pinned (not auto-balanced). |
defender_resource_types |
map(object({ |
{} |
No description |
backend_resource_group |
string |
required |
No description |
backend_storage_account |
string |
required |
No description |
backend_container_name |
string |
tfstate |
No description |
Outputs
| Name | Description |
|---|---|
apim_gateway_url |
No description |
apim_id |
APIM resource ID (consumed by key-rotation stack for RBAC) |
apim_name |
No description |
apim_principal_id |
Principal ID of the APIM system-assigned managed identity |
apim_key_rotation_summary |
No description |
rotation_enabled_tenants |
Comma-separated list of tenant names with key rotation enabled (per-tenant opt-in) |
apim_tenant_subscriptions |
No description |
Resources Created
azurerm_api_management_named_value (ai_foundry_endpoint)azurerm_api_management_named_value (openai_endpoint)azurerm_api_management_named_value (docint_endpoint)azurerm_api_management_named_value (storage_endpoint)azurerm_api_management_named_value (ai_search_endpoint)azurerm_api_management_named_value (speech_services_endpoint)azurerm_api_management_named_value (pii_service_url)azurerm_api_management_backend (ai_foundry)azurerm_api_management_backend (openai)azurerm_api_management_backend (docint)azurerm_api_management_backend (storage)azurerm_api_management_backend (ai_search)azurerm_api_management_backend (speech_services_stt)azurerm_api_management_backend (speech_services_tts)azurerm_api_management_named_value (speech_services_key)azurerm_api_management_logger (app_insights)azurerm_api_management_diagnostic (app_insights)azurerm_api_management_logger (tenant_app_insights)azurerm_api_management_api_diagnostic (tenant)azurerm_api_management_policy_fragment (cognitive_services_auth)azurerm_api_management_policy_fragment (storage_auth)azurerm_api_management_policy_fragment (keyvault_auth)azurerm_api_management_policy_fragment (openai_usage_logging)azurerm_api_management_policy_fragment (openai_streaming_metrics)azurerm_api_management_policy_fragment (pii_anonymization)azurerm_api_management_policy_fragment (intelligent_routing)azurerm_api_management_policy_fragment (tracking_dimensions)azurerm_api_management_api_policy (tenant)azurerm_role_assignment (apim_openai_user)azurerm_role_assignment (apim_docint_user)azurerm_role_assignment (apim_storage_reader)azurerm_role_assignment (apim_search_contributor)azurerm_role_assignment (apim_search_service_contributor)azurerm_role_assignment (apim_speech_services_user)azurerm_role_assignment (apim_language_service_user)azurerm_role_assignment (apim_keyvault_secrets_user)azurerm_role_assignment (apim_pii_svc_reader)azurerm_api_management_subscription (tenant)azurerm_key_vault_secret (apim_subscription_primary_key)azurerm_key_vault_secret (apim_subscription_secondary_key)azurerm_key_vault_secret (apim_rotation_metadata)azurerm_api_management_api (landing_page)azurerm_api_management_api_operation (landing_page_get)azurerm_api_management_api_policy (landing_page)azurerm_api_management_api_operation (tenant_methods)azurerm_api_management_product_api (tenant)azapi_resource (defender_api_collection)
tenant-user-mgmt
Location: infra-ai-hub/stacks/tenant-user-mgmt/
Files (6)
backend.tflocals.tfmain.tfoutputs.tfproviders.tfvariables.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
app_env |
string |
required |
Application environment (dev, test, prod) |
subscription_id |
string |
required |
Azure subscription ID |
tenant_id |
string |
required |
Azure tenant ID |
client_id |
string |
required |
Azure client ID for the service principal (OIDC) |
use_oidc |
bool |
true |
Use OIDC for authentication |
tenants |
any |
{} |
Tenant configurations (reuses the same tfvars as the main config) |
backend_resource_group |
string |
required |
Backend resource group (accepted for shared deploy command contract) |
backend_storage_account |
string |
required |
Backend storage account (accepted for shared deploy command contract) |
backend_container_name |
string |
tfstate |
Backend container name (accepted for shared deploy command contract) |
Outputs
| Name | Description |
|---|---|
tenant_user_management |
Map of tenant names to their Entra group IDs and custom role definition IDs |
Modules
Reusable modules consumed by the stacks above. Each module wraps Azure Verified Modules (AVM) or native azurerm/azapi resources.
ai-foundry-hub
AI Foundry Hub Module
Location: infra-ai-hub/modules/ai-foundry-hub/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
name |
string |
required |
Name of the AI Foundry account |
resource_group_name |
string |
required |
Name of the resource group |
resource_group_id |
string |
required |
Resource ID of the resource group |
location |
string |
required |
Azure region for supporting resources (Log Analytics, App Insights, Private Endpoints) |
ai_location |
string |
null |
Azure region for AI Foundry Hub. Can differ from location for model availability (e.g., canadaeast for GPT-4.1). Defaults to location. |
sku |
string |
S0 |
SKU for the AI Foundry account |
public_network_access_enabled |
bool |
false |
Whether public network access is enabled |
local_auth_enabled |
bool |
false |
Whether local authentication (API keys) is enabled |
private_endpoint_subnet_id |
string |
required |
Subnet ID for private endpoints |
log_analytics |
object({ |
{ |
Log Analytics workspace configuration |
application_insights |
optional(string, |
{ |
Application Insights configuration for AI Foundry monitoring |
ai_agent |
object({ |
{ |
AI Agent service configuration with network injection support |
bing_grounding |
object({ |
{ |
Bing Web Search resource for grounding AI models |
private_endpoint_dns_wait |
object({ |
{} |
Configuration for waiting on policy-managed DNS zone groups |
subscription_id |
string |
required |
Azure subscription ID. |
scripts_dir |
string |
required |
Path to the scripts directory containing wait-for-dns-zone.sh |
purge_on_destroy |
bool |
false |
Whether to permanently purge the AI Foundry account on destroy (bypasses soft delete) |
purge_wait |
object({ |
{} |
Wait configuration for AI Foundry soft-delete before purge |
tags |
map(string) |
{} |
Tags to apply to resources |
Outputs
| Name | Description |
|---|---|
id |
Resource ID of the AI Foundry account |
name |
Name of the AI Foundry account |
ai_location |
Azure region where the AI Foundry Hub is deployed (may differ from PE location) |
endpoint |
Primary endpoint of the AI Foundry account |
principal_id |
Principal ID of the system-assigned managed identity |
tenant_id |
Tenant ID of the system-assigned managed identity |
private_endpoint_id |
Resource ID of the private endpoint |
private_endpoint_ip |
Private IP address of the private endpoint |
log_analytics_workspace_id |
Resource ID of the Log Analytics workspace (if created or provided) |
application_insights_id |
Resource ID of Application Insights (if enabled) |
application_insights_connection_string |
Connection string for Application Insights (if enabled) |
application_insights_instrumentation_key |
Instrumentation key for Application Insights (if enabled, deprecated - use connection_string) |
ai_agent_id |
Resource ID of the AI Agent service (if enabled) |
ai_agent_principal_id |
Principal ID of the AI Agent's managed identity |
bing_grounding_id |
Resource ID of the Bing Grounding resource (if enabled) |
bing_grounding_endpoint |
Endpoint of the Bing Grounding resource (if enabled) |
Resources Created
azurerm_log_analytics_workspace (this)azurerm_application_insights (this)azapi_resource (ai_foundry)azurerm_private_endpoint (ai_foundry)null_resource (wait_for_dns)azurerm_monitor_diagnostic_setting (ai_foundry)azapi_resource (ai_agent)azurerm_private_endpoint (ai_agent)null_resource (wait_for_dns_ai_agent)azurerm_cognitive_account (bing_grounding)null_resource (purge_ai_foundry)
apim
API Management Module (stv2)
Location: infra-ai-hub/modules/apim/
Files (5)
locals.tfmain.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
name |
string |
required |
Name of the API Management instance |
resource_group_name |
string |
required |
Name of the resource group |
location |
string |
required |
Azure region for resources |
sku_name |
string |
StandardV2_1 |
SKU name for APIM (stv2). StandardV2_1-10 for cost-effective, PremiumV2_1-30 for VNet injection and advanced features. |
publisher_name |
string |
required |
Publisher name for APIM |
publisher_email |
string |
required |
Publisher email for APIM |
public_network_access_enabled |
bool |
true |
Whether public network access is enabled for APIM. Set to false to restrict access to private endpoints only. |
enable_private_endpoint |
bool |
false |
Whether to create a private endpoint for APIM inbound access. Must be set explicitly to avoid plan-time evaluation issues. |
private_endpoint_subnet_id |
string |
null |
Subnet ID for APIM private endpoint (stv2). Required when enable_private_endpoint is true. |
private_dns_zone_ids |
list(string) |
[] |
List of private DNS zone IDs to link for APIM private endpoint |
enable_vnet_integration |
bool |
false |
Whether to enable VNet integration for outbound connectivity. Required when backend services have public network access disabled. |
vnet_integration_subnet_id |
string |
null |
Subnet ID for APIM VNet integration (outbound). Required when enable_vnet_integration is true. Subnet must have delegation to Microsoft.Web/hostingEnvironments. |
enable_diagnostics |
bool |
false |
Whether to enable diagnostic settings. Must be set explicitly to avoid plan-time evaluation issues. |
log_analytics_workspace_id |
string |
null |
Log Analytics Workspace ID for diagnostics. Required when enable_diagnostics is true. |
tenant_products |
map(object({ |
{} |
Map of tenant names to their product configurations |
tags |
map(string) |
{} |
Tags to apply to resources |
enable_telemetry |
bool |
false |
Enable AVM telemetry |
apis |
optional(string, |
{} |
Map of APIs to create in APIM |
subscriptions |
string |
{} |
Map of subscriptions to create |
named_values |
map(object({ |
{} |
Map of named values (properties) for APIM |
policy_fragments |
map(object({ |
{} |
Map of reusable policy fragments |
global_policy_xml |
string |
null |
XML content for the global API Management policy |
scripts_dir |
string |
required |
Path to the scripts directory containing wait-for-dns-zone.sh |
private_endpoint_dns_wait |
object({ |
{} |
Configuration for waiting on policy-managed DNS zone groups |
Outputs
| Name | Description |
|---|---|
id |
Resource ID of the API Management instance |
name |
Name of the API Management instance |
gateway_url |
Gateway URL of the API Management instance |
management_url |
Management URL of the API Management instance |
developer_portal_url |
Developer portal URL |
private_ip_addresses |
Private IP addresses of APIM (when VNet integrated) |
public_ip_addresses |
Public IP addresses of APIM |
product_ids |
Map of product names to their resource IDs |
principal_id |
Principal ID of the APIM managed identity |
private_endpoint_ip |
Private IP address of the APIM private endpoint |
Resources Created
azurerm_api_management_policy (global)null_resource (wait_for_dns_apim)
app-configuration
App Configuration Module
Location: infra-ai-hub/modules/app-configuration/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
name |
string |
required |
Name of the App Configuration store |
resource_group_name |
string |
required |
Name of the resource group |
location |
string |
required |
Azure region for resources |
sku |
string |
standard |
SKU for App Configuration (free or standard) |
public_network_access_enabled |
bool |
false |
Enable public network access |
local_auth_enabled |
bool |
false |
Enable local authentication (access keys) |
purge_protection_enabled |
bool |
true |
Enable purge protection |
soft_delete_retention_days |
number |
7 |
Soft delete retention in days (1-7) |
private_endpoint_subnet_id |
string |
null |
Subnet ID for private endpoint |
encryption |
object({ |
{ |
Customer-managed key encryption configuration |
replicas |
list(object({ |
[] |
Geo-replication configuration (Standard SKU only) |
feature_flags |
map(object({ |
{} |
Feature flags to create |
configuration_keys |
optional(string) |
{} |
Configuration key-value pairs |
log_analytics_workspace_id |
string |
null |
Log Analytics Workspace ID for diagnostics |
scripts_dir |
string |
required |
Path to shared scripts directory for DNS wait operations |
private_endpoint_dns_wait |
object({ |
{} |
Configuration for waiting on policy-managed DNS zone groups |
tags |
map(string) |
{} |
Tags to apply to resources |
Outputs
| Name | Description |
|---|---|
resource_id |
Resource ID of the App Configuration store |
name |
Name of the App Configuration store |
endpoint |
Endpoint of the App Configuration store |
primary_read_key |
Primary read key connection string |
principal_id |
Principal ID of the system-assigned managed identity |
private_endpoint_id |
Resource ID of the private endpoint |
Resources Created
azurerm_app_configuration (this)azurerm_private_endpoint (app_config)azurerm_app_configuration_feature (features)azurerm_app_configuration_key (keys)azurerm_monitor_diagnostic_setting (app_config)null_resource (wait_for_dns_appconfig)
app-gateway
Application Gateway Module
Location: infra-ai-hub/modules/app-gateway/
Files (5)
locals.tfmain.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
name |
string |
required |
Name of the Application Gateway |
resource_group_name |
string |
required |
Name of the resource group |
location |
string |
required |
Azure region for resources |
subnet_id |
string |
required |
Subnet ID for the Application Gateway |
sku |
object({ |
{ |
SKU configuration for App Gateway |
autoscale |
object({ |
null |
Autoscale configuration (if set, overrides sku.capacity) |
waf_enabled |
bool |
true |
Enable Web Application Firewall |
waf_mode |
string |
Prevention |
WAF mode: Detection or Prevention |
ssl_certificates |
map(object({ |
{} |
SSL certificates — from Key Vault (key_vault_secret_id) or direct PFX upload (data+password). Certificates can also be uploaded via Azure Portal; Terraform will ignore changes to ssl_certificate blocks. |
ssl_certificate_name |
string |
null |
Name of an SSL certificate already on the App Gateway (uploaded via CLI/portal). When set, enables HTTPS listener and HTTP→HTTPS redirect. Only set after the cert has been uploaded to the App Gateway. |
backend_apim |
object({ |
required |
APIM backend configuration. The FQDN resolves to the PE private IP via private DNS zone linked to the App GW VNet. |
frontend_hostname |
string |
required |
Frontend hostname for the listener |
enable_diagnostics |
bool |
false |
Whether to enable diagnostic settings (use static bool to avoid count unknown at plan time) |
log_analytics_workspace_id |
string |
null |
Log Analytics Workspace ID for diagnostics |
key_vault_id |
string |
null |
Key Vault ID for SSL certificate access |
public_ip_resource_id |
string |
null |
Resource ID of a pre-created static Public IP for the App Gateway. When set, App GW uses this existing PIP. When null, this module does not create or associate any Public IP, and you must ensure a suitable frontend configuration (for example, a private frontend or an externally managed PIP). Typically provided by the dns-zone module. |
tags |
map(string) |
{} |
Tags to apply to resources |
zones |
set(string) |
[1, |
Availability zones for App Gateway |
url_path_map_configurations |
map(object({ |
null |
URL path maps for path-based routing |
rewrite_rule_set |
map(object({ |
null |
Rewrite rule sets for header and URL manipulation |
waf_policy_id |
string |
null |
Resource ID of WAF policy to associate with App Gateway |
Outputs
| Name | Description |
|---|---|
id |
Resource ID of the Application Gateway |
name |
Name of the Application Gateway |
public_ip_address |
Public IP address of the Application Gateway. Returns null - get this from the dns_zone module output instead (dns_zone_public_ip) to avoid data source lookup failures during first deployment. |
public_ip_id |
Public IP resource ID |
backend_address_pools |
Backend address pools |
principal_id |
Principal ID of the App Gateway managed identity (user-assigned) |
Resources Created
azurerm_user_assigned_identity (appgw)azurerm_role_assignment (appgw_to_keyvault)azurerm_application_gateway (this)azurerm_monitor_diagnostic_setting (appgw)null_resource (diag_destination_type_trigger)
container-app-environment
Container App Environment Module
Location: infra-ai-hub/modules/container-app-environment/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
name |
string |
required |
Name of the Container App Environment |
resource_group_name |
string |
required |
Name of the resource group |
location |
string |
required |
Azure region for resources |
infrastructure_subnet_id |
string |
required |
Subnet ID for the Container App Environment infrastructure |
internal_load_balancer_enabled |
bool |
true |
Use internal load balancer (private only access) |
zone_redundancy_enabled |
bool |
false |
Enable zone redundancy. Requires /23+ subnet. Set to false for /27 consumption-only. |
mtls_enabled |
bool |
true |
Enable mTLS peer authentication between apps |
workload_profiles |
string |
[] |
Workload profiles for dedicated compute (Consumption-only if empty) |
log_analytics_workspace_id |
string |
null |
Log Analytics Workspace ID for diagnostics |
enable_diagnostics |
bool |
false |
Enable diagnostic settings. Use this instead of null-checking log_analytics_workspace_id to avoid count depending on resource attributes during destroy. |
tags |
map(string) |
{} |
Tags to apply to resources |
private_endpoint_subnet_id |
string |
required |
Subnet ID for Container App Environment private endpoint. Required when enable_private_endpoint is true. |
scripts_dir |
string |
required |
Path to shared scripts directory for DNS wait operations |
private_endpoint_dns_wait |
object({ |
{} |
Configuration for waiting on policy-managed DNS zone groups |
Outputs
| Name | Description |
|---|---|
resource_id |
Resource ID of the Container App Environment |
name |
Name of the Container App Environment |
default_domain |
Default domain of the Container App Environment |
static_ip_address |
Static IP address of the Container App Environment |
Resources Created
azurerm_container_app_environment (main)azurerm_private_endpoint (containerapps)null_resource (wait_for_dns_cae)azurerm_monitor_diagnostic_setting (cae)
container-registry
Container Registry Module
Location: infra-ai-hub/modules/container-registry/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
name |
string |
required |
Name of the Container Registry |
resource_group_name |
string |
required |
Name of the resource group |
location |
string |
required |
Azure region for resources |
sku |
string |
Premium |
SKU for Container Registry. Premium required for private endpoints and geo-replication. |
public_network_access_enabled |
bool |
true |
Enable public network access. Set to true for public ACR, false for private-only. |
private_endpoint_subnet_id |
string |
null |
Subnet ID for private endpoints. Required if public_network_access_enabled is false. |
admin_enabled |
bool |
false |
Enable admin user for the registry |
quarantine_policy_enabled |
bool |
false |
Enable quarantine policy for images |
data_endpoint_enabled |
bool |
false |
Enable dedicated data endpoints for each region |
export_policy_enabled |
bool |
true |
Enable export policy (allows images to be exported) |
zone_redundancy_enabled |
bool |
true |
Enable zone redundancy (Premium only) |
enable_trust_policy |
bool |
false |
Enable content trust / image signing (Premium only) |
retention_policy_days |
number |
7 |
Days to retain untagged manifests (0 = disabled) |
georeplications |
map(object({ |
{} |
Geo-replication configuration for Premium SKU |
log_analytics_workspace_id |
string |
null |
Log Analytics Workspace ID for diagnostics |
scripts_dir |
string |
required |
Path to shared scripts directory for DNS wait operations |
private_endpoint_dns_wait |
object({ |
{} |
Configuration for waiting on policy-managed DNS zone groups |
resource_group_name_for_dns_wait |
string |
null |
Resource group name to use for DNS wait operations (where PE is created) |
tags |
map(string) |
{} |
Tags to apply to resources |
enable_telemetry |
bool |
false |
Enable AVM telemetry |
Outputs
| Name | Description |
|---|---|
resource_id |
Resource ID of the Container Registry |
name |
Name of the Container Registry |
login_server |
Login server URL of the Container Registry |
principal_id |
Principal ID of the system-assigned managed identity |
private_endpoint_id |
Resource ID of the private endpoint |
Resources Created
null_resource (wait_for_dns_acr)
defender
Microsoft Defender for Cloud Subscription Pricing
Location: infra-ai-hub/modules/defender/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
resource_types |
map(object({ |
{} |
Map of Defender for Cloud resource types to enable with their configuration. Keys are resource type names (e.g., 'Api', 'StorageAccounts'), values are objects with subplan. |
Outputs
| Name | Description |
|---|---|
enabled_resource_types |
List of Defender for Cloud resource types that are enabled |
pricing_ids |
Map of resource type to pricing resource ID |
Resources Created
azurerm_security_center_subscription_pricing (this)
dns-zone
=============================================================================
Location: infra-ai-hub/modules/dns-zone/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
name_prefix |
string |
required |
Prefix for resource names (e.g., ai-hub-test) |
location |
string |
required |
Azure region for resources |
dns_zone_name |
string |
required |
DNS zone name (e.g., test.aihub.gov.bc.ca) |
resource_group_name |
string |
required |
Name of the DNS resource group |
a_record_ttl |
number |
3600 |
TTL for the A record in seconds |
tags |
map(string) |
{} |
Tags to apply to resources |
ddos_protection_enabled |
bool |
false |
Enable DDoS IP Protection on the App Gateway public IP. Provides per-IP adaptive L3/L4 DDoS mitigation, telemetry, and alerting. Cost: ~$199 USD/month per IP. |
log_analytics_workspace_id |
string |
required |
Log Analytics workspace ID for diagnostic settings on the public IP and DNS zone |
Outputs
| Name | Description |
|---|---|
dns_zone_id |
Resource ID of the DNS zone |
dns_zone_name |
Name of the DNS zone |
name_servers |
Name servers for the DNS zone (provide these to your DNS registrar for delegation) |
public_ip_id |
Resource ID of the static public IP (pass to App Gateway module) |
public_ip_address |
IP address of the static public IP |
resource_group_name |
Name of the DNS resource group |
resource_group_id |
Resource ID of the DNS resource group |
Resources Created
azurerm_resource_group (dns)azurerm_dns_zone (this)azurerm_public_ip (appgw)azurerm_dns_a_record (apex)azurerm_monitor_diagnostic_setting (pip)
foundry-project
=============================================================================
Location: infra-ai-hub/modules/foundry-project/
Files (5)
locals.tfmain.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
tenant_name |
string |
required |
Name of the tenant (used for resource naming) |
location |
string |
required |
Azure region for the project |
ai_location |
string |
required |
Azure region for AI resources (can differ from project location for model availability) |
ai_foundry_hub_id |
string |
required |
Resource ID of the parent AI Foundry hub |
tags |
map(string) |
{} |
Tags to apply to resources |
key_vault |
object({ |
{ |
Key Vault configuration from tenant module |
storage_account |
object({ |
{ |
Storage account configuration from tenant module |
ai_search |
object({ |
{ |
AI Search configuration from tenant module |
cosmos_db |
object({ |
{ |
Cosmos DB configuration from tenant module |
document_intelligence |
object({ |
{ |
Document Intelligence configuration from tenant module |
ai_model_deployments |
string |
{} |
AI model deployments to create on the shared AI Foundry Hub (prefixed with tenant name) |
project_connections |
object({ |
{} |
Toggle which project connections to create |
Outputs
| Name | Description |
|---|---|
project_id |
Resource ID of the AI Foundry project |
project_name |
Name of the AI Foundry project |
project_principal_id |
Principal ID of the AI Foundry project's managed identity |
ai_model_deployment_ids |
Map of original model names to their resource IDs (deployment names are tenant-prefixed) |
ai_model_deployment_names |
List of deployed AI model names (tenant-prefixed, e.g., 'wlrs-gpt-4.1-mini') |
ai_model_deployment_mapping |
Map of client-facing model names to tenant-prefixed deployment names |
has_model_deployments |
Whether this tenant has any AI model deployments |
complete |
Marker indicating all project resources are complete (for serialization) |
Resources Created
azapi_resource (project)azurerm_role_assignment (project_to_keyvault)azurerm_role_assignment (project_to_storage)azurerm_role_assignment (project_to_search)azurerm_role_assignment (project_to_cosmos)azurerm_role_assignment (project_to_docint)azapi_resource (connection_storage)azapi_resource (connection_ai_search)azapi_resource (connection_cosmos)azapi_resource (rai_policy)azapi_resource (ai_model_deployment)azapi_resource (connection_docint)
key-rotation-function
=============================================================================
Location: infra-ai-hub/modules/key-rotation-function/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
name_prefix |
string |
required |
Naming prefix (e.g., ai-services-hub-dev) |
resource_group_name |
string |
required |
Resource group for all resources |
resource_group_id |
string |
required |
Resource group ID (for RBAC scoping) |
location |
string |
required |
Azure region |
tags |
map(string) |
{} |
Resource tags |
container_app_environment_id |
string |
required |
Container App Environment resource ID |
container_registry_url |
string |
ghcr.io |
Container registry URL (e.g., ghcr.io) |
container_image_name |
string |
required |
Container image name (e.g., bcgov/ai-hub-tracking/apim-key-rotation) |
container_image_tag |
string |
latest |
Container image tag |
cpu |
number |
0.5 |
CPU cores allocated to the container (e.g., 0.5) |
memory |
string |
1Gi |
Memory allocated to the container (e.g., 1Gi) |
cron_expression |
string |
0 |
Cron expression for the job schedule (e.g., 0 9 * * * for daily 09:00 UTC) |
replica_timeout_seconds |
number |
1800 |
Maximum seconds a replica can run before being terminated |
key_propagation_wait_seconds |
number |
10 |
Seconds to pause after APIM key regeneration for propagation (per tenant) |
replica_retry_limit |
number |
1 |
Maximum number of retries for a failed replica |
apim_id |
string |
required |
APIM resource ID (for RBAC) |
apim_name |
string |
required |
APIM instance name (passed to job as env var) |
hub_keyvault_id |
string |
required |
Hub Key Vault resource ID (for RBAC) |
hub_keyvault_name |
string |
required |
Hub Key Vault name (passed to job as env var) |
environment |
string |
required |
Target environment (dev, test, prod) |
app_name |
string |
required |
Application name prefix |
subscription_id |
string |
required |
Azure subscription ID |
rotation_enabled |
bool |
true |
Master rotation toggle |
rotation_interval_days |
number |
7 |
Days between rotations (must be < 90) |
dry_run |
bool |
false |
When true, logs what would happen without making changes |
included_tenants |
string |
required |
Comma-separated list of tenant names to include in rotation (empty = no tenants — safe default) |
secret_expiry_days |
number |
90 |
Key Vault secret expiry in days (Landing Zone max: 90) |
Outputs
| Name | Description |
|---|---|
job_id |
Resource ID of the key rotation Container App Job |
job_name |
Name of the key rotation Container App Job |
principal_id |
System-assigned Managed Identity principal ID |
Resources Created
terraform_data (image_refresh)azurerm_container_app_job (rotation)azurerm_role_assignment (job_kv_secrets_officer)azurerm_role_assignment (job_apim_contributor)azurerm_role_assignment (job_rg_reader)
network
Network Module - Main Configuration
Location: infra-ai-hub/modules/network/
Files (6)
data.tflocals.tfmain.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
name_prefix |
string |
required |
Prefix used for naming resources (e.g., ai-hub-dev) |
location |
string |
required |
Azure region for resources |
common_tags |
map(string) |
{} |
Common tags to apply to all resources |
vnet_name |
string |
required |
Name of the existing virtual network (target environment) |
vnet_resource_group_name |
string |
required |
Resource group name where the virtual network exists (target environment) |
source_vnet_address_space |
string |
required |
Address space of the source environment VNet (single CIDR string), used for NSG allow rules (e.g., tools VNet CIDR). |
subnet_allocation |
map(map(string)) |
required |
No description |
external_peered_projects |
map(object({ |
{} |
No description |
Outputs
| Name | Description |
|---|---|
private_endpoint_subnet_id |
Resource ID of the primary private endpoint subnet (first in pool) |
private_endpoint_subnet_cidr |
CIDR of the primary private endpoint subnet |
private_endpoint_nsg_id |
Resource ID of the private endpoint NSG |
private_endpoint_subnet_pool |
Map of all PE subnets available for tenant allocation (name => cidr) |
private_endpoint_subnet_ids_by_key |
Map of PE subnet pool key to resource ID. |
private_endpoint_subnet_cidrs_by_key |
Map of PE subnet pool key to CIDR string. |
private_endpoint_subnet_keys_ordered |
Ordered list of PE subnet pool keys for deterministic iteration. |
apim_subnet_id |
Resource ID of the APIM subnet (null if not enabled) |
apim_subnet_cidr |
CIDR of the APIM subnet (null if not enabled) |
apim_nsg_id |
Resource ID of the APIM NSG (null if not enabled) |
appgw_subnet_id |
Resource ID of the App Gateway subnet (null if not enabled) |
appgw_subnet_cidr |
CIDR of the App Gateway subnet (null if not enabled) |
appgw_nsg_id |
Resource ID of the App Gateway NSG (null if not enabled) |
aca_subnet_id |
Resource ID of the ACA subnet (null if not enabled) |
aca_subnet_cidr |
CIDR of the ACA subnet (null if not enabled) |
aca_nsg_id |
Resource ID of the ACA NSG (null if not enabled) |
vnet_id |
Resource ID of the target VNet |
vnet_name |
Name of the target VNet |
Resources Created
azurerm_network_security_group (private_endpoints)azapi_resource (pe_subnets)azurerm_network_security_group (apim)azapi_resource (apim_subnet)azurerm_network_security_group (appgw)azurerm_route_table (appgw)azurerm_route (appgw_internet)azurerm_route (appgw_bcgov_internal)azapi_resource (appgw_subnet)azurerm_network_security_group (aca)azapi_resource (aca_subnet)
pii-redaction-service
=============================================================================
Location: infra-ai-hub/modules/pii-redaction-service/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
name_prefix |
string |
required |
Prefix for all resource names (e.g. 'aihub-dev'). |
resource_group_name |
string |
required |
Name of the resource group where the Container App is deployed. |
container_app_environment_id |
string |
required |
Resource ID of the shared Container App Environment. |
container_registry_url |
string |
ghcr.io |
Container registry URL (without trailing slash). |
container_image_name |
string |
required |
Full image name within the registry (e.g. 'bcgov/ai-hub-tracking/jobs/pii-redaction-service'). |
container_image_tag |
string |
latest |
Container image tag. Use 'latest' for rolling deployments or a semver tag for pinned releases. |
cpu |
number |
0.25 |
vCPU allocation per replica. |
memory |
string |
512Mi |
Memory allocation per replica (e.g. '1Gi'). |
min_replicas |
number |
1 |
Minimum number of Container App replicas. |
max_replicas |
number |
5 |
Maximum number of Container App replicas. |
language_endpoint |
string |
required |
Azure Language Service endpoint URL (PII_LANGUAGE_ENDPOINT). |
language_service_id |
string |
required |
Resource ID of the Language Service — used for the Cognitive Services User RBAC assignment. |
language_api_version |
string |
required |
Language API version string (PII_LANGUAGE_API_VERSION). |
per_batch_timeout_seconds |
number |
10 |
Timeout in seconds for each Language API batch call (PII_PER_BATCH_TIMEOUT_SECONDS). |
total_processing_timeout_seconds |
number |
85 |
Overall deadline in seconds for processing all batches (PII_TOTAL_PROCESSING_TIMEOUT_SECONDS). |
transient_retry_attempts |
number |
4 |
Maximum retries for transient 429 and 5xx Language API responses after the initial request. |
retry_backoff_base_seconds |
number |
1 |
Base exponential backoff delay in seconds when Retry-After is absent. |
retry_backoff_max_seconds |
number |
10 |
Maximum exponential backoff delay in seconds when Retry-After is absent. |
max_concurrent_batches |
number |
15 |
Maximum Language API batches per request. Requests requiring more batches are rejected with HTTP 413. |
max_batch_concurrency |
number |
3 |
Number of Language API batches allowed in flight simultaneously (semaphore bound). |
max_doc_chars |
number |
5000 |
Maximum characters per Language API document before word-boundary chunking (PII_MAX_DOC_CHARS). |
max_docs_per_call |
number |
5 |
Maximum documents per Language API call (PII_MAX_DOCS_PER_CALL). |
log_level |
string |
INFO |
Application log level (PII_LOG_LEVEL). One of: DEBUG, INFO, WARNING, ERROR. |
tags |
map(string) |
{} |
Resource tags. |
Outputs
| Name | Description |
|---|---|
container_app_id |
Resource ID of the PII Redaction Container App. |
container_app_fqdn |
Internal FQDN of the PII Redaction Container App (accessible within the VNet via the Container App Environment). |
principal_id |
Object ID of the Container App's system-assigned managed identity. |
Resources Created
terraform_data (image_refresh)azurerm_container_app (service)azurerm_role_assignment (svc_language_cognitive_user)
tenant-user-management
=============================================================================
Location: infra-ai-hub/modules/tenant-user-management/
Files (6)
data.tflocals.tfmain.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
tenant_name |
string |
required |
Unique identifier for the tenant (used in group/role names) |
display_name |
string |
required |
Human-readable display name for the tenant |
app_env |
string |
required |
Application environment (dev, test, prod) — included in group/role names to prevent cross-environment conflicts |
resource_group_id |
string |
required |
Resource group ID for tenant scope role assignments |
user_management |
object({ |
{} |
Tenant user management configuration (Entra groups + custom roles) |
Outputs
| Name | Description |
|---|---|
group_object_ids |
Object IDs for tenant role groups (empty when create_groups = false) |
role_definition_ids |
Role definition IDs for tenant custom roles |
mode |
Assignment mode: 'group' when create_groups=true, 'direct_user' otherwise |
direct_user_assignments |
Map of direct user role assignments (empty when create_groups = true) |
Resources Created
azuread_group (tenant)azuread_group_member (seed_members)azurerm_role_definition (tenant_admin)azurerm_role_definition (tenant_write)azurerm_role_definition (tenant_read)azurerm_role_assignment (tenant_groups)azurerm_role_assignment (direct_users)
tenant
Tenant Module - Main Configuration
Location: infra-ai-hub/modules/tenant/
Files (6)
data.tflocals.tfmain.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
tenant_name |
string |
required |
Unique identifier for the tenant (used in resource names) |
display_name |
string |
required |
Human-readable display name for the tenant |
location |
string |
required |
Azure region for resources |
ai_location |
string |
null |
Azure region for AI services (OpenAI, Document Intelligence). Can differ from location for model availability. |
resource_group_name_override |
string |
null |
Optional custom name for the tenant resource group. If not provided, defaults to '{tenant_name}-rg' |
private_endpoint_subnet_id |
string |
required |
Subnet ID for private endpoints |
log_analytics_workspace_id |
string |
null |
Resource ID of Log Analytics workspace for diagnostics (legacy/shared) |
log_analytics |
object({ |
{ |
Per-tenant Log Analytics workspace configuration |
private_endpoint_dns_wait |
object({ |
{} |
Configuration for waiting on policy-managed DNS zone groups |
scripts_dir |
string |
required |
Path to shared scripts directory for DNS wait operations |
key_vault |
object({ |
{ |
Key Vault configuration for the tenant |
storage_account |
optional(string, |
{ |
Storage Account configuration for the tenant |
ai_search |
object({ |
{ |
Azure AI Search configuration for the tenant |
cosmos_db |
optional(string, |
{ |
Cosmos DB configuration for the tenant |
document_intelligence |
object({ |
{ |
Document Intelligence configuration for the tenant |
speech_services |
object({ |
{ |
Azure Speech Services configuration for the tenant |
ai_foundry_hub_id |
string |
null |
Resource ID of the shared AI Foundry Hub (for reference in outputs) |
tags |
map(string) |
{} |
Tags to apply to tenant resources |
Outputs
| Name | Description |
|---|---|
resource_group_name |
Name of the tenant's resource group |
resource_group_id |
ID of the tenant's resource group |
key_vault_id |
Resource ID of the Key Vault |
key_vault_name |
Name of the Key Vault |
key_vault_uri |
URI of the Key Vault |
storage_account_id |
Resource ID of the Storage Account |
storage_account_name |
Name of the Storage Account |
storage_account_primary_blob_endpoint |
Primary blob endpoint of the Storage Account |
storage_account_primary_access_key |
Primary access key of the Storage Account |
storage_account_primary_connection_string |
Primary connection string of the Storage Account |
ai_search_id |
Resource ID of the AI Search service |
ai_search_name |
Name of the AI Search service |
ai_search_endpoint |
Endpoint URL of the AI Search service |
cosmos_db_id |
Resource ID of the Cosmos DB account |
cosmos_db_name |
Name of the Cosmos DB account |
cosmos_db_account_name |
Account name of the Cosmos DB (alias for cosmos_db_name, used for role assignments) |
cosmos_db_resource_group_name |
Resource group name containing the Cosmos DB account |
cosmos_db_endpoint |
Endpoint of the Cosmos DB account |
cosmos_db_database_name |
Name of the pre-created Cosmos DB SQL database |
document_intelligence_id |
Resource ID of the Document Intelligence account |
document_intelligence_name |
Name of the Document Intelligence account |
document_intelligence_endpoint |
Endpoint of the Document Intelligence account |
speech_services_id |
Resource ID of the Speech Services account |
speech_services_name |
Name of the Speech Services account |
speech_services_endpoint |
Endpoint of the Speech Services account |
speech_services_primary_key |
Primary access key for the Speech Services account |
openai_endpoint |
Endpoint for OpenAI-compatible API calls (shared AI Foundry Hub) |
openai_id |
Resource ID for AI models (points to AI Foundry Hub) |
log_analytics_workspace_id |
Resource ID of the tenant's Log Analytics workspace (if enabled) |
has_log_analytics |
Whether the tenant has a Log Analytics workspace (own or shared) |
log_analytics_enabled |
Whether the tenant has its own dedicated Log Analytics workspace |
application_insights_id |
Resource ID of the tenant's Application Insights (if tenant LAW enabled) |
application_insights_instrumentation_key |
Instrumentation key for the tenant's Application Insights |
application_insights_connection_string |
Connection string for the tenant's Application Insights |
enabled_resources |
Summary of which resources are enabled for this tenant |
Resources Created
azurerm_resource_group (tenant)random_string (suffix)azurerm_log_analytics_workspace (tenant)azurerm_application_insights (tenant)azurerm_monitor_diagnostic_setting (key_vault)azurerm_storage_account (this)azurerm_storage_container (default)azurerm_monitor_diagnostic_setting (storage)azurerm_monitor_diagnostic_setting (ai_search)azurerm_cosmosdb_account (this)azurerm_cosmosdb_sql_database (default)azurerm_cosmosdb_sql_container (default)azurerm_monitor_diagnostic_setting (cosmos)azurerm_private_endpoint (cosmos_db)azurerm_monitor_diagnostic_setting (document_intelligence)azurerm_monitor_diagnostic_setting (speech_services)null_resource (wait_for_dns_key_vault)null_resource (wait_for_dns_ai_search)null_resource (wait_for_dns_cosmos_db)null_resource (wait_for_dns_document_intelligence)null_resource (wait_for_dns_speech_services)
waf-policy
WAF Policy Module
Location: infra-ai-hub/modules/waf-policy/
Files (4)
main.tfoutputs.tfvariables.tfversions.tf
Variables
| Name | Type | Default | Description |
|---|---|---|---|
name |
string |
required |
Name of the WAF policy |
resource_group_name |
string |
required |
Name of the resource group |
location |
string |
required |
Azure region for resources |
enabled |
bool |
true |
Enable the WAF policy |
mode |
string |
Prevention |
WAF mode: Detection or Prevention |
request_body_check |
bool |
true |
Enable request body inspection |
request_body_enforcement |
bool |
true |
Enforce max request body size limit. When false, WAF inspects up to inspect_limit but does not reject oversized requests. Requires OWASP CRS 3.2+ |
request_body_inspect_limit_in_kb |
number |
128 |
How deep into a request body the WAF inspects and applies rules (KB). Only used with CRS 3.2+ |
max_request_body_size_kb |
number |
128 |
Maximum request body size in KB (8-2000) |
file_upload_limit_mb |
number |
100 |
Maximum file upload size in MB |
managed_rule_sets |
"Microsoft_BotManagerRuleSet" |
[ |
Managed rule sets to apply |
exclusions |
list(object({ |
[] |
Rule exclusions for false positive handling |
custom_rules |
"RateLimitRule") |
[] |
Custom WAF rules (MatchRule or RateLimitRule) |
tags |
map(string) |
{} |
Tags to apply to resources |
Outputs
| Name | Description |
|---|---|
resource_id |
Resource ID of the WAF policy |
name |
Name of the WAF policy |
http_listener_ids |
Associated HTTP listener IDs |
path_based_rule_ids |
Associated path-based rule IDs |
Resources Created
azurerm_web_application_firewall_policy (this)
Adding New Modules
When creating new Terraform modules, follow these conventions to ensure automatic documentation:
- Add a description comment at the top of
main.tf:# Description: This module creates Azure storage accounts with encryption
- Always include
descriptionfor variables:variable "storage_name" { type = string description = "Name of the storage account (must be globally unique)" } - Always include
descriptionfor outputs:output "storage_id" { value = azurerm_storage_account.main.id description = "The resource ID of the storage account" } - Run the documentation generator:
./docs/generate-tf-docs.sh