Messaging & Events Security Baselines
Messaging services carry sensitive business events between distributed components. Service Bus handles enterprise messaging with queues and topics, while Event Hubs ingests high-volume event streams. Both services share similar network and identity security patterns but differ in data protection needs.
Secure Messaging Architecture
Azure Service Bus
Enterprise message broker with queues, topics/subscriptions, and sessions. Service Bus Premium runs on dedicated infrastructure with VNet integration, CMK encryption, and zone redundancy — always use Premium tier for production workloads requiring security isolation.
| Property | Value |
|---|---|
| Defender Plan | Defender for Resource Manager (control plane protection) |
| Azure Policy Built-in | Yes — Service Bus initiative (TLS, auth, network, CMK) |
| Diagnostic Logs | OperationalLogs, VNetAndIPFilteringLogs, RuntimeAuditLogs |
| Recommended SKU | Premium (VNet integration, CMK, zone redundancy, dedicated resources) |
| Key Consideration | Messages may contain PII/PHI — treat Service Bus as a data store for security classification |
Baseline Controls
| Domain | Feature | Supported | Default | Responsibility |
|---|---|---|---|---|
| NS | Private Endpoint | ✓ | Manual | Customer |
| NS | Disable Public Access | ✓ | Manual | Customer |
| NS | TLS 1.2 Minimum | ✓ | Default | Microsoft |
| IM | Entra ID RBAC | ✓ | Manual | Customer |
| IM | Disable SAS/Key Auth | ✓ | Manual | Customer |
| DP | Encryption at Rest | ✓ | Default | Microsoft |
| DP | CMK (Premium) | ✓ | Manual | Customer |
| LT | Diagnostic Settings | ✓ | Manual | Customer |
| BR | Geo-Disaster Recovery | ✓ | Manual | Customer |
Private Endpoint
Disable Public Access
TLS 1.2 Minimum
Entra ID RBAC
Disable SAS/Key Auth
Encryption at Rest
CMK (Premium)
Diagnostic Settings
Geo-Disaster Recovery
Terraform: Hardened Service Bus Premium
resource "azurerm_servicebus_namespace" "main" {
name = "sb-messaging-prod"
resource_group_name = azurerm_resource_group.messaging.name
location = azurerm_resource_group.messaging.location
sku = "Premium" # Required for VNet, CMK, zone redundancy
capacity = 1 # Messaging units
premium_messaging_partitions = 1
zone_redundant = true # BR: Zone redundancy
# NS-2: Disable public network access
public_network_access_enabled = false
# NS-8: TLS 1.2 minimum
minimum_tls_version = "1.2"
# IM-1: Disable SAS/key-based auth — Entra only
local_auth_enabled = false
# DP-5: CMK encryption
customer_managed_key {
key_vault_key_id = azurerm_key_vault_key.sb_cmk.id
identity_id = azurerm_user_assigned_identity.sb.id
infrastructure_encryption_enabled = true # Double encryption
}
identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.sb.id]
}
}
# Queue with dead letter monitoring
resource "azurerm_servicebus_queue" "orders" {
name = "orders"
namespace_id = azurerm_servicebus_namespace.main.id
max_delivery_count = 10
dead_lettering_on_message_expiration = true
requires_duplicate_detection = true
duplicate_detection_history_time_window = "PT10M"
# Messages auto-expire after 14 days (data retention limit)
default_message_ttl = "P14D"
}
# Topic with subscription filtering
resource "azurerm_servicebus_topic" "events" {
name = "business-events"
namespace_id = azurerm_servicebus_namespace.main.id
max_size_in_megabytes = 5120
requires_duplicate_detection = true
default_message_ttl = "P7D"
}
# NS-2: Private Endpoint
resource "azurerm_private_endpoint" "sb" {
name = "pe-sb-prod"
location = azurerm_resource_group.messaging.location
resource_group_name = azurerm_resource_group.messaging.name
subnet_id = azurerm_subnet.messaging.id
private_service_connection {
name = "psc-sb-prod"
private_connection_resource_id = azurerm_servicebus_namespace.main.id
subresource_names = ["namespace"]
is_manual_connection = false
}
private_dns_zone_group {
name = "pdz-sb"
private_dns_zone_ids = [azurerm_private_dns_zone.servicebus.id]
}
}
# IM: RBAC role assignment for sending application
resource "azurerm_role_assignment" "app_sb_sender" {
scope = azurerm_servicebus_queue.orders.id
role_definition_name = "Azure Service Bus Data Sender"
principal_id = azurerm_linux_web_app.api.identity[0].principal_id
}
resource "azurerm_role_assignment" "func_sb_receiver" {
scope = azurerm_servicebus_queue.orders.id
role_definition_name = "Azure Service Bus Data Receiver"
principal_id = azurerm_linux_function_app.processor.identity[0].principal_id
}
# LT-3: Diagnostic Settings
resource "azurerm_monitor_diagnostic_setting" "sb" {
name = "diag-sb-prod"
target_resource_id = azurerm_servicebus_namespace.main.id
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
enabled_log { category = "OperationalLogs" }
enabled_log { category = "VNetAndIPFilteringLogs" }
enabled_log { category = "RuntimeAuditLogs" }
metric { category = "AllMetrics" }
}resource "azurerm_servicebus_namespace" "main" {
name = "sb-messaging-prod"
resource_group_name = azurerm_resource_group.messaging.name
location = azurerm_resource_group.messaging.location
sku = "Premium" # Required for VNet, CMK, zone redundancy
capacity = 1 # Messaging units
premium_messaging_partitions = 1
zone_redundant = true # BR: Zone redundancy
# NS-2: Disable public network access
public_network_access_enabled = false
# NS-8: TLS 1.2 minimum
minimum_tls_version = "1.2"
# IM-1: Disable SAS/key-based auth — Entra only
local_auth_enabled = false
# DP-5: CMK encryption
customer_managed_key {
key_vault_key_id = azurerm_key_vault_key.sb_cmk.id
identity_id = azurerm_user_assigned_identity.sb.id
infrastructure_encryption_enabled = true # Double encryption
}
identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.sb.id]
}
}
# Queue with dead letter monitoring
resource "azurerm_servicebus_queue" "orders" {
name = "orders"
namespace_id = azurerm_servicebus_namespace.main.id
max_delivery_count = 10
dead_lettering_on_message_expiration = true
requires_duplicate_detection = true
duplicate_detection_history_time_window = "PT10M"
# Messages auto-expire after 14 days (data retention limit)
default_message_ttl = "P14D"
}
# Topic with subscription filtering
resource "azurerm_servicebus_topic" "events" {
name = "business-events"
namespace_id = azurerm_servicebus_namespace.main.id
max_size_in_megabytes = 5120
requires_duplicate_detection = true
default_message_ttl = "P7D"
}
# NS-2: Private Endpoint
resource "azurerm_private_endpoint" "sb" {
name = "pe-sb-prod"
location = azurerm_resource_group.messaging.location
resource_group_name = azurerm_resource_group.messaging.name
subnet_id = azurerm_subnet.messaging.id
private_service_connection {
name = "psc-sb-prod"
private_connection_resource_id = azurerm_servicebus_namespace.main.id
subresource_names = ["namespace"]
is_manual_connection = false
}
private_dns_zone_group {
name = "pdz-sb"
private_dns_zone_ids = [azurerm_private_dns_zone.servicebus.id]
}
}
# IM: RBAC role assignment for sending application
resource "azurerm_role_assignment" "app_sb_sender" {
scope = azurerm_servicebus_queue.orders.id
role_definition_name = "Azure Service Bus Data Sender"
principal_id = azurerm_linux_web_app.api.identity[0].principal_id
}
resource "azurerm_role_assignment" "func_sb_receiver" {
scope = azurerm_servicebus_queue.orders.id
role_definition_name = "Azure Service Bus Data Receiver"
principal_id = azurerm_linux_function_app.processor.identity[0].principal_id
}
# LT-3: Diagnostic Settings
resource "azurerm_monitor_diagnostic_setting" "sb" {
name = "diag-sb-prod"
target_resource_id = azurerm_servicebus_namespace.main.id
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
enabled_log { category = "OperationalLogs" }
enabled_log { category = "VNetAndIPFilteringLogs" }
enabled_log { category = "RuntimeAuditLogs" }
metric { category = "AllMetrics" }
}Azure Portal Instructions
Step-by-step instructions for configuring each Service Bus control through the Azure Portal.
NS-1 — Service Endpoints
- Navigate to your Service Bus namespace in the Azure Portal
- In the left menu under Settings, select Networking
- Select the Selected networks option
- Under Virtual networks, click + Add existing virtual network
- Select the VNet and subnet to allow access from
- Under Firewall, optionally add allowed client IP addresses
- Click Save
NS-2 — Private Endpoints
- Navigate to your Service Bus namespace in the Azure Portal
- In the left menu under Settings, select Networking
- Select the Private endpoint connections tab
- Click + Private endpoint to create a new private endpoint
- Select the VNet, subnet, and configure the private DNS zone (
privatelink.servicebus.windows.net) - Go back to the Public access tab and set it to Disabled
- Click Save
IM-1 — Entra ID Authentication
- Navigate to your Service Bus namespace in the Azure Portal
- In the left menu, select Access control (IAM)
- Click + Add role assignment
- Assign "Azure Service Bus Data Sender" and/or "Azure Service Bus Data Receiver" roles to users, groups, or managed identities
- To disable SAS/local authentication, go to Settings > Shared access policies
- Note: Disabling local auth requires ARM/CLI (
set disableLocalAuth=true) — the portal does not yet expose this toggle directly
IM-3 — Managed Identities
- On the application side (e.g., App Service or Function App), enable Managed Identity under Settings > Identity
- Navigate to your Service Bus namespace in the Azure Portal
- Go to Access control (IAM)
- Click + Add role assignment
- Assign "Azure Service Bus Data Sender" or "Azure Service Bus Data Receiver" to the application managed identity
- Update the application code to use
DefaultAzureCredentialinstead of connection strings
DP-3 — Data in Transit Encryption
This feature is managed by Microsoft and enabled by default. All AMQP and HTTPS connections use TLS 1.2 automatically. No portal configuration is required.
DP-4 — Data at Rest Encryption
This feature is managed by Microsoft and enabled by default. No portal configuration is required — all messages, queues, and topics are encrypted at rest.
DP-5 — Encryption with CMK
- CMK is available on Premium tier only
- Navigate to your Service Bus namespace (Premium) in the Azure Portal
- In the left menu under Settings, select Encryption
- Select Customer-managed key
- Select the Key Vault and encryption key
- Choose or configure the managed identity for Key Vault access
- Click Save
- Note: For new namespaces, CMK can be configured during the creation wizard on the Encryption tab
LT-3 — Diagnostic Logging
- Navigate to your Service Bus namespace in the Azure Portal
- In the left menu under Monitoring, select Diagnostic settings
- Click + Add diagnostic setting
- Give the setting a name
- Select log categories: OperationalLogs, VNetAndIPFilteringLogs, RuntimeAuditLogs
- Select the destination: Send to Log Analytics workspace
- Select your Log Analytics workspace
- Click Save
LT-1 — Defender for Resource Manager
- Navigate to Microsoft Defender for Cloud in the Azure Portal
- In the left menu, select Environment settings
- Select your subscription
- Under Cloud Workload Protection (CWP), find the Resource Manager row
- Toggle the plan to On
- Click Save — this monitors control plane operations across all resources including Service Bus
SAS Keys Are Legacy
local_auth_enabled = false and use Managed Identity with
Azure Service Bus Data Sender/Receiver RBAC roles instead. Split sender and receiver roles
per least privilege.
Service Bus Security Design Patterns
Dead Letter Monitoring
Monitor dead letter queues as a security signal. Unexpected message failures may indicate poisoned messages, format injection attempts, or deserialization attacks. Alert when DLQ count exceeds threshold.
Message TTL as Data Retention
Set default_message_ttl aligned to your data retention policy. Messages containing
PII should not persist indefinitely. Expired messages can be routed to dead letter for
audit-compliant disposal.
Separate Send/Receive Identities
Use distinct Managed Identities for producers and consumers with separate RBAC assignments. A compromised consumer should not be able to inject messages, and a compromised producer should not be able to read them.
Message Payload Encryption
For highly sensitive data (PII, PHI, financial), encrypt the message payload at the application layer before sending. Service Bus encryption at rest protects the broker storage, but application-layer encryption protects against broker-level compromise.
Azure Event Hubs
Big data streaming platform and event ingestion service capable of millions of events per second. Event Hubs shares many security patterns with Service Bus as both use the same underlying AMQP infrastructure, but adds Kafka compatibility and Capture for long-term archival.
| Property | Value |
|---|---|
| Defender Plan | Defender for Resource Manager (control plane) |
| Azure Policy Built-in | Yes — Event Hubs initiative |
| Diagnostic Logs | OperationalLogs, AutoScaleLogs, KafkaCoordinatorLogs, RuntimeAuditLogs |
| Recommended SKU | Premium or Dedicated (VNet integration, CMK) |
Baseline Controls
| Domain | Feature | Supported | Default | Responsibility |
|---|---|---|---|---|
| NS | Private Endpoint | ✓ | Manual | Customer |
| NS | Disable Public Access | ✓ | Manual | Customer |
| IM | Entra ID RBAC | ✓ | Manual | Customer |
| IM | Disable SAS Auth | ✓ | Manual | Customer |
| DP | Encryption at Rest | ✓ | Default | Microsoft |
| DP | CMK (Premium/Ded) | ✓ | Manual | Customer |
| LT | Diagnostic Settings | ✓ | Manual | Customer |
| BR | Event Capture | ✓ | Manual | Customer |
Private Endpoint
Disable Public Access
Entra ID RBAC
Disable SAS Auth
Encryption at Rest
CMK (Premium/Ded)
Diagnostic Settings
Event Capture
Terraform: Hardened Event Hubs Premium
resource "azurerm_eventhub_namespace" "main" {
name = "evhns-telemetry-prod"
resource_group_name = azurerm_resource_group.messaging.name
location = azurerm_resource_group.messaging.location
sku = "Premium"
capacity = 1
zone_redundant = true
# NS-2: Disable public access
public_network_access_enabled = false
# NS-8: TLS 1.2 minimum
minimum_tls_version = "1.2"
# IM-1: Disable SAS/key auth
local_authentication_enabled = false
identity {
type = "SystemAssigned"
}
}
resource "azurerm_eventhub" "telemetry" {
name = "telemetry-events"
namespace_id = azurerm_eventhub_namespace.main.id
partition_count = 8
message_retention = 7 # Retention days
# BR: Capture events to immutable storage
capture_description {
enabled = true
encoding = "Avro"
interval_in_seconds = 300
size_limit_in_bytes = 314572800
destination {
name = "EventHubArchive"
archive_name_format = "{Namespace}/{EventHub}/{PartitionId}/{Year}/{Month}/{Day}/{Hour}/{Minute}/{Second}"
blob_container_name = azurerm_storage_container.capture.name
storage_account_id = azurerm_storage_account.archive.id
}
}
}
# NS-2: Private Endpoint
resource "azurerm_private_endpoint" "eh" {
name = "pe-evhns-prod"
location = azurerm_resource_group.messaging.location
resource_group_name = azurerm_resource_group.messaging.name
subnet_id = azurerm_subnet.messaging.id
private_service_connection {
name = "psc-evhns-prod"
private_connection_resource_id = azurerm_eventhub_namespace.main.id
subresource_names = ["namespace"]
is_manual_connection = false
}
}
# IM: Least privilege RBAC
resource "azurerm_role_assignment" "producer_eh" {
scope = azurerm_eventhub.telemetry.id
role_definition_name = "Azure Event Hubs Data Sender"
principal_id = azurerm_linux_web_app.telemetry_api.identity[0].principal_id
}
resource "azurerm_role_assignment" "consumer_eh" {
scope = azurerm_eventhub.telemetry.id
role_definition_name = "Azure Event Hubs Data Receiver"
principal_id = azurerm_linux_function_app.telemetry_processor.identity[0].principal_id
}resource "azurerm_eventhub_namespace" "main" {
name = "evhns-telemetry-prod"
resource_group_name = azurerm_resource_group.messaging.name
location = azurerm_resource_group.messaging.location
sku = "Premium"
capacity = 1
zone_redundant = true
# NS-2: Disable public access
public_network_access_enabled = false
# NS-8: TLS 1.2 minimum
minimum_tls_version = "1.2"
# IM-1: Disable SAS/key auth
local_authentication_enabled = false
identity {
type = "SystemAssigned"
}
}
resource "azurerm_eventhub" "telemetry" {
name = "telemetry-events"
namespace_id = azurerm_eventhub_namespace.main.id
partition_count = 8
message_retention = 7 # Retention days
# BR: Capture events to immutable storage
capture_description {
enabled = true
encoding = "Avro"
interval_in_seconds = 300
size_limit_in_bytes = 314572800
destination {
name = "EventHubArchive"
archive_name_format = "{Namespace}/{EventHub}/{PartitionId}/{Year}/{Month}/{Day}/{Hour}/{Minute}/{Second}"
blob_container_name = azurerm_storage_container.capture.name
storage_account_id = azurerm_storage_account.archive.id
}
}
}
# NS-2: Private Endpoint
resource "azurerm_private_endpoint" "eh" {
name = "pe-evhns-prod"
location = azurerm_resource_group.messaging.location
resource_group_name = azurerm_resource_group.messaging.name
subnet_id = azurerm_subnet.messaging.id
private_service_connection {
name = "psc-evhns-prod"
private_connection_resource_id = azurerm_eventhub_namespace.main.id
subresource_names = ["namespace"]
is_manual_connection = false
}
}
# IM: Least privilege RBAC
resource "azurerm_role_assignment" "producer_eh" {
scope = azurerm_eventhub.telemetry.id
role_definition_name = "Azure Event Hubs Data Sender"
principal_id = azurerm_linux_web_app.telemetry_api.identity[0].principal_id
}
resource "azurerm_role_assignment" "consumer_eh" {
scope = azurerm_eventhub.telemetry.id
role_definition_name = "Azure Event Hubs Data Receiver"
principal_id = azurerm_linux_function_app.telemetry_processor.identity[0].principal_id
}Azure Portal Instructions
Step-by-step instructions for configuring each Event Hubs control through the Azure Portal.
NS-2 — Private Endpoints
- Navigate to your Event Hubs namespace in the Azure Portal
- In the left menu under Settings, select Networking
- Select the Private endpoint connections tab
- Click + Private endpoint to create a new private endpoint
- Select the VNet, subnet, and configure the private DNS zone (
privatelink.servicebus.windows.net) - Go back to the Public access tab and set it to Disabled
- Click Save
IM-1 — Entra ID Authentication
- Navigate to your Event Hubs namespace in the Azure Portal
- In the left menu, select Access control (IAM)
- Click + Add role assignment
- Assign "Azure Event Hubs Data Sender" and/or "Azure Event Hubs Data Receiver" roles to users, groups, or managed identities
- To disable SAS/local authentication, this requires ARM/CLI (
set disableLocalAuth=true)
IM-3 — Managed Identities
- On the application side (e.g., App Service or Function App), enable Managed Identity under Settings > Identity
- Navigate to your Event Hubs namespace in the Azure Portal
- Go to Access control (IAM)
- Click + Add role assignment
- Assign "Azure Event Hubs Data Sender" or "Azure Event Hubs Data Receiver" to the application managed identity
- Update the application to use
DefaultAzureCredentialinstead of connection strings
DP-3 — Data in Transit Encryption
This feature is managed by Microsoft and enabled by default. TLS 1.2 is enforced for all AMQP and HTTPS connections automatically. No portal configuration is required.
DP-4 — Data at Rest Encryption
This feature is managed by Microsoft and enabled by default. No portal configuration is required — all event data including captured events is encrypted at rest.
DP-5 — Encryption with CMK
- CMK is available on Premium and Dedicated tiers only
- Navigate to your Event Hubs namespace (Premium/Dedicated) in the Azure Portal
- In the left menu under Settings, select Encryption
- Select Customer-managed key
- Select the Key Vault and encryption key
- Choose or configure the managed identity for Key Vault access
- Click Save
- Note: For new namespaces, CMK can be configured during creation on the Encryption tab
LT-3 — Diagnostic Logging
- Navigate to your Event Hubs namespace in the Azure Portal
- In the left menu under Monitoring, select Diagnostic settings
- Click + Add diagnostic setting
- Give the setting a name
- Select log categories: OperationalLogs, ArchiveLogs, AutoScaleLogs, KafkaUserErrorLogs
- Select the destination: Send to Log Analytics workspace
- Select your Log Analytics workspace
- Click Save
Capture Security