A standalone module that provisions a single
azurerm_eventgrid_system_topic— the Event Grid projection of an Azure resource's system events — targetinghashicorp/azurerm ~> 4.0. The empty call ships with a system-assigned managed identity so downstream delivery runs under role-based access control, not shared keys.
- 📡 Provisions one Event Grid system topic — the managed representation of the platform events a source Azure resource (a Storage account, a resource group, a subscription, a Key Vault, and many more) already emits.
- 🔗 Binds to a source via
source_arm_resource_idandtopic_type, the two immutable fields that identify which resource's events flow through the topic. - 🪪 Identity-first delivery — a system-assigned managed identity by default, so event subscriptions built on this topic can deliver to endpoints under role-based access control with no shared keys to distribute.
- 🧾 Metric-ready — emits
metric_arm_resource_idso a sibling diagnostic-settings or metric-alert module can target the topic directly. - 🧱 Composable, single-purpose — the topic only; event subscriptions, diagnostics, private connectivity, and role grants are deliberately left to sibling modules and wired by
id.
💡 Why it matters: A system topic is the seam between an Azure resource's built-in event stream and the subscriptions that route those events to handlers. Shipping it with a managed identity by default means the very first subscription you attach can deliver events using Entra ID and least-privilege role assignments instead of a long-lived key — the safe posture is the default, and removing the identity is the deliberate step.
If this module saves you time, please consider supporting it:
- ⭐ Star the repository on GitHub.
- 🤝 Connect on LinkedIn: linkedin.com/in/microsoftexpert
- ☕ Buy me a coffee: buymeacoffee.com/microsoftexpert
flowchart LR
RG["terraform-azurerm-resource-group"]
SRC["Source resource (Storage / RG / Subscription)"]
ID["terraform-azurerm-user-assigned-identity"]
RA["terraform-azurerm-role-assignments"]
ST["terraform-azurerm-eventgrid-system-topic"]
SUB["terraform-azurerm-eventgrid-event-subscription"]
HANDLER["Handlers (Functions / Queues / Webhooks)"]
RG -->|"resource_group_name + location"| ST
SRC -->|"source_arm_resource_id + topic_type"| ST
ID -->|"identity_ids"| ST
ST -->|"identity principal delivers under RBAC"| RA
ST -->|"system topic id"| SUB
SUB -->|"delivers events"| HANDLER
classDef self fill:#0078D4,stroke:#004578,color:#ffffff;
classDef keystone fill:#004578,stroke:#002b4d,color:#ffffff;
classDef sibling fill:#eef2f7,stroke:#c7d2e0,color:#1b2a3a;
class ST self;
class SUB keystone;
class RG,SRC,ID,RA,HANDLER sibling;
This module owns only the system topic. It consumes a resource group, a source resource, and (optionally) a user-assigned identity by reference, and it is consumed downstream by event subscriptions, role assignments, and diagnostic settings — each by the topic's id (or metric_arm_resource_id for metrics).
flowchart LR
subgraph INPUTS["Inputs"]
N["name / resource_group_name / location"]
SRC["source_arm_resource_id / topic_type"]
IDN["identity (default SystemAssigned)"]
T["tags / timeouts"]
end
ST["azurerm_eventgrid_system_topic.this"]
subgraph OUTPUTS["Outputs"]
OID["id"]
ONAME["name"]
OMET["metric_arm_resource_id"]
OPRIN["identity_principal_id"]
end
N -->|"required identity"| ST
SRC -->|"event source binding"| ST
IDN -->|"dynamic identity block"| ST
T -->|"tags + timeouts"| ST
ST -->|"emits"| OID
ST -->|"emits"| ONAME
ST -->|"emits"| OMET
ST -->|"emits"| OPRIN
classDef self fill:#0078D4,stroke:#004578,color:#ffffff;
classDef keystone fill:#004578,stroke:#002b4d,color:#ffffff;
classDef sibling fill:#eef2f7,stroke:#c7d2e0,color:#1b2a3a;
class ST keystone;
class N,SRC,IDN,T,OID,ONAME,OMET,OPRIN sibling;
Resource inventory
| Resource | Count | Role |
|---|---|---|
azurerm_eventgrid_system_topic.this |
1 | The keystone system topic, including its optional identity and timeouts blocks. |
| Requirement | Value |
|---|---|
| Terraform | >= 1.12.0 |
hashicorp/azurerm |
~> 4.0 |
| Provider block | None in this module — the caller configures provider "azurerm" { features {} }, auth, and subscription. |
Schema notes that bite (verified against the live provider schema):
- 🔁
name,resource_group_name, andlocationare force-new — changing any of them replaces the system topic. - 🔁
source_arm_resource_idandtopic_typeare force-new — a system topic is permanently bound to one source resource of one kind. Repointing it at a different source, or changing its type, forces replacement. - 🧭
topic_typemust match the kind of resource named bysource_arm_resource_id(for exampleMicrosoft.Storage.StorageAccountswith a Storage account ID). The set of topic types is defined by the platform and grows over time, so it is accepted as a free-form string rather than a closed enum. - 🪪
identityandtagsare the only fields that update in place; everything else is immutable. - 📊
id,metric_arm_resource_id, and the identity'sprincipal_id/tenant_idare computed — known only after apply. - 🧱 A system topic has no public-network, local-auth, or firewall knobs of its own. It is a passive projection of a source's events; those controls live on the source resource and on the event subscriptions attached to this topic.
- Create / manage the system topic:
EventGrid Contributoron the target resource group (orContributor), or a custom role withMicrosoft.EventGrid/systemTopics/*at resource-group scope. - Bind to the source resource: read access (for example
Reader) on the resource named bysource_arm_resource_id, so Event Grid can attach the topic to that source. - Event delivery (data plane): grant the system topic's identity principal the role each delivery target requires (for example
Storage Queue Data Message Senderon a queue, orEventGrid Data Senderon another topic). Do this with a sibling role-assignments module at the target's scope, usingidentity_principal_idfrom the outputs.
Grant the least privilege at the smallest scope that works.
- An existing resource group in a supported US Azure region.
- The
Microsoft.EventGridresource provider registered on the target subscription. - The source resource already provisioned — its Resource ID is
source_arm_resource_id, and its own resource provider (for exampleMicrosoft.Storage) registered. - The caller configures the
provider "azurerm" { features {} }block, auth, and subscription — this module declares none of them.
terraform-azurerm-eventgrid-system-topic/
├── providers.tf # required_version >= 1.12.0; azurerm ~> 4.0; no provider block
├── variables.tf # deeply-typed object() schemas + tags/timeouts tail
├── main.tf # azurerm_eventgrid_system_topic.this; dynamic identity / timeouts blocks
├── outputs.tf # id first, then name, metric_arm_resource_id, identity principal/tenant
├── README.md # this document
├── SCOPE.md # the cross-module contract
├── LICENSE # MIT, Copyright (c) 2026 Casey Wood
└── .gitignore # the canonical library ignore set
The smallest real call — a system topic for a Storage account, with the default managed identity:
provider "azurerm" {
features {}
}
module "eventgrid_system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-orders-storage"
resource_group_name = "rg-eventing-prod"
location = "eastus2"
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-data-prod/providers/Microsoft.Storage/storageAccounts/stordersprod"
tags = {
environment = "prod"
workload = "orders"
}
}ℹ️ The caller owns the provider, authentication, and the mandatory
features {}block. Pin the module with?ref=v1.0.0— never a branch.
Consumes
| Input | Type | Source module |
|---|---|---|
resource_group_name |
string |
terraform-azurerm-resource-group (name) |
location |
string |
caller / terraform-azurerm-resource-group (location) |
source_arm_resource_id |
string |
the source module's id (e.g. terraform-azurerm-storage-account) |
identity.identity_ids |
set(string) |
terraform-azurerm-user-assigned-identity (id) |
Emits
| Output | Description | Consumed by |
|---|---|---|
id |
System topic Resource ID (first) | eventgrid-event-subscription, role assignments, diagnostics |
name |
System topic name | diagnostics / tagging |
metric_arm_resource_id |
Resource ID for platform metrics | metric alerts, diagnostic settings |
identity_principal_id |
System-assigned identity principal ID (null when none) | role assignments on delivery targets |
identity_tenant_id |
Managed identity tenant ID (null when none) | documentation / delivery configuration |
1 · Minimal system topic for a Storage account
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-orders-storage"
resource_group_name = "rg-eventing"
location = "eastus2"
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = var.storage_account_id
}🔒 The empty call already carries a system-assigned managed identity, so the first subscription you attach can deliver events under role-based access control without a shared key.
2 · System topic for a resource group
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-rg-audit"
resource_group_name = "rg-eventing"
location = "eastus2"
topic_type = "Microsoft.Resources.ResourceGroups"
source_arm_resource_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-workload-prod"
}💡 A resource-group system topic surfaces
Microsoft.Resources.ResourceWriteSuccess/Deleteand similar control-plane events for everything in that group.
3 · Subscription-level system topic
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-subscription-audit"
resource_group_name = "rg-governance"
location = "eastus2"
topic_type = "Microsoft.Resources.Subscriptions"
source_arm_resource_id = "/subscriptions/00000000-0000-0000-0000-000000000000"
}ℹ️ For subscription-scoped events,
source_arm_resource_idis the bare subscription ID andtopic_typeisMicrosoft.Resources.Subscriptions. The topic still lives inside a resource group you nominate.
4 · System topic for a Key Vault
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-kv-secrets"
resource_group_name = "rg-eventing"
location = "eastus2"
topic_type = "Microsoft.KeyVault.vaults"
source_arm_resource_id = var.key_vault_id
}💡 Key Vault system topics emit secret/key/certificate near-expiry and new-version events — a common trigger for rotation automation.
5 · Explicit system-assigned identity
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-sysid"
resource_group_name = "rg-eventing"
location = "eastus2"
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = var.storage_account_id
identity = {
type = "SystemAssigned"
}
}ℹ️ This is the module default written out. Use
identity_principal_idfrom the outputs to grant the topic access to identity-authenticated delivery targets.
6 · User-assigned identity for delivery
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-uami"
resource_group_name = "rg-eventing"
location = "eastus2"
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = var.storage_account_id
identity = {
type = "UserAssigned"
identity_ids = [var.user_assigned_identity_id]
}
}ℹ️
identity_idsis required whenevertypeincludesUserAssigned; the module validates this at parse time.
7 · Both identity types together
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-mixed-id"
resource_group_name = "rg-eventing"
location = "eastus2"
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = var.storage_account_id
identity = {
type = "SystemAssigned, UserAssigned"
identity_ids = [var.user_assigned_identity_id]
}
}ℹ️ The combined value must be written exactly as
"SystemAssigned, UserAssigned"(with the comma and space).
8 · No managed identity (opt out)
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-no-identity"
resource_group_name = "rg-eventing"
location = "eastus2"
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = var.storage_account_id
identity = null
}
⚠️ Opting out of the identity means any subscription on this topic must deliver with an endpoint that does not require the topic's identity (for example a storage-queue destination secured a different way). Prefer leaving the default system-assigned identity in place.
9 · Fully tagged, timeouts tuned
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-governed"
resource_group_name = "rg-eventing"
location = "eastus2"
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = var.storage_account_id
tags = {
environment = "prod"
owner = "platform-eventing"
cost_center = "cc-4471"
}
timeouts = {
create = "30m"
delete = "30m"
}
}💡 Tags update in place; apply your organization's tagging convention here without risking replacement.
10 · for_each across many source accounts
locals {
sources = {
orders = var.orders_storage_account_id
inventory = var.inventory_storage_account_id
shipping = var.shipping_storage_account_id
}
}
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
for_each = local.sources
name = "egst-${each.key}"
resource_group_name = "rg-eventing"
location = "eastus2"
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = each.value
tags = { workload = each.key }
}💡 Fan the module out with
for_eachto stamp one system topic per source account with a consistent shape.
11 · Grant the topic identity a delivery role (sibling wiring)
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-delivery"
resource_group_name = module.rg.name
location = module.rg.location
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = module.storage.id
}
# Sibling module grants the topic's identity permission to deliver to a queue.
module "delivery_access" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-role-assignments.git?ref=v1.0.0"
scope = module.storage.id
role_assignments = {
queue_delivery = {
role_definition_name = "Storage Queue Data Message Sender"
principal_id = module.system_topic.identity_principal_id
}
}
}🔒 Delivery runs under the topic's managed identity and a least-privilege role — no key is created or shared.
12 · Diagnostic settings by metric id (sibling wiring)
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-monitored"
resource_group_name = module.rg.name
location = module.rg.location
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = module.storage.id
}
# Sibling diagnostic-settings module targets the topic's resource id.
module "system_topic_diagnostics" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-monitor-diagnostic-setting.git?ref=v1.0.0"
name = "diag-egst"
target_resource_id = module.system_topic.id
log_analytics_workspace_id = var.log_analytics_workspace_id
}ℹ️ Use
idfor a diagnostic setting; usemetric_arm_resource_idwhen a metric alert needs the metrics resource specifically.
13 · Event subscription on the topic (sibling wiring)
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-with-sub"
resource_group_name = module.rg.name
location = module.rg.location
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = module.storage.id
}
# The event subscription is a separate module keyed off the system topic name.
# module "subscription" {
# source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-event-subscription.git?ref=v1.0.0"
# system_topic = module.system_topic.name
# ...
# }ℹ️ Event subscriptions are intentionally a sibling module. A system-topic subscription references this topic by
name(within the same resource group) rather than owning it here.
14 · Least-privilege delivery to a second topic
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-fanout"
resource_group_name = module.rg.name
location = module.rg.location
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = module.storage.id
identity = {
type = "SystemAssigned"
}
}
# Grant only EventGrid Data Sender on the downstream topic — nothing broader.
module "fanout_access" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-role-assignments.git?ref=v1.0.0"
scope = var.downstream_topic_id
role_assignments = {
fanout = {
role_definition_name = "EventGrid Data Sender"
principal_id = module.system_topic.identity_principal_id
}
}
}🔒 Scope the role assignment to the single downstream topic, not the resource group, so the identity can send only where it must.
15 · 🏗️ End-to-end composition
provider "azurerm" {
features {}
}
# 1) Resource group
module "rg" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-group.git?ref=v1.0.0"
name = "rg-eventing-prod"
location = "eastus2"
tags = { environment = "prod", workload = "orders" }
}
# 2) The source resource whose events we want — a Storage account
module "storage" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-storage-account.git?ref=v1.0.0"
name = "stordersprod"
resource_group_name = module.rg.name
location = module.rg.location
}
# 3) The Event Grid system topic — bound to the Storage account, identity by default
module "system_topic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-eventgrid-system-topic.git?ref=v1.0.0"
name = "egst-orders-storage"
resource_group_name = module.rg.name
location = module.rg.location
topic_type = "Microsoft.Storage.StorageAccounts"
source_arm_resource_id = module.storage.id
tags = { environment = "prod" }
}
# 4) Grant the topic identity permission to deliver into a storage queue, least-privilege
module "delivery_access" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-role-assignments.git?ref=v1.0.0"
scope = module.storage.id
role_assignments = {
queue_delivery = {
role_definition_name = "Storage Queue Data Message Sender"
principal_id = module.system_topic.identity_principal_id
}
}
}
# 5) An event subscription (sibling module) consumes the system topic downstream.
# module "subscription" { source = ".../terraform-azurerm-eventgrid-event-subscription..."
# system_topic = module.system_topic.name ... }
output "system_topic_id" {
value = module.system_topic.id
}🔒 The topic delivers under its managed identity and a single least-privilege role assignment scoped to the source — no shared key is created or distributed. The event subscription is intentionally a sibling module, wired by
module.system_topic.name.
Required
| Name | Type | Description |
|---|---|---|
name |
string |
System topic name (force-new). |
resource_group_name |
string |
Existing resource group (force-new). |
location |
string |
Azure region (force-new). |
topic_type |
string |
Source resource kind, e.g. Microsoft.Storage.StorageAccounts (force-new). |
source_arm_resource_id |
string |
Resource ID of the source resource (force-new). |
Optional — identity & tail
| Name | Type | Default | Description |
|---|---|---|---|
identity |
object |
{ type = "SystemAssigned" } |
Managed identity block; type enum-validated; identity_ids required for UserAssigned. Set null for no identity. |
tags |
map(string) |
{} |
Resource tags (mutable). |
timeouts |
object |
null |
Create/read/update/delete timeouts. |
Full object() schemas
variable "identity" {
type = object({
type = string # SystemAssigned | UserAssigned | "SystemAssigned, UserAssigned"
identity_ids = optional(set(string)) # required when type includes UserAssigned
})
default = {
type = "SystemAssigned"
}
}
variable "timeouts" {
type = object({
create = optional(string)
read = optional(string)
update = optional(string)
delete = optional(string)
})
default = null
}| Output | Description | Kind |
|---|---|---|
id |
Passthrough | |
name |
Passthrough | |
resource_group_name |
Resource group containing the system topic | Passthrough |
location |
Azure region of the system topic, normalised by the provider (for example "eastus") | Passthrough |
tags |
Tags applied to the system topic, echoed so a composition can stamp sibling resources with the same set | Passthrough |
topic_type |
System topic type, for example "Microsoft.Storage.StorageAccounts" | Passthrough |
source_resource_id |
Passthrough | |
source_arm_resource_id |
Passthrough | |
source_scope_kind |
Passthrough | |
source_subscription_id |
Subscription ID parsed out of the source Resource ID, or null for a management-group source | Derived |
source_is_global_scope_but_location_is_regional |
Passthrough | |
topic_type_is_not_checked_against_the_source |
Constant | |
source_binding_replacement_destroys_subscriptions |
Constant | |
is_singleton_per_event_source |
Constant | |
metric_resource_id |
Passthrough | |
metric_arm_resource_id |
The same metrics identifier under the deprecated provider attribute name, which is removed in provider 5.0 | Passthrough |
identity_type |
Managed identity type configured on the system topic ("SystemAssigned", "UserAssigned" or "SystemAssigned, UserAssigned"), or null when the topic has no managed identity | Derived |
identity_principal_id |
Derived | |
identity_tenant_id |
Tenant ID of the system topic's managed identity, or null when the topic has no managed identity | Derived |
identity_ids |
Set of user-assigned identity Resource IDs attached to the system topic | Derived |
has_managed_identity |
Whether the system topic carries any managed identity | Derived |
has_system_assigned_identity |
Whether a system-assigned identity is present, stated separately because only this kind populates identity_principal_id | Derived |
has_user_assigned_identity |
Whether at least one user-assigned identity is attached | Derived |
identity_grants_nothing_by_itself |
Derived | |
read_does_not_list_access_keys |
Constant | |
emits_no_secret |
Constant | |
has_no_network_or_local_auth_controls |
Constant | |
uses_deprecated_source_argument |
Constant | |
creates_no_event_subscription |
Constant | |
creates_no_role_assignment |
Always true | Constant |
event_subscription_capacity |
Passthrough |
🔒 No secret is emitted. Delivery is expected to run under the managed identity and role assignments granted on downstream targets.
- Single keystone. The module owns exactly one resource,
azurerm_eventgrid_system_topic.this. The optional nested blocks (identity,timeouts) are rendered withdynamicblocks guarded bytry(...), so an omitted input renders as absent rather than an error. - Secure default is an identity, not a lock. Unlike a data-plane service, a system topic has no public-access or firewall surface to close. Its meaningful secure default is identity-based delivery: the empty call attaches a system-assigned identity so downstream subscriptions authenticate with Entra ID and role assignments. Removing it (
identity = null) is the explicit opt-out. - The source binding is permanent.
source_arm_resource_idandtopic_typeare force-new and must agree — the ID must reference a resource of the kind named by the type. Repointing at a new source, or switching type, replaces the topic and any subscriptions built on it. topic_typeis an open string. The platform adds new system-topic types over time, so this module accepts the value as a validated-shape string rather than a closed enum that could reject a legal, newer type.- Metrics vs resource id. Diagnostic settings and role assignments target
id; metric alerts that need the metrics resource specifically usemetric_arm_resource_id. Both are emitted. features {}dependence. The module carries noprovider {}block. If it appears not to initialize in isolation, the cause is a missing caller-sideprovider "azurerm" { features {} }block — that is expected.
| Concern | Secure default (empty call) | Opt-out (caller must type it) |
|---|---|---|
| Delivery credential | system-assigned managed identity (identity = { type = "SystemAssigned" }) |
set identity = null, or supply a user-assigned identity |
| Embedded secrets | none accepted or emitted | — (not supported by design) |
| Identity type safety | type validated against the legal set; identity_ids required for UserAssigned |
— |
| Source binding | explicit source_arm_resource_id + topic_type, both immutable |
— (a new source means a new topic) |
- The delivery credential defaults to a managed identity so no key is ever distributed.
- The
identity.typeenum is enforced with avalidation {}block listing the legal values. - The module never accepts or emits a plaintext secret.
# From the module folder (offline, plan-only):
terraform init -backend=false
terraform validate
terraform fmt -check- Pin the module with
?ref=v1.0.0— never a branch. - No
terraform applyruns during authoring; a human applies from CI against real credentials. - The caller supplies the provider, auth, and
features {}block.
The offline proof gate is what this module guarantees:
terraform init -backend=false— resolves the pinnedazurerm ~> 4.0provider without a backend.terraform validate— proves the configuration is internally consistent and type-correct against the provider schema; the deeply-typedobject()inputs andvalidation {}rules surface malformed input at parse time.terraform fmt -check— enforces canonical formatting.
Neither validate nor fmt calls Azure. Only terraform plan (run by a human, from CI) exercises the ARM API.
$ terraform output
id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-eventing-prod/providers/Microsoft.EventGrid/systemTopics/egst-orders-storage"
name = "egst-orders-storage"
metric_arm_resource_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-eventing-prod/providers/Microsoft.EventGrid/systemTopics/egst-orders-storage"
identity_principal_id = "11111111-2222-3333-4444-555555555555"
identity_tenant_id = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"| Symptom | Cause | Fix |
|---|---|---|
provider "azurerm" ... features initialization error |
No caller-side features {} block |
Add provider "azurerm" { features {} } to the root module. |
| Plan shows the system topic being replaced | A force-new field changed (name, location, resource_group_name, source_arm_resource_id, or topic_type) |
Confirm the change is intended; these fields cannot change in place. |
| Create fails referencing the source resource | topic_type does not match the kind of source_arm_resource_id, or the caller lacks read access on the source |
Align the type with the source kind and grant Reader on the source resource. |
identity.type must be exactly "SystemAssigned", "UserAssigned" or "SystemAssigned, UserAssigned"... |
identity.type value not in the legal set |
Use SystemAssigned, UserAssigned, or exactly "SystemAssigned, UserAssigned". |
identity.identity_ids is required when identity.type includes "UserAssigned"... |
type includes UserAssigned but no identity_ids supplied |
Provide at least one user-assigned identity resource ID. |
| Subscription delivery is denied | The topic's identity lacks a role on the delivery target | Grant the target's data-plane role to identity_principal_id at the target's scope. |
- Terraform Registry —
azurerm_eventgrid_system_topic - Microsoft Learn — Azure Event Grid system topics
- Microsoft Learn — System topics and event sources
- Sibling modules —
terraform-azurerm-resource-group,terraform-azurerm-storage-account,terraform-azurerm-user-assigned-identity,terraform-azurerm-role-assignments,terraform-azurerm-eventgrid-system-topic-event-subscription(the subscriptions on this topic — it also selects which of this topic's managed identities delivers events),terraform-azurerm-eventgrid-event-subscription(the generic form, which addresses its parent byscopeinstead) - This module's
SCOPE.md
💙 "Infrastructure as Code should be standardized, consistent, and secure."