From aa4864e2028e6fb6ca0d8443991a286a73724005 Mon Sep 17 00:00:00 2001 From: Mosh Date: Sun, 10 May 2026 16:09:27 +0100 Subject: [PATCH] The implementation of Infra resources and the chains --- terraform/bindings/main.tf | 148 ++++++++++++++------- terraform/compute/main.tf | 177 ++++++++++++++----------- terraform/compute/outputs.tf | 45 +++---- terraform/foundation/main.tf | 223 +++++++++++++++++++++----------- terraform/foundation/outputs.tf | 177 ++++++++++++------------- 5 files changed, 452 insertions(+), 318 deletions(-) diff --git a/terraform/bindings/main.tf b/terraform/bindings/main.tf index b202325..ff55508 100644 --- a/terraform/bindings/main.tf +++ b/terraform/bindings/main.tf @@ -43,10 +43,19 @@ data "terraform_remote_state" "connectivity" { # ── Locals ──────────────────────────────────────────────────────────────────── locals { - rg_name = data.terraform_remote_state.connectivity.outputs.rg_taskflow_name - snet_compute_id = data.terraform_remote_state.connectivity.outputs.snet_compute_id - oidc_issuer_url = data.terraform_remote_state.compute.outputs.oidc_issuer_url - kubelet_identity = data.terraform_remote_state.compute.outputs.kubelet_identity_object_id + rg_name = data.terraform_remote_state.connectivity.outputs.rg_taskflow_name + snet_compute_id = data.terraform_remote_state.connectivity.outputs.snet_compute_id + oidc_issuer_url = data.terraform_remote_state.compute.outputs.oidc_issuer_url + kubelet_identity = data.terraform_remote_state.compute.outputs.kubelet_identity_object_id + acr_id = data.terraform_remote_state.foundation.outputs.acr_id + key_vault_id = data.terraform_remote_state.foundation.outputs.key_vault_id + api_service_principal_id = data.terraform_remote_state.foundation.outputs.mi_api_service_principal_id + processor_service_principal_id = data.terraform_remote_state.foundation.outputs.mi_processor_service_principal_id + notification_service_principal_id = data.terraform_remote_state.foundation.outputs.mi_notification_service_principal_id + mi_api_service_id = data.terraform_remote_state.foundation.outputs.mi_api_service_id + mi_processor_service_id = data.terraform_remote_state.foundation.outputs.mi_processor_service_id + mi_notification_service_id = data.terraform_remote_state.foundation.outputs.mi_notification_service_id + service_bus_namespace_id = data.terraform_remote_state.foundation.outputs.service_bus_namespace_id common_tags = { environment = var.environment @@ -61,10 +70,11 @@ locals { # The kubelet identity is created by AKS at cluster provisioning time — # this is why the assignment cannot be in the foundation tier. # -# TODO: implement azurerm_role_assignment.acr_pull -# scope = data.terraform_remote_state.foundation.outputs.acr_id -# role_definition_name = "AcrPull" -# principal_id = local.kubelet_identity +resource "azurerm_role_assignment" "acr_pull" { + scope = local.acr_id + role_definition_name = "AcrPull" + principal_id = local.kubelet_identity +} # ── Federated Identity Credentials ─────────────────────────────────────────── @@ -80,37 +90,50 @@ locals { # 4. Azure AD returns an access token scoped to the managed identity. # 5. Pod uses that token to authenticate to Key Vault / Service Bus. -# TODO: implement azurerm_federated_identity_credential.api_service -# name = "fed-taskflow-api-service" -# resource_group_name = local.rg_name -# audience = ["api://AzureADTokenExchange"] -# issuer = local.oidc_issuer_url -# parent_id = data.terraform_remote_state.foundation.outputs.mi_api_service_id -# subject = "system:serviceaccount:${var.kubernetes_namespace}:api-service" +resource "azurerm_federated_identity_credential" "api_service" { + name = "fed-taskflow-api-service" + audience = ["api://AzureADTokenExchange"] + issuer = local.oidc_issuer_url + subject = "system:serviceaccount:taskflow:api-service" +} -# TODO: implement azurerm_federated_identity_credential.processor_service -# subject = "system:serviceaccount:${var.kubernetes_namespace}:processor-service" +resource "azurerm_federated_identity_credential" "processor_service" { + name = "fed-taskflow-processor-service" + audience = ["api://AzureADTokenExchange"] + issuer = local.oidc_issuer_url + subject = "system:serviceaccount:taskflow:processor-service" +} -# TODO: implement azurerm_federated_identity_credential.notification_service -# subject = "system:serviceaccount:${var.kubernetes_namespace}:notification-service" +resource "azurerm_federated_identity_credential" "notification_service" { + name = "fed-taskflow-notification-service" + audience = ["api://AzureADTokenExchange"] + issuer = local.oidc_issuer_url + subject = "system:serviceaccount:taskflow:notification-service" +} # ── Key Vault Role Assignments ──────────────────────────────────────────────── # Grants each service identity read access to Key Vault secrets. # Key Vault uses RBAC (enable_rbac_authorization = true) — no access policies. # -# TODO: implement azurerm_role_assignment.kv_api_service -# scope = data.terraform_remote_state.foundation.outputs.key_vault_id -# role_definition_name = "Key Vault Secrets User" -# principal_id = data.terraform_remote_state.foundation.outputs.mi_api_service_principal_id +resource "azurerm_role_assignment" "kv_secrets_api_service" { + scope = local.key_vault_id + role_definition_name = "Key Vault Secrets User" + principal_id = local.api_service_principal_id +} -# TODO: implement azurerm_role_assignment.kv_processor_service -# role_definition_name = "Key Vault Secrets User" -# principal_id = data.terraform_remote_state.foundation.outputs.mi_processor_service_principal_id +resource "azurerm_role_assignment" "kv_secrets_processor_service" { + scope = local.key_vault_id + role_definition_name = "Key Vault Secrets User" + principal_id = local.processor_service_principal_id +} # TODO: implement azurerm_role_assignment.kv_notification_service -# role_definition_name = "Key Vault Secrets User" -# principal_id = data.terraform_remote_state.foundation.outputs.mi_notification_service_principal_id +resource "azurerm_role_assignment" "kv_secrets_notification_service" { + scope = local.key_vault_id + role_definition_name = "Key Vault Secrets User" + principal_id = local.notification_service_principal_id +} # ── Service Bus Role Assignments ────────────────────────────────────────────── @@ -119,20 +142,26 @@ locals { # # Scope is the namespace — not the topic or subscription. # Namespace-scoped roles cover all topics/subscriptions within it. -# -# TODO: implement azurerm_role_assignment.sb_sender_api_service -# scope = data.terraform_remote_state.foundation.outputs.service_bus_namespace_id -# role_definition_name = "Azure Service Bus Data Sender" -# principal_id = data.terraform_remote_state.foundation.outputs.mi_api_service_principal_id + +resource "azurerm_role_assignment" "sb_sender_api_service" { + scope = local.service_bus_namespace_id + role_definition_name = "Azure Service Bus Data Sender" + principal_id = local.api_service_principal_id +} # TODO: implement azurerm_role_assignment.sb_sender_processor_service -# role_definition_name = "Azure Service Bus Data Sender" -# principal_id = data.terraform_remote_state.foundation.outputs.mi_processor_service_principal_id +resource "azurerm_role_assignment" "sb_sender_processor_service" { + scope = local.service_bus_namespace_id + role_definition_name = "Azure Service Bus Data Sender" + principal_id = local.processor_service_principal_id +} # TODO: implement azurerm_role_assignment.sb_receiver_notification_service -# scope = data.terraform_remote_state.foundation.outputs.service_bus_namespace_id -# role_definition_name = "Azure Service Bus Data Receiver" -# principal_id = data.terraform_remote_state.foundation.outputs.mi_notification_service_principal_id +resource "azurerm_role_assignment" "sb_receiver_notification_service" { + scope = local.service_bus_namespace_id + role_definition_name = "Azure Service Bus Data Receiver" + principal_id = local.notification_service_principal_id +} # ── Key Vault Private Endpoint ──────────────────────────────────────────────── @@ -148,19 +177,40 @@ locals { # Until then, this endpoint is provisioned but not resolvable. # # TODO: implement azurerm_private_endpoint.key_vault -# name = "pe-kv-taskflow" -# resource_group_name = local.rg_name -# location = var.location -# subnet_id = local.snet_compute_id +resource "azurerm_private_endpoint" "kv" { + name = "pe-kv-taskflow" + resource_group_name = local.rg_name + location = var.location + subnet_id = local.snet_compute_id + + private_service_connection { + name = "pe-kv-taskflow-conn" + private_connection_resource_id = local.key_vault_id + subresource_names = ["vault"] + is_manual_connection = false + } + + tags = local.common_tags +} + +# ----------------------------------------------------------------------------- +# DNS A record — ON DEMAND ONLY +# Uncomment when the platform deploys the privatelink.vaultcore.azure.net zone. # -# private_service_connection { -# name = "psc-kv-taskflow" -# private_connection_resource_id = data.terraform_remote_state.foundation.outputs.key_vault_id -# subresource_names = ["vault"] -# is_manual_connection = false -# } +# Without that zone, this private endpoint provisions successfully and receives +# a private IP in snet-compute, but Key Vault's hostname continues to resolve +# to its public IP. Because public_network_access_enabled = false on the vault, +# services will receive a connection timeout — not a 403. The PE is functional; +# the DNS is the missing link. # -# tags = local.common_tags +# resource "azurerm_private_dns_a_record" "kv" { +# name = local.key_vault_name +# zone_name = "privatelink.vaultcore.azure.net" +# resource_group_name = "" +# ttl = 300 +# records = [azurerm_private_endpoint.kv.private_service_connection[0].private_ip_address] +# } +# ----------------------------------------------------------------------------- # ── Private DNS A Record (on-demand) ───────────────────────────────────────── # Add this block when platform/connectivity provisions the DNS zone. diff --git a/terraform/compute/main.tf b/terraform/compute/main.tf index abeb1f6..c7e9cf2 100644 --- a/terraform/compute/main.tf +++ b/terraform/compute/main.tf @@ -67,28 +67,36 @@ locals { # - Traffic path: AKS node → snet-aks → NAT Gateway → internet # The NVA only handles east-west (spoke-to-spoke). AKS egress bypasses it. -# TODO: implement azurerm_public_ip.nat -# name = "pip-nat-taskflow" -# resource_group_name = local.rg_name -# location = var.location -# allocation_method = "Static" -# sku = "Standard" -# tags = local.common_tags - -# TODO: implement azurerm_nat_gateway.taskflow -# name = "nat-taskflow" -# resource_group_name = local.rg_name -# location = var.location -# sku_name = "Standard" -# tags = local.common_tags - -# TODO: implement azurerm_nat_gateway_public_ip_association.taskflow -# nat_gateway_id = azurerm_nat_gateway.taskflow.id -# public_ip_address_id = azurerm_public_ip.nat.id - -# TODO: implement azurerm_subnet_nat_gateway_association.aks -# subnet_id = local.snet_aks_id -# nat_gateway_id = azurerm_nat_gateway.taskflow.id +resource "azurerm_public_ip" "nat" { + name = "pip-nat-taskflow" + resource_group_name = local.rg_name + location = var.location + allocation_method = "Static" + sku = "Standard" + zones = ["1", "2", "3"] + + tags = local.common_tags +} + +resource "azurerm_nat_gateway" "nat" { + name = "nat-taskflow" + resource_group_name = local.rg_name + location = var.location + sku_name = "Standard" + idle_timeout_in_minutes = 10 + + tags = local.common_tags +} + +resource "azurerm_nat_gateway_public_ip_association" "nat" { + nat_gateway_id = azurerm_nat_gateway.nat.id + public_ip_address_id = azurerm_public_ip.nat.id +} + +resource "azurerm_subnet_nat_gateway_association" "aks" { + subnet_id = local.snet_aks_id + nat_gateway_id = azurerm_nat_gateway.nat.id +} # ── AKS ─────────────────────────────────────────────────────────────────────── @@ -127,62 +135,73 @@ locals { # Cluster-level: SystemAssigned (manages cluster infrastructure — load balancers, etc.) # Workload-level: UserAssigned identities from foundation tier, referenced per service. -# TODO: implement azurerm_kubernetes_cluster.taskflow -# name = "aks-taskflow" -# resource_group_name = local.rg_name -# location = var.location -# dns_prefix = "taskflow" -# kubernetes_version = var.kubernetes_version -# sku_tier = var.aks_sku_tier -# -# oidc_issuer_enabled = true -# workload_identity_enabled = true -# -# network_profile { -# network_plugin = "azure" -# network_plugin_mode = "overlay" -# network_policy = "cilium" -# ebpf_data_plane = "cilium" -# outbound_type = "userAssignedNATGateway" -# service_cidr = "172.16.0.0/16" -# dns_service_ip = "172.16.0.10" -# } -# -# api_server_access_profile { -# authorized_ip_ranges = var.authorized_ip_ranges -# } -# -# default_node_pool { -# name = "system" -# node_count = 1 -# vm_size = var.system_node_vm_size -# vnet_subnet_id = local.snet_aks_id -# only_critical_addons_enabled = true -# node_labels = { -# "nodepool-type" = "system" -# } -# upgrade_settings { -# max_surge = "10%" -# } -# } -# -# identity { -# type = "SystemAssigned" -# } -# -# tags = local.common_tags +resource "azurerm_kubernetes_cluster" "aks" { + name = "aks-taskflow" + resource_group_name = local.rg_name + location = var.location + dns_prefix = "aks-taskflow" + + oidc_issuer_enabled = true + workload_identity_enabled = true + + default_node_pool { + name = "system" + node_count = 1 + vm_size = "Standard_D2s_v3" + vnet_subnet_id = local.snet_aks_id + os_disk_size_gb = 30 + type = "VirtualMachineScaleSets" + only_critical_addons_enabled = true + temporary_name_for_rotation = "systmp" + + node_labels = { + "nodepool-type" = "system" + } + } + + identity { + type = "SystemAssigned" + } + + network_profile { + network_plugin = "azure" + network_plugin_mode = "overlay" + network_policy = "cilium" + network_data_plane = "cilium" + service_cidr = "172.16.0.0/16" + dns_service_ip = "172.16.0.10" + outbound_type = "userAssignedNATGateway" + } + + api_server_access_profile { + authorized_ip_ranges = var.authorized_ip_ranges + } + + oms_agent { + log_analytics_workspace_id = local.law_id + } + + depends_on = [azurerm_subnet_nat_gateway_association.aks] + + tags = local.common_tags +} # TODO: implement azurerm_kubernetes_cluster_node_pool.workload -# name = "workload" -# kubernetes_cluster_id = azurerm_kubernetes_cluster.taskflow.id -# vm_size = var.workload_node_vm_size -# vnet_subnet_id = local.snet_aks_id -# enable_auto_scaling = true -# min_count = var.workload_node_min_count -# max_count = var.workload_node_max_count -# node_labels = { -# "nodepool-type" = "workload" -# } -# upgrade_settings { -# max_surge = "33%" -# } +resource "azurerm_kubernetes_cluster_node_pool" "workload" { + name = "workload" + kubernetes_cluster_id = azurerm_kubernetes_cluster.aks.id + vm_size = "Standard_D2s_v3" + vnet_subnet_id = local.snet_aks_id + os_disk_size_gb = 30 + mode = "User" + + auto_scaling_enabled = true + min_count = 1 + max_count = 3 + + node_labels = { + "nodepool-type" = "workload" + } + + tags = local.common_tags +} diff --git a/terraform/compute/outputs.tf b/terraform/compute/outputs.tf index a030eab..b858d5f 100644 --- a/terraform/compute/outputs.tf +++ b/terraform/compute/outputs.tf @@ -1,31 +1,26 @@ # ── AKS ─────────────────────────────────────────────────────────────────────── -# TODO: output "aks_id" { -# description = "Resource ID of the AKS cluster" -# value = azurerm_kubernetes_cluster.taskflow.id -# } +output "aks_id" { + description = "Resource ID of the AKS cluster." + value = azurerm_kubernetes_cluster.aks.id +} -# TODO: output "aks_name" { -# description = "Name of the AKS cluster — used by az aks command invoke in CI/CD" -# value = azurerm_kubernetes_cluster.taskflow.name -# } +output "aks_name" { + description = "Name of the AKS cluster." + value = azurerm_kubernetes_cluster.aks.name +} -# TODO: output "oidc_issuer_url" { -# description = "OIDC issuer URL for the cluster. Required by bindings tier to create -# federated credentials on managed identities." -# value = azurerm_kubernetes_cluster.taskflow.oidc_issuer_url -# } +output "oidc_issuer_url" { + description = "OIDC issuer URL of the AKS cluster. Consumed by the bindings tier to create federated credentials." + value = azurerm_kubernetes_cluster.aks.oidc_issuer_url +} -# TODO: output "kubelet_identity_object_id" { -# description = "Object ID of the kubelet managed identity. -# Bindings tier assigns AcrPull on ACR to this identity so nodes can pull images." -# value = azurerm_kubernetes_cluster.taskflow.kubelet_identity[0].object_id -# } +output "kubelet_identity_object_id" { + description = "Object ID of the AKS kubelet identity. Used as the principal for the AcrPull role assignment in the bindings tier." + value = azurerm_kubernetes_cluster.aks.kubelet_identity[0].object_id +} - -# ── NAT Gateway ─────────────────────────────────────────────────────────────── - -# TODO: output "nat_gateway_public_ip" { -# description = "Public IP address of the NAT Gateway — stable egress IP for AKS nodes" -# value = azurerm_public_ip.nat.ip_address -# } +output "nat_gateway_public_ip" { + description = "Public IP address of the NAT Gateway. All AKS pod egress originates from this address." + value = azurerm_public_ip.nat.ip_address +} diff --git a/terraform/foundation/main.tf b/terraform/foundation/main.tf index f2f5a5d..fbebbfb 100644 --- a/terraform/foundation/main.tf +++ b/terraform/foundation/main.tf @@ -41,102 +41,169 @@ locals { } } +data "azurerm_client_config" "current" {} + +resource "random_string" "suffix" { + length = 4 + upper = false + special = false +} + # ── ACR ─────────────────────────────────────────────────────────────────────── # Container registry for all taskflow service images. -# -# Design decisions: -# - admin_enabled: false — images are pulled via AcrPull role assignment on -# the AKS kubelet identity. No username/password ever used. -# - public_network_access_enabled: true — Basic SKU does not support private -# endpoints. Images are pushed from GitHub Actions runners (public) and -# pulled by AKS nodes (also public egress via NAT Gateway). -# -# TODO: implement azurerm_container_registry.taskflow -# name = "crtaskflow" -# resource_group_name = local.rg_name -# location = var.location -# sku = var.acr_sku -# admin_enabled = false -# tags = local.common_tags + +resource "azurerm_container_registry" "acr" { + name = "crtaskflow${random_string.suffix.result}" + resource_group_name = local.rg_name + location = var.location + sku = "Standard" + admin_enabled = false + + tags = local.common_tags +} # ── Service Bus ─────────────────────────────────────────────────────────────── # Async messaging between processor-service and notification-service. # Topic/subscription model: processor publishes, notification-service consumes. -# -# Design decisions: -# - local_auth_enabled: false — forces workload identity authentication. -# No connection strings are used anywhere. Role assignments are in the -# bindings tier after managed identities exist. -# - Standard SKU required — Basic SKU does not support topics. -# -# TODO: implement azurerm_servicebus_namespace.taskflow -# name = "sb-taskflow-" -# resource_group_name = local.rg_name -# location = var.location -# sku = var.service_bus_sku -# local_auth_enabled = false -# tags = local.common_tags - -# TODO: implement azurerm_servicebus_topic.task_events -# name = "task-events" -# namespace_id = azurerm_servicebus_namespace.taskflow.id + +resource "azurerm_servicebus_namespace" "sb" { + name = "sb-taskflow-${random_string.suffix.result}" + resource_group_name = local.rg_name + location = var.location + sku = "Standard" + local_auth_enabled = false + + tags = local.common_tags +} + +resource "azurerm_servicebus_topic" "task_events" { + name = "task-events" + namespace_id = azurerm_servicebus_namespace.sb.id + + partitioning_enabled = true + default_message_ttl = "PT1H" + max_size_in_megabytes = 1024 +} # TODO: implement azurerm_servicebus_subscription.notification_service -# name = "notification-service" -# topic_id = azurerm_servicebus_topic.task_events.id -# max_delivery_count = 10 +resource "azurerm_servicebus_subscription" "notification" { + name = "notification-service" + topic_id = azurerm_servicebus_topic.task_events.id + max_delivery_count = 5 + + lock_duration = "PT30S" + dead_lettering_on_message_expiration = true + default_message_ttl = "PT1H" +} # ── Key Vault ───────────────────────────────────────────────────────────────── # Central secret store for all taskflow services. # -# Design decisions: -# - enable_rbac_authorization: true — no legacy access policies. -# Secrets access is granted via role assignments in the bindings tier. -# - public_network_access_enabled: false — accessed via private endpoint only. -# Private endpoint is provisioned in the bindings tier into snet-compute. -# - purge_protection_enabled: true — prevents accidental permanent deletion. -# - soft_delete_retention_days: 7 — minimum retention, sufficient for a lab. # -# TODO: implement azurerm_key_vault.taskflow -# name = "kv-taskflow-" -# resource_group_name = local.rg_name -# location = var.location -# tenant_id = data.azurerm_client_config.current.tenant_id -# sku_name = "standard" -# enable_rbac_authorization = true -# public_network_access_enabled = false -# purge_protection_enabled = true -# soft_delete_retention_days = 7 -# tags = local.common_tags +resource "azurerm_key_vault" "kv" { + name = "kv-taskflow-${random_string.suffix.result}" + resource_group_name = local.rg_name + location = var.location + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + + rbac_authorization_enabled = true + purge_protection_enabled = true + soft_delete_retention_days = 90 + public_network_access_enabled = false + + network_acls { + default_action = "Deny" + bypass = ["AzureServices"] + } + + tags = local.common_tags +} # ── Managed Identities ──────────────────────────────────────────────────────── # One User Assigned Managed Identity per service that requires Azure resource # access (Key Vault, Service Bus). # -# Why created here (Tier 1) and not in the bindings tier: -# - AKS must reference the identities at cluster creation time so the -# workload identity webhook can project tokens for them. -# - Federated credentials (which need the AKS OIDC issuer URL) are added -# in the bindings tier after AKS exists. The identity itself is decoupled -# from the federation — it is just a principal until bindings wires it up. -# -# TODO: implement azurerm_user_assigned_identity.api_service -# name = "mi-taskflow-api-service" -# resource_group_name = local.rg_name -# location = var.location -# tags = local.common_tags - -# TODO: implement azurerm_user_assigned_identity.processor_service -# name = "mi-taskflow-processor-service" -# resource_group_name = local.rg_name -# location = var.location -# tags = local.common_tags - -# TODO: implement azurerm_user_assigned_identity.notification_service -# name = "mi-taskflow-notification-service" -# resource_group_name = local.rg_name -# location = var.location -# tags = local.common_tags +resource "azurerm_user_assigned_identity" "mi_api_service" { + name = "mi-taskflow-api-service" + resource_group_name = local.rg_name + location = var.location + + tags = local.common_tags +} + +resource "azurerm_user_assigned_identity" "mi_processor_service" { + name = "mi-taskflow-processor-service" + resource_group_name = local.rg_name + location = var.location + + tags = local.common_tags +} + +resource "azurerm_user_assigned_identity" "mi_notification_service" { + name = "mi-taskflow-notification-service" + resource_group_name = local.rg_name + location = var.location + + tags = local.common_tags +} + + +#----- Diagnostic Settings ─────────────────────────────────────────────────────── +# Wires diagnostics from ACR, Service Bus, and Key Vault to the Log Analytics +# workspace provisioned by platform/management. +resource "azurerm_monitor_diagnostic_setting" "acr" { + name = "diag-acr-taskflow" + target_resource_id = azurerm_container_registry.acr.id + log_analytics_workspace_id = local.law_id + + enabled_log { + category = "ContainerRegistryRepositoryEvents" + } + + enabled_log { + category = "ContainerRegistryLoginEvents" + } + + enabled_metric { + category = "AllMetrics" + + } +} + +resource "azurerm_monitor_diagnostic_setting" "sb" { + name = "diag-sb-taskflow" + target_resource_id = azurerm_servicebus_namespace.sb.id + log_analytics_workspace_id = local.law_id + + enabled_log { + category = "OperationalLogs" + } + + enabled_log { + category = "RuntimeAuditLogs" + } + + enabled_metric { + category = "AllMetrics" + + } +} + +resource "azurerm_monitor_diagnostic_setting" "kv" { + name = "diag-kv-taskflow" + target_resource_id = azurerm_key_vault.kv.id + log_analytics_workspace_id = local.law_id + + enabled_log { + category = "AuditEvent" + } + + enabled_metric { + category = "AllMetrics" + + } +} diff --git a/terraform/foundation/outputs.tf b/terraform/foundation/outputs.tf index 8fa36a9..b2fa57c 100644 --- a/terraform/foundation/outputs.tf +++ b/terraform/foundation/outputs.tf @@ -1,87 +1,90 @@ -# ── ACR ─────────────────────────────────────────────────────────────────────── -# Consumed by: bindings (AcrPull role assignment) - -# TODO: output "acr_id" { -# description = "Resource ID of the container registry" -# value = azurerm_container_registry.taskflow.id -# } - -# TODO: output "acr_name" { -# description = "Name of the container registry" -# value = azurerm_container_registry.taskflow.name -# } - -# TODO: output "acr_login_server" { -# description = "Login server URL for the container registry" -# value = azurerm_container_registry.taskflow.login_server -# } - - -# ── Service Bus ─────────────────────────────────────────────────────────────── -# Consumed by: bindings (Service Bus role assignments) - -# TODO: output "service_bus_namespace_id" { -# description = "Resource ID of the Service Bus namespace" -# value = azurerm_servicebus_namespace.taskflow.id -# } - -# TODO: output "service_bus_namespace_name" { -# description = "Name of the Service Bus namespace" -# value = azurerm_servicebus_namespace.taskflow.name -# } - - -# ── Key Vault ───────────────────────────────────────────────────────────────── -# Consumed by: bindings (private endpoint + role assignments) - -# TODO: output "key_vault_id" { -# description = "Resource ID of the Key Vault" -# value = azurerm_key_vault.taskflow.id -# } - -# TODO: output "key_vault_uri" { -# description = "URI of the Key Vault — used by CSI driver SecretProviderClass" -# value = azurerm_key_vault.taskflow.vault_uri -# } - -# TODO: output "key_vault_name" { -# description = "Name of the Key Vault" -# value = azurerm_key_vault.taskflow.name -# } - - -# ── Managed Identities ──────────────────────────────────────────────────────── -# Consumed by: compute (passed to AKS), bindings (federated creds + role assignments) -# client_id — used in Kubernetes ServiceAccount annotation -# id — used in AKS identity block -# principal_id — used for role assignments - -# TODO: output "mi_api_service_id" { -# value = azurerm_user_assigned_identity.api_service.id -# } -# TODO: output "mi_api_service_client_id" { -# value = azurerm_user_assigned_identity.api_service.client_id -# } -# TODO: output "mi_api_service_principal_id" { -# value = azurerm_user_assigned_identity.api_service.principal_id -# } - -# TODO: output "mi_processor_service_id" { -# value = azurerm_user_assigned_identity.processor_service.id -# } -# TODO: output "mi_processor_service_client_id" { -# value = azurerm_user_assigned_identity.processor_service.client_id -# } -# TODO: output "mi_processor_service_principal_id" { -# value = azurerm_user_assigned_identity.processor_service.principal_id -# } - -# TODO: output "mi_notification_service_id" { -# value = azurerm_user_assigned_identity.notification_service.id -# } -# TODO: output "mi_notification_service_client_id" { -# value = azurerm_user_assigned_identity.notification_service.client_id -# } -# TODO: output "mi_notification_service_principal_id" { -# value = azurerm_user_assigned_identity.notification_service.principal_id -# } +# ACR +output "acr_id" { + description = "Resource ID of the Azure Container Registry." + value = azurerm_container_registry.acr.id +} + +output "acr_name" { + description = "Name of the Azure Container Registry." + value = azurerm_container_registry.acr.name +} + +output "acr_login_server" { + description = "Login server hostname of the Azure Container Registry." + value = azurerm_container_registry.acr.login_server +} + +# Service Bus +output "service_bus_namespace_id" { + description = "Resource ID of the Service Bus namespace." + value = azurerm_servicebus_namespace.sb.id +} + +output "service_bus_namespace_name" { + description = "Name of the Service Bus namespace." + value = azurerm_servicebus_namespace.sb.name +} + +# Key Vault +output "key_vault_id" { + description = "Resource ID of the Key Vault." + value = azurerm_key_vault.kv.id +} + +output "key_vault_uri" { + description = "URI of the Key Vault. Used by services to construct secret references." + value = azurerm_key_vault.kv.vault_uri +} + +output "key_vault_name" { + description = "Name of the Key Vault." + value = azurerm_key_vault.kv.name +} + +# Managed Identity — api-service +output "mi_api_service_id" { + description = "Resource ID of the api-service managed identity." + value = azurerm_user_assigned_identity.mi_api_service.id +} + +output "mi_api_service_client_id" { + description = "Client ID of the api-service managed identity. Annotated onto the Kubernetes ServiceAccount." + value = azurerm_user_assigned_identity.mi_api_service.client_id +} + +output "mi_api_service_principal_id" { + description = "Principal ID of the api-service managed identity. Used as subject for RBAC role assignments." + value = azurerm_user_assigned_identity.mi_api_service.principal_id +} + +# Managed Identity — processor-service +output "mi_processor_service_id" { + description = "Resource ID of the processor-service managed identity." + value = azurerm_user_assigned_identity.mi_processor_service.id +} + +output "mi_processor_service_client_id" { + description = "Client ID of the processor-service managed identity. Annotated onto the Kubernetes ServiceAccount." + value = azurerm_user_assigned_identity.mi_processor_service.client_id +} + +output "mi_processor_service_principal_id" { + description = "Principal ID of the processor-service managed identity. Used as subject for RBAC role assignments." + value = azurerm_user_assigned_identity.mi_processor_service.principal_id +} + +# Managed Identity — notification-service +output "mi_notification_service_id" { + description = "Resource ID of the notification-service managed identity." + value = azurerm_user_assigned_identity.mi_notification_service.id +} + +output "mi_notification_service_client_id" { + description = "Client ID of the notification-service managed identity. Annotated onto the Kubernetes ServiceAccount." + value = azurerm_user_assigned_identity.mi_notification_service.client_id +} + +output "mi_notification_service_principal_id" { + description = "Principal ID of the notification-service managed identity. Used as subject for RBAC role assignments." + value = azurerm_user_assigned_identity.mi_notification_service.principal_id +}