A single IoT Hub custom routing endpoint that forwards device messages to a Service Bus queue. Secure by default: the empty call authenticates with a managed identity (
identityBased), so no shared secret is required unless the caller explicitly opts into a connection string. Targetshashicorp/azurerm ~> 4.0.
This module manages a single IoT Hub custom routing endpoint that delivers device messages to a Service Bus queue you already own.
- π Creates one
azurerm_iothub_endpoint_servicebus_queuekeyed to an existing IoT Hub. - π Authenticates with a managed identity by default (
identityBased) β a shared connection string is an explicit opt-in. - π― Points at the target queue by
endpoint_uri+entity_path(identity auth) or an embeddedconnection_string(key auth). - β±οΈ Exposes per-operation
timeouts; this resource does not support Azuretags. - π€ Emits the endpoint's resource
idfirst, then itsname, for downstreamazurerm_iothub_routewiring.
π‘ Why it matters: IoT Hub message routing decouples device ingestion from downstream processing. A Service Bus queue endpoint gives you ordered, load-levelled, competing-consumer delivery of device telemetry β and with
identityBasedauth, the hub reaches the queue through a managed identity, so there is no connection string to store, rotate, or leak.
If this module saves you time, a little support goes a long way:
- β Star the repository on GitHub.
- π Connect on LinkedIn: linkedin.com/in/microsoftexpert
- β Buy me a coffee: buymeacoffee.com/microsoftexpert
flowchart LR
hub["terraform-azurerm-iothub"]
me["terraform-azurerm-iothub-endpoint-servicebus-queue"]
sbns["terraform-azurerm-servicebus-namespace"]
sbq["azurerm_servicebus_queue owned by the namespace module"]
uai["terraform-azurerm-user-assigned-identity"]
route["terraform-azurerm-iothub-route"]
sib_eh["terraform-azurerm-iothub-endpoint-eventhub"]
sib_top["terraform-azurerm-iothub-endpoint-servicebus-topic"]
sib_sc["terraform-azurerm-iothub-endpoint-storage-container"]
sib_cdb["terraform-azurerm-iothub-endpoint-cosmosdb-account"]
hub -->|"iothub_id"| me
sbns -->|"endpoint_uri"| me
sbq -->|"entity_path"| me
uai -->|"identity_id"| me
me -->|"endpoint_names"| route
hub -.->|"sibling endpoint"| sib_eh
hub -.->|"sibling endpoint"| sib_top
hub -.->|"sibling endpoint"| sib_sc
hub -.->|"sibling endpoint"| sib_cdb
classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
classDef target fill:#004578,stroke:#002d4d,color:#ffffff;
classDef ext fill:#f2f2f2,stroke:#c8c8c8,color:#111111;
class me me;
class hub target;
class sbns,sbq,uai,route,sib_eh,sib_top,sib_sc,sib_cdb ext;
The IoT Hub is owned by a sibling module and referenced by iothub_id. This module attaches one Service Bus queue endpoint to that hub, targeting a queue owned by the Service Bus namespace/queue modules and (for identity auth) a user-assigned identity. A routing rule module then directs matching messages to this endpoint by name. The Event Hub, Service Bus topic, storage container, and Cosmos DB account endpoints are parallel siblings that attach to the same hub.
flowchart LR
in_id["name / resource_group_name / iothub_id"]
in_auth["authentication_type / identity_id / subscription_id"]
in_tgt["connection_string / endpoint_uri / entity_path"]
res["azurerm_iothub_endpoint_servicebus_queue.this"]
out["id / name"]
in_id -->|"input"| res
in_auth -->|"input"| res
in_tgt -->|"input"| res
res -->|"output"| out
classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
class res me;
Resource inventory
| Resource | Cardinality | Role |
|---|---|---|
azurerm_iothub_endpoint_servicebus_queue.this |
single (keystone) | The Service Bus queue routing endpoint attached to the named IoT Hub. |
| Requirement | Value |
|---|---|
| Terraform | >= 1.12.0 |
hashicorp/azurerm |
~> 4.0 |
| Provider block | None in this module β the caller configures provider "azurerm", its authentication, and the mandatory features {} block. |
Schema notes that bite
- π
name,resource_group_name, andiothub_idare force-new β changing any of them destroys and recreates the endpoint. - π The endpoint
namemust be unique across the hub's endpoint types. The namesevents,operationsMonitoringEvents,fileNotifications, and$defaultare reserved and cannot be used. - π
keyBasedauth requiresconnection_string; it is the only valid target spec for key auth. - πͺͺ
identityBasedauth requiresendpoint_uriandentity_path; aconnection_stringis not used. β οΈ identity_idis only valid withidentityBasedand must reference one of the IoT Hub's assigned identities. Omit it to fall back to the hub's system-assigned identity.- βΉοΈ
subscription_iddefaults to the parent IoT Hub's subscription when omitted.
Least-privilege at the parent IoT Hub scope:
Microsoft.Devices/IotHubs/writeandMicrosoft.Devices/IotHubs/readon the IoT Hub βContributorscoped to the hub (there is no built-in "IoT Hub Contributor" role, and the four real IoT Hub roles are data-plane only, so none can create a routing endpoint) (or Contributor scoped to the hub) covers these.- For
identityBasedrouting, the hub's identity needs Azure Service Bus Data Sender on the target queue so it can deliver messages.
- An existing IoT Hub (
azurerm_iothub) and a target Service Bus queue in a supported US Azure region. - For
identityBasedauth, a managed identity assigned to the IoT Hub with data-plane access to the queue (Azure Service Bus Data Sender). - The
Microsoft.Devicesresource provider registered on the subscription.
terraform-azurerm-iothub-endpoint-servicebus-queue/
βββ providers.tf # required_version + azurerm ~> 4.0 pin; no provider block
βββ variables.tf # typed inputs: name, resource_group_name, iothub_id, authentication_type, connection_string, endpoint_uri, entity_path, identity_id, subscription_id, timeouts
βββ main.tf # keystone azurerm_iothub_endpoint_servicebus_queue.this + dynamic timeouts
βββ outputs.tf # id (first), then name
βββ README.md # this document
βββ SCOPE.md # the cross-module contract
βββ LICENSE # MIT
βββ .gitignore # canonical library ignore set
provider "azurerm" {
features {}
}
module "servicebus_queue_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
name = "telemetry-to-sbq"
resource_group_name = "rg-iot-eastus"
iothub_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-iot-eastus/providers/Microsoft.Devices/IotHubs/iot-plant-eastus"
# identityBased is the default β no connection string required.
endpoint_uri = "sb://sb-iot-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
}βΉοΈ The caller owns the
provider "azurerm"block, its authentication (Azure CLI, managed identity, or OIDC), and the mandatoryfeatures {}block. This module declares no provider configuration.
π With no
authentication_typeset, the endpoint usesidentityBasedβ the safe default. No shared secret is required.
Consumes
| Input | Type | Source module |
|---|---|---|
iothub_id |
string |
terraform-azurerm-iothub |
endpoint_uri / entity_path |
string |
terraform-azurerm-servicebus-namespace β the namespace's endpoint, and the queue's own name from that module's queues map |
identity_id |
string |
terraform-azurerm-user-assigned-identity |
connection_string |
string (sensitive) |
provisioned out of band (key auth only) |
Emits
| Output | Description | Consumed by |
|---|---|---|
id |
Resource ID of the routing endpoint (emitted first). | terraform-azurerm-iothub-route |
name |
Endpoint name. | routing rules referencing the endpoint by name |
The examples below reference existing resources by ID or name rather than creating them; this module owns only its own resource. Those references are declared inputs:
variable "iot_identity_audit_id" {
description = "id of an existing iot identity audit that these examples reference but do not create."
type = string
}
variable "iot_identity_ingest_id" {
description = "id of an existing iot identity ingest that these examples reference but do not create."
type = string
}
variable "servicebus_queue_name" {
description = "name of an existing servicebus queue that these examples reference but do not create."
type = string
}1 Β· Minimal, secure default (identityBased)
The empty-ish call: identity auth is used because it is the default. The hub's system-assigned identity delivers to the queue.
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
name = "telemetry-to-sbq"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
endpoint_uri = "sb://sb-iot-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
}π
authentication_typedefaults toidentityBased; no connection string is stored anywhere.
2 Β· Explicit identityBased for auditability
State the secure default explicitly so the posture is obvious in review.
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
name = "telemetry-to-sbq"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
authentication_type = "identityBased"
endpoint_uri = "sb://sb-iot-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
}π‘ Writing the value down makes the security choice visible even though it matches the default.
3 Β· User-assigned identity
Pin delivery to a specific user-assigned identity that is attached to the hub.
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
name = "telemetry-to-sbq"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
authentication_type = "identityBased"
endpoint_uri = "sb://sb-iot-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
identity_id = module.iot_identity.id
}
β οΈ identity_idmust be one of the IoT Hub's assigned identities. If it is not attached to the hub, the apply fails.
4 Β· keyBased with a connection string
Opt into key auth only when a connection string is genuinely unavoidable.
variable "sbq_connection_string" {
type = string
sensitive = true
}
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
name = "telemetry-to-sbq"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
authentication_type = "keyBased"
connection_string = var.sbq_connection_string
}π Provision the connection string out of band (for example, from a Key Vault reference) and pass it in β never commit it to source. Prefer
identityBasedwhere you can.
5 Β· Explicit target subscription
Route to a queue in a different subscription from the hub.
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
name = "telemetry-to-sbq"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
endpoint_uri = "sb://sb-shared-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
subscription_id = "11111111-1111-1111-1111-111111111111"
}βΉοΈ
subscription_iddefaults to the hub's subscription; set it only when the target queue lives elsewhere.
6 Β· Custom timeouts
Override the provider's per-operation timeouts.
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
name = "telemetry-to-sbq"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
endpoint_uri = "sb://sb-iot-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
timeouts = {
create = "30m"
delete = "30m"
}
}βΉοΈ Unset timeout fields fall back to provider defaults; leave
timeoutsasnullto accept them entirely.
7 Β· Referencing upstream Service Bus outputs
Wire the namespace endpoint and queue name from sibling modules instead of hardcoding them.
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
name = "telemetry-to-sbq"
resource_group_name = module.resource_group.name
iothub_id = module.iothub.id
endpoint_uri = module.servicebus_namespace.endpoint
entity_path = var.servicebus_queue_name
}π‘ Sourcing the URI and queue name from module outputs keeps a rename in one place and lets Terraform order creation correctly.
8 Β· Naming by environment
Derive the endpoint name from an environment value for repeatable multi-stage deployments.
variable "environment" {
type = string
default = "prod"
}
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
name = "telemetry-to-sbq-${var.environment}"
resource_group_name = "rg-iot-${var.environment}-eastus"
iothub_id = module.iothub.id
endpoint_uri = "sb://sb-iot-${var.environment}-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
}π
nameis force-new β changing the environment value replaces the endpoint, which is the intended behavior across distinct environments.
9 Β· Multiple endpoints with for_each
Attach a keyed set of queue endpoints to one hub.
locals {
endpoints = {
telemetry = "device-telemetry"
alerts = "device-alerts"
audit = "device-audit"
}
}
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
for_each = local.endpoints
name = "route-to-${each.key}"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
endpoint_uri = "sb://sb-iot-eastus.servicebus.windows.net"
entity_path = each.value
}π‘ A stable map key (
telemetry,alerts,audit) means adding or removing one endpoint never re-indexes the others.
10 Β· Per-endpoint queue and identity with for_each
Give each endpoint its own queue and user-assigned identity via a map of objects.
locals {
endpoints = {
telemetry = { queue = "device-telemetry", identity = var.iot_identity_ingest_id }
audit = { queue = "device-audit", identity = var.iot_identity_audit_id }
}
}
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
for_each = local.endpoints
name = "route-to-${each.key}"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
authentication_type = "identityBased"
endpoint_uri = "sb://sb-iot-eastus.servicebus.windows.net"
entity_path = each.value.queue
identity_id = each.value.identity
}βΉοΈ Deeply-typed local maps keep a growing set of endpoints declarative and reviewable.
11 Β· Mixed auth across a fleet
Most endpoints use identity auth; a single legacy queue is opted into key auth deliberately.
locals {
endpoints = {
telemetry = { name = "route-to-telemetry", auth = "identityBased", uri = "sb://sb-iot-eastus.servicebus.windows.net", queue = "device-telemetry", conn = null }
legacy = { name = "route-to-legacy", auth = "keyBased", uri = null, queue = null, conn = var.legacy_connection_string }
}
}
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
for_each = local.endpoints
name = each.value.name
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
authentication_type = each.value.auth
endpoint_uri = each.value.uri
entity_path = each.value.queue
connection_string = each.value.conn
}
β οΈ Thelegacyentry uses a connection string β keep such opt-outs explicit and few, and review them each plan.
12 Β· Consuming the emitted id downstream
Feed the endpoint id into an output or another module.
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
name = "telemetry-to-sbq"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
endpoint_uri = "sb://sb-iot-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
}
output "sbq_endpoint_id" {
value = module.sbq_endpoint.id
}π‘ The
idis emitted first by convention and is the canonical cross-resource reference in Azure.
13 Β· Wiring the endpoint name into a route
A routing rule directs matching messages to this endpoint by name.
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
name = "telemetry-to-sbq"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
endpoint_uri = "sb://sb-iot-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
}
module "telemetry_route" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-route.git?ref=v1.0.0"
name = "telemetry-route"
resource_group_name = "rg-iot-eastus"
iothub_name = module.iothub.name
routing_source = "DeviceMessages"
condition = "level = 'telemetry'"
endpoint_names = [module.sbq_endpoint.name]
enabled = true
}π‘ A route references the endpoint by
name, notid; the endpoint must exist before the route that targets it.
14 Β· ποΈ End-to-end composition
Wire the IoT Hub, a Service Bus namespace, and a user-assigned identity from sibling modules into this endpoint, then feed the endpoint into a route.
provider "azurerm" {
features {}
}
module "resource_group" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-group.git?ref=v1.0.0"
name = "rg-iot-eastus"
location = "eastus"
}
module "iot_identity" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-user-assigned-identity.git?ref=v1.0.0"
name = "id-iot-ingest"
resource_group_name = module.resource_group.name
location = module.resource_group.location
}
module "iothub" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub.git?ref=v1.0.0"
name = "iot-plant-eastus"
resource_group_name = module.resource_group.name
location = module.resource_group.location
sku = { name = "S1", capacity = 1 }
}
module "servicebus_namespace" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-servicebus-namespace.git?ref=v1.0.0"
name = "sb-iot-eastus"
resource_group_name = module.resource_group.name
location = module.resource_group.location
# The queue is a child of the namespace module, not a module of its own.
queues = {
"device-telemetry" = { name = "device-telemetry" }
}
}
module "sbq_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-servicebus-queue.git?ref=v1.0.0"
name = "telemetry-to-sbq"
resource_group_name = module.resource_group.name
iothub_id = module.iothub.id
authentication_type = "identityBased"
endpoint_uri = module.servicebus_namespace.endpoint
entity_path = "device-telemetry"
identity_id = module.iot_identity.id
}
module "telemetry_route" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-route.git?ref=v1.0.0"
name = "telemetry-route"
resource_group_name = module.resource_group.name
iothub_name = module.iothub.name
routing_source = "DeviceMessages"
condition = "level = 'telemetry'"
endpoint_names = [module.sbq_endpoint.name]
enabled = true
}
output "sbq_endpoint_id" {
value = module.sbq_endpoint.id
}π‘ Attribute references let Terraform order creation automatically: resource group and identity, then the hub and Service Bus queue, then this endpoint, then the route β no
depends_onrequired. The hub's identity must hold Azure Service Bus Data Sender on the queue foridentityBaseddelivery to succeed.
Identity (required)
| Name | Type | Description |
|---|---|---|
name |
string |
Name of the routing endpoint. Unique across the hub's endpoint types. Force-new. |
resource_group_name |
string |
Resource group under which the endpoint is created. Force-new. |
iothub_id |
string |
Resource ID of the parent IoT Hub. Force-new. |
Configuration (optional)
| Name | Type | Default | Description |
|---|---|---|---|
authentication_type |
string |
"identityBased" |
identityBased (managed identity) or keyBased (connection string). Secure default. |
connection_string |
string (sensitive) |
null |
Service Bus queue connection string. Required and only valid with keyBased. |
endpoint_uri |
string |
null |
Service Bus namespace endpoint URI. Required and only valid with identityBased. |
entity_path |
string |
null |
Target queue name within the namespace. Required and only valid with identityBased. |
identity_id |
string |
null |
User-assigned identity ID (must be attached to the hub). Only valid with identityBased. |
subscription_id |
string |
null |
Target subscription. Defaults to the hub's subscription. |
timeouts |
object(...) |
null |
Per-operation timeouts. |
Full input schemas
variable "name" {
type = string
description = <<-DESC
The name of the routing endpoint. Must be unique across the IoT Hub's endpoint types.
The names `events`, `operationsMonitoringEvents`, `fileNotifications`, and `$default` are
reserved and cannot be used. Changing this forces a new resource to be created.
DESC
}
variable "resource_group_name" {
type = string
description = "The name of the resource group containing the **Service Bus queue** this endpoint targets -- NOT the IoT Hub's resource group. The hub is located entirely from `iothub_id`; this value is sent as part of the endpoint payload and never appears in the endpoint's Resource ID. Changing this forces a new resource to be created."
}
variable "iothub_id" {
type = string
description = "The resource ID of the parent IoT Hub this endpoint attaches to. Changing this forces a new resource to be created."
}
variable "authentication_type" {
type = string
default = "identityBased"
description = <<-DESC
How the endpoint authenticates against the Service Bus namespace. One of `keyBased` (uses a
`connection_string`) or `identityBased` (uses a managed identity plus `endpoint_uri` and
`entity_path`). Defaults to `identityBased` so no shared secret is required; set to `keyBased`
only when a connection string is unavoidable.
DESC
validation {
condition = contains(["keyBased", "identityBased"], var.authentication_type)
error_message = "authentication_type must be one of: keyBased, identityBased."
}
}
variable "connection_string" {
type = string
default = null
sensitive = true
description = <<-DESC
The connection string for the Service Bus queue. Mandatory and only valid when `authentication_type`
is `keyBased`. Provision this out of band and pass a reference β never commit it to source.
DESC
}
variable "endpoint_uri" {
type = string
default = null
description = "URI of the Service Bus namespace endpoint. Mandatory and only valid when `authentication_type` is `identityBased`."
}
variable "entity_path" {
type = string
default = null
description = "Name of the Service Bus queue within the namespace. Mandatory and only valid when `authentication_type` is `identityBased`."
}
variable "identity_id" {
type = string
default = null
description = <<-DESC
Resource ID of the user-assigned managed identity used to authenticate. Only valid when
`authentication_type` is `identityBased`, and must be one of the IoT Hub's assigned identities.
When omitted with `identityBased`, the IoT Hub's system-assigned identity is used.
DESC
}
variable "subscription_id" {
type = string
default = null
description = "The subscription ID for the endpoint. When omitted, it defaults to the subscription of the parent IoT Hub."
}
variable "timeouts" {
type = object({
create = optional(string)
read = optional(string)
update = optional(string)
delete = optional(string)
})
default = null
description = "Optional per-operation timeouts (create / read / update / delete)."
}| Output | Description | Kind |
|---|---|---|
id |
Azure Resource ID of the routing endpoint | Passthrough |
name |
Name of the routing endpoint, as created | Passthrough |
iothub_id |
Resource ID of the parent IoT Hub this endpoint is attached to | Passthrough |
iothub_name |
Name of the parent IoT Hub, taken from iothub_id | Derived |
iothub_resource_group_name |
Resource group holding the parent IoT Hub, taken from iothub_id | Derived |
iothub_subscription_id |
Subscription containing the parent IoT Hub, taken from iothub_id | Derived |
target_resource_group_name |
Resource group of the TARGET Service Bus namespace, read back from the endpoint record | Passthrough |
subscription_id |
Subscription recorded on the endpoint for the target namespace | Passthrough |
authentication_type |
The authentication mode in force, read back from Azure: keyBased or identityBased | Passthrough |
endpoint_uri |
Service Bus namespace URI in use on the identityBased path, read back from Azure | Passthrough |
entity_path |
Name of the Service Bus queue within the namespace on the identityBased path, read back from Azure | Passthrough |
identity_id |
User-assigned identity authenticating the endpoint, read back from Azure | Passthrough |
uses_managed_identity |
True when delivery authenticates with a managed identity and no stored credential exists for this endpoint | Passthrough |
uses_user_assigned_identity |
True when a specific user-assigned identity was named | Derived |
uses_system_assigned_identity |
True when the endpoint authenticates as the parent hub itself | Derived |
has_connection_string |
Whether a connection string was supplied | Passthrough |
key_based_target_host |
Namespace host extracted from the connection string on the keyBased path, null otherwise | Derived |
connection_string_is_never_emitted |
Constant true | Constant |
azure_returns_the_shared_key_masked |
Constant true | Constant |
rotating_only_the_shared_key_produces_no_plan_diff |
Constant true, and the sharpest edge in the keyBased path | Constant |
provider_default_authentication_type_is_key_based |
Constant true, recorded because this module deviates from it | Constant |
routes_reference_this_endpoint_by_name |
Constant true | Constant |
destroy_removes_only_the_endpoint_never_its_routes |
Constant true | Constant |
endpoint_name_is_unique_across_all_endpoint_types |
Constant true | Constant |
inline_iothub_endpoint_blocks_cannot_be_mixed_with_this_module |
Constant true | Constant |
every_change_rewrites_the_whole_parent_hub |
Constant true | Constant |
changes_to_sibling_endpoints_serialise_on_a_hub_lock |
Constant true | Constant |
replacement_deletes_before_it_recreates |
Constant true | Constant |
force_new_fields |
The inputs that force replacement rather than an in-place update | Derived |
send_permission_on_the_target_is_not_verifiable_here |
Constant true | Constant |
create_guards_the_queue_collection_by_testing_the_event_hub_collection |
Constant true, and unique to the two Service Bus endpoint types -- the Event Hub and Cosmos DB endpoints do not have it | Constant |
entity_path_may_end_in_a_period_or_slash |
Constant true, and the one behavioural difference from the Service Bus TOPIC endpoint sibling | Constant |
- Single keystone, no children. This module owns exactly one resource,
azurerm_iothub_endpoint_servicebus_queue.this. The IoT Hub, the Service Bus queue, and any routing rules are sibling concerns referenced by id or name, never created here. - Force-new fields.
name,resource_group_name, andiothub_idall force replacement when changed. Because the endpointnamemust also be unique across the hub's endpoint types, plan carefully before renaming β Terraform destroys and recreates, briefly interrupting delivery. identityBasedvskeyBased. The two auth modes take mutually exclusive target specs.identityBased(the default) usesendpoint_uri+entity_pathand an optionalidentity_id;keyBasedusesconnection_string. Supplying the wrong combination for the selected mode is rejected at apply time.connection_stringis sensitive. The one secret-bearing input is markedsensitive = true. Provision it out of band and pass a reference; the module emits no secret in its outputs.- Identity attachment. When
identity_idis set, it must reference one of the IoT Hub's assigned identities and that identity must hold Azure Service Bus Data Sender on the queue. Omitting it falls back to the hub's system-assigned identity. features {}dependence. Theprovider "azurerm"block β including its mandatoryfeatures {}block β belongs to the caller's root module. If a plan fails to initialize in isolation, a missing caller-sidefeatures {}block is the usual cause.- No tags. The Service Bus queue endpoint resource does not support Azure
tags, so the universal tail istimeoutsonly.
| Concern | Secure default (empty call) | Opt-out (caller must type it) |
|---|---|---|
| Endpoint authentication | authentication_type = "identityBased" (managed identity) |
set to "keyBased" with a connection_string |
| Secret handling | no connection_string required or stored |
supply a connection_string (out of band, sensitive) |
| Secret emission | no secret emitted from outputs | β |
| Tags | not supported by this resource; none accepted | β |
- The enum that gates credential exposure defaults to the locked-down member (
identityBased), enforced by avalidation {}block listing the legal values. - The module never emits a secret; the one secret input (
connection_string) issensitive = trueand must be provisioned out of band.
# Initialize without a backend (plan-only, static analysis).
terraform init -backend=false
# Validate types and references.
terraform validate
# Confirm formatting.
terraform fmt -check- Pin the module with
?ref=v1.0.0β never a branch. - No cloud apply is performed by this workflow; a human runs
terraform applyfrom CI.
The offline proof gate:
terraform init -backend=falseβ resolves the provider without contacting a backend.terraform validateβ proves every input type, theauthentication_typeenum, and each attribute reference is well-formed.terraform fmt -checkβ confirms canonical formatting.
What only terraform plan (against a configured provider) exercises: whether the named IoT Hub and target Service Bus queue actually exist, whether the referenced identity_id is attached to the hub, and whether the hub's identity holds Azure Service Bus Data Sender on the queue. Validation cannot see live tenant state.
$ terraform output
id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-iot-eastus/providers/Microsoft.Devices/IotHubs/iot-plant-eastus/endpoints/telemetry-to-sbq"
name = "telemetry-to-sbq"| Symptom | Cause | Fix |
|---|---|---|
IotHub not found / 404 on apply |
iothub_id does not match an existing hub. |
Confirm the hub exists in the target resource group and subscription; source iothub_id from the IoT Hub module's output. |
endpoint name is reserved |
name is one of events, operationsMonitoringEvents, fileNotifications, or $default. |
Choose a unique, non-reserved endpoint name. |
endpoint name already exists |
Another endpoint on the hub already uses this name. |
Endpoint names must be unique across the hub's endpoint types; pick a different name. |
Messages not delivered with identityBased |
The hub's identity lacks data-plane access to the queue, or identity_id is not attached to the hub. |
Grant the identity Azure Service Bus Data Sender on the queue and ensure identity_id references an identity assigned to the hub. |
connection_string is required / auth mismatch |
keyBased selected without a connection_string, or identity fields supplied with keyBased. |
Provide connection_string for keyBased; use endpoint_uri + entity_path for identityBased. |
| Plan shows the endpoint being replaced after a minor edit | A force-new field (name, resource_group_name, or iothub_id) changed. |
Confirm the replacement is intended; if not, revert the field. |
provider not initialized / features error |
The caller's root module is missing the provider "azurerm" { features {} } block. |
Add the provider block with features {} to the root module; this module intentionally omits it. |
azurerm_iothub_endpoint_servicebus_queueresourceazurerm_iothubresource- IoT Hub message routing β Microsoft Learn
- Sibling modules:
terraform-azurerm-iothub-endpoint-eventhub,terraform-azurerm-iothub-endpoint-servicebus-topic,terraform-azurerm-iothub-endpoint-storage-container,terraform-azurerm-iothub-endpoint-cosmosdb-account,terraform-azurerm-iothub-route - This module's
SCOPE.mdβ the cross-module contract.
π "Infrastructure as Code should be standardized, consistent, and secure."