Azure 42: Service Bus Topics with terraform-common Publisher/Subscriber
easy⏱ 5 mincourseazure
Publisher module
module "topic-publisher" { source = "git::...terraform-common//azure/service-bus/topic/publisher?ref=main" }. Inputs: environment, system, publisher_name, topic_purpose, key_vault_name, partitioning_enabled, default_message_ttl. Creates the topic, the publisher's SAS policy (Send), and stores the connection string in the named Key Vault. One module per topic per producer.
module "topic-publisher" {
source = "git::...terraform-common//azure/service-bus/topic/publisher?ref=main"
environment = "prod"
system = "shop"
publisher_name = "odyssey-cron"
topic_purpose = "inventory-import-deltas"
key_vault_name = "odyssey-cr-p-kv-prod"
partitioning_enabled = false
default_message_ttl = "P14D"
}
Subscriber module
The mirror image: a Listen-only SAS policy bound to a named subscription on the topic. The connection string lives in the SUBSCRIBER's Key Vault — not the publisher's — so each consumer team owns their own secret rotation cadence. Each subscriber is a Terraform invocation with the topic_purpose pointing at the producer's topic.
module "topic-subscriber-lease" {
source = "git::...terraform-common//azure/service-bus/topic/subscriber?ref=main"
environment = "prod"
subscriber_name = "lease-consumer"
topic_purpose = "leasing-incentives"
key_vault_name = "lease-cons-s-kv-prod" # subscriber's own vault
}
Connection string flows into the pod
After terraform apply the secret primaryconnectionstring exists in the subscriber's Key Vault. The pipeline declares the vault in vaultNames:, the helm step does --set secrets.FINANCE_SERVICE_BUS_CONNECTION_STRING=\"$(lease-cons-s-kv-prod-connectionstring)\", and Spring Boot reads spring.cloud.azure.servicebus.connection-string at startup. Pattern is identical across all 14 backend services.
Build the topology graph
Write buildBusGraph(modules) that takes a list of { kind: 'publisher' | 'subscriber', name, topic, vault } entries and returns { topics: { [topic]: { publisher, subscribers: string[], vaults: string[] } } }. Then validate(graph) must error if any topic has zero publishers or more than one publisher. Log the topology and the validation result for a realistic Lythia spread.
Use long default_message_ttl in prod (P14D)
Two weeks gives oncall room to investigate when a consumer is broken without losing data. If you set it to the Azure default (14 days) you also avoid an awkward 'why did we drop messages on Friday' postmortem. Combine with dead-letter monitoring so a stuck message pages somebody.
Quiz: who owns the topic?
A team wants to add a new subscription to ecommerce-intent. Whose Terraform repo do they edit? Their own — they add a topic-subscriber module pointing at topic_purpose = "ecommerce-intent". Topic ownership lives with the publisher; subscriptions live with the consumer. That's why each side has its own Key Vault.