Skip to content

Commit f1ed437

Browse files
committed
Event hubs
1 parent 63be737 commit f1ed437

6 files changed

Lines changed: 366 additions & 0 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Terraform Template - Azure Event Hubs
2+
3+
Costa Rica
4+
5+
[![GitHub](https://img.shields.io/badge/--181717?logo=github&logoColor=ffffff)](https://github.com/)
6+
[brown9804](https://github.com/brown9804)
7+
8+
Last updated: 2026-02-16
9+
10+
------------------------------------------
11+
12+
> This template contains Terraform configurations to create an Azure Event Hubs namespace and an Event Hub (optionally with consumer groups).
13+
14+
> [!NOTE]
15+
> - The Resource Group is created via the AzAPI provider (management plane) to keep creation idempotent (ARM PUT) and align with other templates in this repository.
16+
> - Event Hubs namespace names are globally unique. Keeping `append_random_suffix = true` helps avoid collisions.
17+
18+
## File Descriptions
19+
20+
- **main.tf**: Creates the Resource Group, Event Hubs Namespace, Event Hub, and optional consumer groups.
21+
- **variables.tf**: Defines the input variables used in the Terraform configuration.
22+
- **provider.tf**: Configures the AzureRM + AzAPI providers.
23+
- **terraform.tfvars**: Example values for the variables defined in `variables.tf`.
24+
- **output.tf**: Defines outputs such as namespace and Event Hub IDs.
25+
26+
## Variables
27+
28+
| Variable Name | Description | Type | Example Value |
29+
| --- | --- | --- | --- |
30+
| `resource_group_name` | Resource Group name to create/deploy into. | string | `"rg-analytics-dev-EH"` |
31+
| `location` | Azure region for the deployment. | string | `"eastus"` |
32+
| `eventhub_namespace_name` | Base namespace name. If suffix enabled, final is `<base>-<suffix>`. | string | `"ehns-analytics-dev"` |
33+
| `eventhub_name` | Event Hub name inside the namespace. | string | `"events"` |
34+
| `sku` | Namespace SKU (`Basic`, `Standard`, `Premium`). | string | `"Standard"` |
35+
| `capacity` | Namespace capacity (ignored for `Basic`). | number | `1` |
36+
| `partition_count` | Event Hub partitions. | number | `2` |
37+
| `message_retention` | Retention in days. | number | `1` |
38+
| `consumer_group_names` | Optional consumer groups to create. | list(string) | `["cg-default"]` |
39+
| `append_random_suffix` | Append a random suffix to avoid collisions. | bool | `true` |
40+
| `random_suffix_length` | Length of the random suffix when enabled. | number | `6` |
41+
| `tags` | Tags applied to resources. | map(string) | `{ "env": "dev" }` |
42+
43+
## Usage
44+
45+
1. Authenticate:
46+
47+
```sh
48+
az login
49+
az account show
50+
# If needed:
51+
az account set --subscription "<subscription-id-or-name>"
52+
```
53+
54+
2. Initialize:
55+
56+
```sh
57+
terraform init -upgrade
58+
```
59+
60+
3. Validate and plan:
61+
62+
```sh
63+
terraform validate
64+
terraform plan
65+
```
66+
67+
4. Apply:
68+
69+
```sh
70+
terraform apply -auto-approve
71+
```
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
data "azurerm_client_config" "current" {}
2+
3+
# Resource group creation is idempotent in ARM (PUT). This will create the RG if it doesn't exist,
4+
# or update tags if it already exists.
5+
resource "azapi_resource" "resource_group" {
6+
type = "Microsoft.Resources/resourceGroups@2022-09-01"
7+
name = var.resource_group_name
8+
location = var.location
9+
parent_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}"
10+
11+
body = jsonencode({
12+
tags = var.tags
13+
})
14+
15+
response_export_values = [
16+
"id",
17+
"name"
18+
]
19+
}
20+
21+
resource "random_string" "suffix" {
22+
length = var.random_suffix_length
23+
upper = false
24+
special = false
25+
numeric = true
26+
27+
keepers = {
28+
resource_group_name = var.resource_group_name
29+
location = var.location
30+
namespace_base = var.eventhub_namespace_name
31+
eventhub_name = var.eventhub_name
32+
sku = var.sku
33+
capacity = tostring(var.capacity)
34+
}
35+
}
36+
37+
locals {
38+
suffix = var.append_random_suffix ? random_string.suffix.result : ""
39+
40+
namespace_name = var.append_random_suffix ? "${var.eventhub_namespace_name}-${local.suffix}" : var.eventhub_namespace_name
41+
namespace_capacity = var.sku == "Basic" ? null : var.capacity
42+
}
43+
44+
resource "azurerm_eventhub_namespace" "ns" {
45+
name = local.namespace_name
46+
location = var.location
47+
resource_group_name = var.resource_group_name
48+
49+
sku = var.sku
50+
capacity = local.namespace_capacity
51+
52+
tags = var.tags
53+
54+
depends_on = [
55+
azapi_resource.resource_group
56+
]
57+
}
58+
59+
resource "azurerm_eventhub" "eh" {
60+
name = var.eventhub_name
61+
namespace_name = azurerm_eventhub_namespace.ns.name
62+
resource_group_name = var.resource_group_name
63+
64+
partition_count = var.partition_count
65+
message_retention = var.message_retention
66+
67+
depends_on = [
68+
azurerm_eventhub_namespace.ns
69+
]
70+
}
71+
72+
resource "azurerm_eventhub_consumer_group" "cg" {
73+
for_each = toset(var.consumer_group_names)
74+
75+
name = each.value
76+
namespace_name = azurerm_eventhub_namespace.ns.name
77+
eventhub_name = azurerm_eventhub.eh.name
78+
resource_group_name = var.resource_group_name
79+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
output "resource_group_id" {
2+
description = "The resource ID of the Resource Group."
3+
value = azapi_resource.resource_group.id
4+
}
5+
6+
output "eventhub_namespace_id" {
7+
description = "The resource ID of the Event Hubs namespace."
8+
value = azurerm_eventhub_namespace.ns.id
9+
}
10+
11+
output "eventhub_namespace_name" {
12+
description = "The name of the Event Hubs namespace."
13+
value = azurerm_eventhub_namespace.ns.name
14+
}
15+
16+
output "eventhub_id" {
17+
description = "The resource ID of the Event Hub."
18+
value = azurerm_eventhub.eh.id
19+
}
20+
21+
output "eventhub_name" {
22+
description = "The name of the Event Hub."
23+
value = azurerm_eventhub.eh.name
24+
}
25+
26+
output "consumer_group_ids" {
27+
description = "Map of consumer group names to IDs."
28+
value = { for k, v in azurerm_eventhub_consumer_group.cg : k => v.id }
29+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# provider.tf
2+
# This file configures the Azure provider to interact with Azure resources.
3+
# It specifies the required provider and its version, along with provider-specific configurations.
4+
5+
terraform {
6+
required_version = ">= 1.8, < 2.0"
7+
8+
required_providers {
9+
azurerm = {
10+
source = "hashicorp/azurerm"
11+
version = "~> 3.116"
12+
}
13+
14+
azapi = {
15+
source = "Azure/azapi"
16+
version = "~> 1.13"
17+
}
18+
19+
random = {
20+
source = "hashicorp/random"
21+
version = "~> 3.6"
22+
}
23+
}
24+
}
25+
26+
provider "azurerm" {
27+
features {
28+
resource_group {
29+
prevent_deletion_if_contains_resources = false
30+
}
31+
}
32+
33+
# Uses the current Azure CLI context (az login + az account set)
34+
skip_provider_registration = false
35+
}
36+
37+
provider "azapi" {
38+
# Uses the current Azure CLI context (az login + az account set)
39+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Adjust this as needed
2+
# tfvars -> values used for deployment process
3+
resource_group_name = "rg-analytics-dev-EH"
4+
location = "eastus"
5+
6+
append_random_suffix = true
7+
random_suffix_length = 6
8+
9+
eventhub_namespace_name = "ehns-analytics-dev"
10+
eventhub_name = "events"
11+
12+
sku = "Standard"
13+
capacity = 1
14+
15+
partition_count = 2
16+
message_retention = 1
17+
18+
consumer_group_names = [
19+
"cg-default"
20+
]
21+
22+
tags = {
23+
env = "dev"
24+
area = "analytics-bigdata"
25+
iac = "terraform"
26+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# variables.tf
2+
3+
variable "resource_group_name" {
4+
description = "The name of the Azure Resource Group to deploy into. This template will create the RG if it does not exist (idempotent ARM PUT)."
5+
type = string
6+
7+
validation {
8+
condition = length(trimspace(var.resource_group_name)) > 0
9+
error_message = "resource_group_name must not be empty."
10+
}
11+
}
12+
13+
variable "location" {
14+
description = "The Azure region where the Resource Group and Event Hubs resources will be created."
15+
type = string
16+
17+
validation {
18+
condition = length(trimspace(var.location)) > 0
19+
error_message = "location must not be empty."
20+
}
21+
}
22+
23+
variable "append_random_suffix" {
24+
description = "Whether to append a random suffix to globally-unique names (for example, the Event Hubs namespace) to avoid collisions."
25+
type = bool
26+
default = true
27+
}
28+
29+
variable "random_suffix_length" {
30+
description = "Length of the random suffix appended when append_random_suffix is true."
31+
type = number
32+
default = 6
33+
34+
validation {
35+
condition = var.random_suffix_length >= 4 && var.random_suffix_length <= 16
36+
error_message = "random_suffix_length must be between 4 and 16."
37+
}
38+
}
39+
40+
variable "tags" {
41+
description = "A map of tags to assign to the resources."
42+
type = map(string)
43+
default = {}
44+
}
45+
46+
variable "eventhub_namespace_name" {
47+
description = "Base name of the Event Hubs namespace. If append_random_suffix is true, the final name will be '<base>-<suffix>'."
48+
type = string
49+
50+
validation {
51+
condition = (
52+
length(trimspace(var.eventhub_namespace_name)) >= 6
53+
&& length(var.eventhub_namespace_name) <= (var.append_random_suffix ? (50 - 1 - var.random_suffix_length) : 50)
54+
&& can(regex("^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$", var.eventhub_namespace_name))
55+
)
56+
error_message = "eventhub_namespace_name must be 6-50 chars, contain only alphanumeric or '-', start/end with alphanumeric, and leave room for '-<suffix>' when append_random_suffix is true."
57+
}
58+
}
59+
60+
variable "eventhub_name" {
61+
description = "Name of the Event Hub to create within the namespace."
62+
type = string
63+
64+
validation {
65+
condition = (
66+
length(trimspace(var.eventhub_name)) > 0
67+
&& length(var.eventhub_name) <= 256
68+
&& can(regex("^[a-zA-Z0-9][a-zA-Z0-9._-]*$", var.eventhub_name))
69+
)
70+
error_message = "eventhub_name must be 1-256 chars and contain only alphanumeric, '.', '_', or '-'."
71+
}
72+
}
73+
74+
variable "sku" {
75+
description = "Event Hubs namespace SKU. Allowed: Basic, Standard, Premium."
76+
type = string
77+
default = "Standard"
78+
79+
validation {
80+
condition = contains(["Basic", "Standard", "Premium"], var.sku)
81+
error_message = "sku must be one of: Basic, Standard, Premium."
82+
}
83+
}
84+
85+
variable "capacity" {
86+
description = "Namespace capacity. For Basic, this is ignored. For Standard, it represents throughput units; for Premium, it represents messaging units."
87+
type = number
88+
default = 1
89+
90+
validation {
91+
condition = var.capacity >= 1
92+
error_message = "capacity must be >= 1."
93+
}
94+
}
95+
96+
variable "partition_count" {
97+
description = "Number of partitions for the Event Hub."
98+
type = number
99+
default = 2
100+
101+
validation {
102+
condition = var.partition_count >= 1 && var.partition_count <= 32
103+
error_message = "partition_count must be between 1 and 32."
104+
}
105+
}
106+
107+
variable "message_retention" {
108+
description = "Message retention in days for the Event Hub."
109+
type = number
110+
default = 1
111+
112+
validation {
113+
condition = var.message_retention >= 1 && var.message_retention <= 90
114+
error_message = "message_retention must be between 1 and 90."
115+
}
116+
}
117+
118+
variable "consumer_group_names" {
119+
description = "Optional list of consumer group names to create for the Event Hub."
120+
type = list(string)
121+
default = []
122+
}

0 commit comments

Comments
 (0)