A single IoT Hub custom routing endpoint that forwards device messages to an Event Hub. Secure by default: the empty call authenticates with a managed identity, so no shared secret is required. Targets
hashicorp/azurerm ~> 4.0.
This module manages a single IoT Hub custom routing endpoint that delivers device-to-cloud messages to an Event Hub you already own.
- π Creates one
azurerm_iothub_endpoint_eventhubattached to an existing IoT Hub byiothub_id. - π Authenticates with a managed identity by default (
identityBased) β no connection string, no shared secret. - π Supports an explicit
keyBasedopt-in that carries asensitiveconnection string when identity auth is not an option. - π― Points at the target Event Hub by
endpoint_uri+entity_path(identity auth) or embeds it inconnection_string(key auth); it never creates the Event Hub or namespace. - π€ Emits the endpoint's resource
idfirst, then itsname, for downstreamazurerm_iothub_routewiring.
π‘ Why it matters: Message routing lets an IoT Hub fan device telemetry out to purpose-built services without custom egress code. An Event Hub endpoint gives high-throughput streaming consumers a clean landing zone, and the identity-based default keeps the delivery path free of standing secrets.
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-eventhub"]
eh["terraform-azurerm-eventhub-namespace"]
route["terraform-azurerm-iothub-route"]
uai["terraform-azurerm-user-assigned-identity"]
sbq["terraform-azurerm-iothub-endpoint-servicebus-queue"]
sbt["terraform-azurerm-iothub-endpoint-servicebus-topic"]
sc["terraform-azurerm-iothub-endpoint-storage-container"]
cdb["terraform-azurerm-iothub-endpoint-cosmosdb-account"]
hub -->|"iothub_id"| me
eh -->|"endpoint_uri / entity_path"| me
uai -->|"identity_id"| me
me -->|"attaches to"| hub
route -->|"routes to"| me
hub -->|"iothub_id"| sbq
hub -->|"iothub_id"| sbt
hub -->|"iothub_id"| sc
hub -->|"iothub_id"| 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 eh,route,uai,sbq,sbt,sc,cdb ext;
This module is one of five IoT Hub routing-endpoint siblings β Event Hub, Service Bus queue, Service Bus topic, storage container, and Cosmos DB account. Each attaches to the same parent IoT Hub by iothub_id. This endpoint targets an Event Hub namespace, optionally authenticates with a user-assigned identity, and is consumed by an azurerm_iothub_route that decides which messages reach it.
flowchart LR
in_id["name / resource_group_name / iothub_id"]
in_auth["authentication_type / identity_id"]
in_idn["endpoint_uri / entity_path (identityBased)"]
in_key["connection_string (keyBased)"]
res["azurerm_iothub_endpoint_eventhub.this"]
out["id / name"]
in_id -->|"input"| res
in_auth -->|"input"| res
in_idn -->|"input"| res
in_key -->|"input"| res
res -->|"output"| out
classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
class res me;
Resource inventory
| Resource | Cardinality | Role |
|---|---|---|
azurerm_iothub_endpoint_eventhub.this |
single (keystone) | The Event Hub routing endpoint registered on the parent 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. - π
keyBasedauth requiresconnection_string; the URI and entity fields do not apply. - π
identityBasedauth requiresendpoint_uriandentity_path; the connection string does not apply. β οΈ The endpointnamemust be unique across the hub's endpoint types.events,operationsMonitoringEvents,fileNotifications, and$defaultare reserved and cannot be used.- βΉοΈ
identity_idis valid only withidentityBasedand must be one of the IoT Hub's assigned identities; omit it to fall back to the hub's system-assigned identity.
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 this.- For
identityBasedrouting, the hub's identity needs Azure Event Hubs Data Sender on the target Event Hub so it can deliver messages.
- An existing IoT Hub and a target Event Hub in a supported US Azure region.
- For
identityBasedauth, a managed identity assigned to the IoT Hub with data-plane access (Azure Event Hubs Data Sender) to the Event Hub. - The
Microsoft.Devicesresource provider registered on the subscription.
terraform-azurerm-iothub-endpoint-eventhub/
βββ 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_eventhub.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 "eventhub_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
name = "telemetry-eventhub"
resource_group_name = "rg-iot-eastus"
iothub_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-iot-eastus/providers/Microsoft.Devices/IotHubs/iot-platform-eastus"
# Secure default: identity-based auth, no shared secret.
authentication_type = "identityBased"
endpoint_uri = "sb://ehns-telemetry-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.
π
authentication_typedefaults toidentityBased, so the endpoint carries no secret. With noidentity_id, delivery uses the IoT Hub's system-assigned identity.
Consumes
| Input | Type | Source module |
|---|---|---|
iothub_id |
string |
terraform-azurerm-iothub |
endpoint_uri / entity_path |
string |
terraform-azurerm-eventhub-namespace β the sb:// URI composed from its name, and the event hub's own name from that module's event_hubs map. There is no separate event-hub module; an event hub is a for_each child of the namespace |
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 "eventhub_name" {
description = "name of an existing eventhub that these examples reference but do not create."
type = string
}
variable "key_vault_id" {
description = "id of an existing key vault that these examples reference but do not create."
type = string
}1 Β· Minimal, secure default (identityBased)
The empty-ish call: identity-based auth with the hub's system-assigned identity, no secret.
module "eh_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
name = "telemetry-eventhub"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
endpoint_uri = "sb://ehns-telemetry-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
}π
authentication_typedefaults toidentityBased; no connection string is accepted or stored.
2 Β· Explicit identityBased for auditability
State the secure default in full so the posture is obvious in review.
module "eh_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
name = "telemetry-eventhub"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
authentication_type = "identityBased"
endpoint_uri = "sb://ehns-telemetry-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
}π‘ Writing
authentication_typedown documents the security choice even though it matches the default.
3 Β· identityBased with a user-assigned identity
Deliver through a specific user-assigned identity rather than the hub's system-assigned one.
module "eh_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
name = "telemetry-eventhub"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
authentication_type = "identityBased"
endpoint_uri = "sb://ehns-telemetry-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, and that identity needs Azure Event Hubs Data Sender on the target Event Hub.
4 Β· keyBased with a connection string
The explicit opt-in when identity auth is not available.
variable "eventhub_connection_string" {
type = string
sensitive = true
}
module "eh_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
name = "legacy-eventhub"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
authentication_type = "keyBased"
connection_string = var.eventhub_connection_string
}π Provision
connection_stringout of band and pass a reference β never commit it to source. The input is markedsensitive = true.
5 Β· keyBased sourced from a Key Vault reference
Keep the secret in Key Vault and hand only a reference to Terraform.
data "azurerm_key_vault_secret" "eh_conn" {
name = "eventhub-send-connection"
key_vault_id = var.key_vault_id
}
module "eh_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
name = "legacy-eventhub"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
authentication_type = "keyBased"
connection_string = data.azurerm_key_vault_secret.eh_conn.value
}π‘ A Key Vault data source keeps the connection string out of state files that you author and out of version control.
6 Β· Explicit subscription_id override
Register the endpoint against an Event Hub in a different subscription from the hub.
module "eh_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
name = "cross-sub-eventhub"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
subscription_id = "11111111-1111-1111-1111-111111111111"
endpoint_uri = "sb://ehns-shared-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
}βΉοΈ When
subscription_idis omitted, the endpoint defaults to the subscription of the parent IoT Hub.
7 Β· Custom timeouts
Override the provider's per-operation timeouts for a slow-provisioning environment.
module "eh_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
name = "telemetry-eventhub"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
endpoint_uri = "sb://ehns-telemetry-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.
8 Β· Referencing upstream namespace outputs
Wire the Event Hub namespace endpoint from a sibling module instead of hardcoding it.
module "eh_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
name = "telemetry-eventhub"
resource_group_name = module.resource_group.name
iothub_id = module.iothub.id
endpoint_uri = "sb://${module.eventhub_namespace.name}.servicebus.windows.net"
entity_path = var.eventhub_name
}π‘ Sourcing values from module outputs lets Terraform order creation correctly and keeps a rename in one place.
9 Β· Multiple endpoints with for_each
Create a keyed set of Event Hub endpoints on one hub.
locals {
endpoints = {
telemetry = { name = "telemetry-eventhub", entity = "device-telemetry" }
alerts = { name = "alerts-eventhub", entity = "device-alerts" }
audit = { name = "audit-eventhub", entity = "device-audit" }
}
}
module "eh_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
for_each = local.endpoints
name = each.value.name
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
endpoint_uri = "sb://ehns-telemetry-eastus.servicebus.windows.net"
entity_path = each.value.entity
}π‘ A stable map key (
telemetry,alerts,audit) means adding or removing one endpoint never re-indexes the others.
10 Β· Naming by environment
Derive endpoint names from an environment variable for repeatable multi-stage deployments.
variable "environment" {
type = string
default = "prod"
}
module "eh_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
name = "telemetry-eventhub-${var.environment}"
resource_group_name = "rg-iot-${var.environment}-eastus"
iothub_id = module.iothub.id
endpoint_uri = "sb://ehns-telemetry-${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.
11 Β· Consuming the emitted id in a route
Feed the endpoint id (via its name) into an IoT Hub route that selects which messages arrive.
module "eh_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
name = "telemetry-eventhub"
resource_group_name = "rg-iot-eastus"
iothub_id = module.iothub.id
endpoint_uri = "sb://ehns-telemetry-eastus.servicebus.windows.net"
entity_path = "device-telemetry"
}
output "endpoint_id" {
value = module.eh_endpoint.id
}π‘ The
idis emitted first by convention and is the canonical cross-resource reference in Azure; a route references the endpoint by itsname.
12 Β· Mixed auth across a fleet
Most endpoints use identity auth; one legacy target stays on a connection string.
variable "legacy_connection_string" {
type = string
sensitive = true
}
locals {
endpoints = {
telemetry = { name = "telemetry-eventhub", auth = "identityBased", entity = "device-telemetry" }
legacy = { name = "legacy-eventhub", auth = "keyBased", entity = null }
}
}
module "eh_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.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.auth == "identityBased" ? "sb://ehns-telemetry-eastus.servicebus.windows.net" : null
entity_path = each.value.entity
connection_string = each.value.auth == "keyBased" ? var.legacy_connection_string : null
}
β οΈ KeepkeyBasedopt-outs explicit and few. Every connection string is a standing secret; review each one on plan.
13 Β· ποΈ End-to-end composition
Wire an IoT Hub, a user-assigned identity, and an Event Hub namespace from sibling modules into this endpoint, then hand the endpoint into an IoT Hub 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-telemetry"
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-platform-eastus"
resource_group_name = module.resource_group.name
location = module.resource_group.location
sku = { name = "S1", capacity = 1 }
}
module "eventhub_namespace" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventhub-namespace.git?ref=v1.0.0"
name = "ehns-telemetry-eastus"
resource_group_name = module.resource_group.name
location = module.resource_group.location
}
module "eventhub_endpoint" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-iothub-endpoint-eventhub.git?ref=v1.0.0"
name = "telemetry-eventhub"
resource_group_name = module.resource_group.name
iothub_id = module.iothub.id
authentication_type = "identityBased"
endpoint_uri = "sb://${module.eventhub_namespace.name}.servicebus.windows.net"
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-to-eventhub"
resource_group_name = module.resource_group.name
iothub_name = module.iothub.name
routing_source = "DeviceMessages"
condition = "true"
endpoint_names = [module.eventhub_endpoint.name]
enabled = true
}
output "eventhub_endpoint_id" {
value = module.eventhub_endpoint.id
}π‘ Attribute references (
module.iothub.id,module.iot_identity.id,module.eventhub_namespace.name,module.eventhub_endpoint.name) let Terraform order creation automatically β hub and identity, then the endpoint, then the route β with nodepends_on. The namespace module emits itsnamerather than a service-bus URI, so thesb://endpoint is composed from that name; the reference still carries the dependency.
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. |
Authentication (choose one mode)
| Name | Type | Default | Description |
|---|---|---|---|
authentication_type |
string |
"identityBased" |
identityBased or keyBased. Secure default. |
endpoint_uri |
string |
null |
Event Hubs namespace endpoint URI. Required for identityBased. |
entity_path |
string |
null |
Event Hub name within the namespace. Required for identityBased. |
identity_id |
string |
null |
User-assigned identity ID. Optional; identityBased only. |
connection_string |
string (sensitive) |
null |
Event Hub connection string. Required for keyBased. |
Other (optional)
| Name | Type | Default | Description |
|---|---|---|---|
subscription_id |
string |
null |
Subscription of the endpoint. 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 **Event Hub** 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 Event Hubs 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 Event Hub. 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 Event Hubs namespace endpoint. Mandatory and only valid when `authentication_type` is `identityBased`."
}
variable "entity_path" {
type = string
default = null
description = "Name of the Event Hub 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 Event Hubs 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 |
Event Hubs namespace URI in use on the identityBased path, read back from Azure | Passthrough |
entity_path |
Name of the Event Hub 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 |
- Single keystone, no children. This module owns exactly one resource,
azurerm_iothub_endpoint_eventhub.this. The parent hub, the target Event Hub, and any routing rules are sibling concerns, referenced by id or name rather than created here. - Force-new fields.
name,resource_group_name, andiothub_idall force replacement when changed. Because the endpoint attaches to a specific hub, moving it to a different hub or renaming it destroys and recreates the endpoint β plan carefully on a live hub, since traffic to the endpoint is interrupted during replacement. keyBasedversusidentityBased. The two modes are mutually exclusive on their supporting inputs.identityBased(the default) needsendpoint_uriandentity_pathand uses a managed identity β either the specifiedidentity_idor the hub's system-assigned identity.keyBasedneedsconnection_stringand carries a standing secret; use it only when identity auth is genuinely unavailable.- Secret handling.
connection_stringis the only secret input; it is markedsensitive = trueso Terraform redacts it in plan and console output. The module emits no secret β the only outputs areidandname. Provision the connection string out of band (for example from Key Vault) and pass a reference. 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 IoT Hub Event Hub endpoint resource does not support Azure
tags, so the universal tail istimeoutsonly.
| Concern | Secure default (empty call) | Opt-out (caller must type it) |
|---|---|---|
| Authentication | authentication_type = "identityBased" (managed identity, no secret) |
set to "keyBased" and supply a connection_string |
| Secret exposure | no secret accepted or emitted with the default | connection_string (sensitive) only under keyBased |
| Hub / Event Hub ownership | referenced by id and URI, never created here | β |
| Tags | not supported by this resource; none accepted | β |
- The input that gates whether a standing secret exists defaults to the secretless value (
identityBased). - Where a secret is unavoidable,
connection_stringissensitive = true; provision it out of band and pass a reference, do not commit it. - The module emits no secret β only the endpoint
idandnameleave the module.
# 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 Event Hub actually exist, whether the endpoint_uri / entity_path or connection_string resolve to a reachable target, whether the chosen identity holds Azure Event Hubs Data Sender on the Event Hub, and whether the caller's identity holds the required Microsoft.Devices/IotHubs/* permissions. Validation cannot see live tenant state.
$ terraform output
id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-iot-eastus/providers/Microsoft.Devices/IotHubs/iot-platform-eastus/routingEndpoints/telemetry-eventhub"
name = "telemetry-eventhub"| Symptom | Cause | Fix |
|---|---|---|
endpoint name is reserved on plan or apply |
name is one of events, operationsMonitoringEvents, fileNotifications, or $default. |
Choose a unique custom name that does not collide with a built-in endpoint. |
connection_string is required error |
authentication_type = "keyBased" but no connection_string was supplied. |
Provide a connection_string, or switch to identityBased with endpoint_uri + entity_path. |
endpoint_uri/entity_path required error |
authentication_type = "identityBased" but one of the two is missing. |
Supply both endpoint_uri and entity_path; they are mandatory for identity auth. |
| Messages not delivered; sender authorization failure | The identity lacks data-plane access to the Event Hub. | Grant Azure Event Hubs Data Sender on the target Event Hub to the hub's identity (or the identity_id). |
identity_id not found / not assigned |
The supplied identity is not one of the IoT Hub's assigned identities. | Assign the user-assigned identity to the IoT Hub first, or omit identity_id to use the system-assigned identity. |
| 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_eventhubresourceazurerm_iothubresourceazurerm_iothub_routeresource- IoT Hub message routing β Microsoft Learn
- Sibling modules:
terraform-azurerm-iothub,terraform-azurerm-eventhub-namespace,terraform-azurerm-user-assigned-identity,terraform-azurerm-iothub-route - This module's
SCOPE.mdβ the cross-module contract.
π "Infrastructure as Code should be standardized, consistent, and secure."