Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions infrastructure/modules/relay-hybrid-connection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# relay-hybrid-connection

Deploy an [Azure Relay Hybrid Connection](https://learn.microsoft.com/en-us/azure/azure-relay/relay-hybrid-connections-protocol) to enable secure, bi-directional communication between on-premises applications and Azure services through an existing Azure Relay namespace.

This module creates:
- Azure Relay Hybrid Connection
- Optional authorization rules with configurable permissions (Listen, Send, Manage)

**Note:** This module requires an existing Azure Relay namespace. Use the `relay-namespace` module to create the namespace first.

## Terraform documentation
For the list of inputs, outputs, resources... check the [terraform module documentation](tfdocs.md).

## Usage

### Basic usage (without authorization rules)
```hcl
module "relay_hybrid_connection" {
source = "../../../dtos-devops-templates/infrastructure/modules/relay-hybrid-connection"

name = "hc-${var.application}-${var.environment}"
relay_namespace_name = module.relay_namespace.name
resource_group_name = var.resource_group_name
}
```

### With authorization rules
```hcl
module "relay_hybrid_connection" {
source = "../../../dtos-devops-templates/infrastructure/modules/relay-hybrid-connection"

name = "hc-${var.application}-${var.environment}"
relay_namespace_name = module.relay_namespace.name
resource_group_name = var.resource_group_name
requires_client_authorization = true
user_metadata = "Application hybrid connection"

authorization_rules = {
"listen-rule" = {
listen = true
send = false
manage = false
}
"send-rule" = {
listen = false
send = true
manage = false
}
"manage-rule" = {
listen = true
send = true
manage = true
}
}
}
```

### Complete example with namespace
```hcl
# Create the relay namespace first
module "relay_namespace" {
source = "../../../dtos-devops-templates/infrastructure/modules/relay-namespace"

name = "relay-${var.application}-${var.environment}"
resource_group_name = var.resource_group_name
location = var.location
log_analytics_workspace_id = data.terraform_remote_state.audit.outputs.log_analytics_workspace_id

tags = var.tags
}

# Create hybrid connection with authorization rules
module "relay_hybrid_connection" {
source = "../../../dtos-devops-templates/infrastructure/modules/relay-hybrid-connection"

name = "hc-${var.application}-${var.environment}"
relay_namespace_name = module.relay_namespace.name
resource_group_name = var.resource_group_name

authorization_rules = {
"app-listener" = {
listen = true
}
"app-sender" = {
send = true
}
}
}
```

## Naming constraints

The Azure Relay Hybrid Connection name must follow these rules:

| Constraint | Requirement |
|------------|-------------|
| Length | 1-260 characters |
| Start | Must start with a letter or number |
| End | Must end with a letter or number |
| Characters | Letters, numbers, hyphens, underscores, and periods |

Authorization rule names follow similar constraints (1-256 characters).

## Authorization rules

Authorization rules support three permission types:

| Permission | Description |
|------------|-------------|
| `listen` | Allows receiving messages from the hybrid connection |
| `send` | Allows sending messages to the hybrid connection |
| `manage` | Allows managing the hybrid connection (requires both `listen` and `send` to be `true`) |

**Note:** When `manage = true`, both `listen` and `send` must also be set to `true`. The module validates this constraint.

## Outputs

| Output | Description |
|--------|-------------|
| `name` | The name of the Hybrid Connection |
| `id` | The resource ID of the Hybrid Connection |
| `authorization_rule_ids` | Map of authorization rule names to their IDs |
| `authorization_rule_primary_keys` | Map of authorization rule names to their primary keys (sensitive) |
| `authorization_rule_secondary_keys` | Map of authorization rule names to their secondary keys (sensitive) |
| `authorization_rule_primary_connection_strings` | Map of authorization rule names to their primary connection strings (sensitive) |
| `authorization_rule_secondary_connection_strings` | Map of authorization rule names to their secondary connection strings (sensitive) |
26 changes: 26 additions & 0 deletions infrastructure/modules/relay-hybrid-connection/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* --------------------------------------------------------------------------------------------------
Azure Relay Hybrid Connection
-------------------------------------------------------------------------------------------------- */
resource "azurerm_relay_hybrid_connection" "hybrid_connection" {
name = var.name
relay_namespace_name = var.relay_namespace_name
resource_group_name = var.resource_group_name
requires_client_authorization = var.requires_client_authorization
user_metadata = var.user_metadata
}

/* --------------------------------------------------------------------------------------------------
Azure Relay Hybrid Connection Authorization Rules
-------------------------------------------------------------------------------------------------- */
resource "azurerm_relay_hybrid_connection_authorization_rule" "auth_rule" {
for_each = var.authorization_rules

name = each.key
hybrid_connection_name = azurerm_relay_hybrid_connection.hybrid_connection.name
namespace_name = var.relay_namespace_name
resource_group_name = var.resource_group_name

listen = each.value.listen
send = each.value.send
manage = each.value.manage
}
38 changes: 38 additions & 0 deletions infrastructure/modules/relay-hybrid-connection/output.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
output "id" {
description = "The ID of the Relay Hybrid Connection."
value = azurerm_relay_hybrid_connection.hybrid_connection.id
}

output "name" {
description = "The name of the Relay Hybrid Connection."
value = azurerm_relay_hybrid_connection.hybrid_connection.name
}

output "authorization_rule_ids" {
description = "A map of authorization rule names to their IDs."
value = { for k, v in azurerm_relay_hybrid_connection_authorization_rule.auth_rule : k => v.id }
}

output "authorization_rule_primary_keys" {
description = "A map of authorization rule names to their primary keys."
value = { for k, v in azurerm_relay_hybrid_connection_authorization_rule.auth_rule : k => v.primary_key }
sensitive = true
}

output "authorization_rule_secondary_keys" {
description = "A map of authorization rule names to their secondary keys."
value = { for k, v in azurerm_relay_hybrid_connection_authorization_rule.auth_rule : k => v.secondary_key }
sensitive = true
}

output "authorization_rule_primary_connection_strings" {
description = "A map of authorization rule names to their primary connection strings."
value = { for k, v in azurerm_relay_hybrid_connection_authorization_rule.auth_rule : k => v.primary_connection_string }
sensitive = true
}

output "authorization_rule_secondary_connection_strings" {
description = "A map of authorization rule names to their secondary connection strings."
value = { for k, v in azurerm_relay_hybrid_connection_authorization_rule.auth_rule : k => v.secondary_connection_string }
sensitive = true
}
97 changes: 97 additions & 0 deletions infrastructure/modules/relay-hybrid-connection/tfdocs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Module documentation

## Required Inputs

The following input variables are required:

### <a name="input_name"></a> [name](#input\_name)

Description: The name of the Azure Relay Hybrid Connection.

Type: `string`

### <a name="input_relay_namespace_name"></a> [relay\_namespace\_name](#input\_relay\_namespace\_name)

Description: The name of the Azure Relay namespace in which to create the Hybrid Connection.

Type: `string`

### <a name="input_resource_group_name"></a> [resource\_group\_name](#input\_resource\_group\_name)

Description: The name of the resource group containing the Azure Relay namespace.

Type: `string`

## Optional Inputs

The following input variables are optional (have default values):

### <a name="input_authorization_rules"></a> [authorization\_rules](#input\_authorization\_rules)

Description: A map of authorization rules to create for the Hybrid Connection.

Type:

```hcl
map(object({
listen = optional(bool, false)
send = optional(bool, false)
manage = optional(bool, false)
}))
```

Default: `{}`

### <a name="input_requires_client_authorization"></a> [requires\_client\_authorization](#input\_requires\_client\_authorization)

Description: Whether client authorization is required for this Hybrid Connection.

Type: `bool`

Default: `true`

### <a name="input_user_metadata"></a> [user\_metadata](#input\_user\_metadata)

Description: User metadata string for the Hybrid Connection.

Type: `string`

Default: `null`

## Outputs

The following outputs are exported:

### <a name="output_authorization_rule_ids"></a> [authorization\_rule\_ids](#output\_authorization\_rule\_ids)

Description: A map of authorization rule names to their IDs.

### <a name="output_authorization_rule_primary_connection_strings"></a> [authorization\_rule\_primary\_connection\_strings](#output\_authorization\_rule\_primary\_connection\_strings)

Description: A map of authorization rule names to their primary connection strings.

### <a name="output_authorization_rule_primary_keys"></a> [authorization\_rule\_primary\_keys](#output\_authorization\_rule\_primary\_keys)

Description: A map of authorization rule names to their primary keys.

### <a name="output_authorization_rule_secondary_connection_strings"></a> [authorization\_rule\_secondary\_connection\_strings](#output\_authorization\_rule\_secondary\_connection\_strings)

Description: A map of authorization rule names to their secondary connection strings.

### <a name="output_authorization_rule_secondary_keys"></a> [authorization\_rule\_secondary\_keys](#output\_authorization\_rule\_secondary\_keys)

Description: A map of authorization rule names to their secondary keys.

### <a name="output_id"></a> [id](#output\_id)

Description: The ID of the Relay Hybrid Connection.

### <a name="output_name"></a> [name](#output\_name)

Description: The name of the Relay Hybrid Connection.
## Resources

The following resources are used by this module:

- [azurerm_relay_hybrid_connection.hybrid_connection](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/relay_hybrid_connection) (resource)
- [azurerm_relay_hybrid_connection_authorization_rule.auth_rule](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/relay_hybrid_connection_authorization_rule) (resource)
56 changes: 56 additions & 0 deletions infrastructure/modules/relay-hybrid-connection/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
variable "name" {
description = "The name of the Azure Relay Hybrid Connection."
type = string
validation {
condition = can(regex("^[a-zA-Z0-9]([a-zA-Z0-9-._]{0,258}[a-zA-Z0-9])?$", var.name))
error_message = "The hybrid connection name must be 1-260 characters, start and end with an alphanumeric character, and can only contain letters, numbers, hyphens, underscores, and periods."
}
}

variable "relay_namespace_name" {
description = "The name of the Azure Relay namespace in which to create the Hybrid Connection."
type = string
}

variable "resource_group_name" {
description = "The name of the resource group containing the Azure Relay namespace."
type = string
}

variable "requires_client_authorization" {
description = "Whether client authorization is required for this Hybrid Connection."
type = bool
default = true
}

variable "user_metadata" {
description = "User metadata string for the Hybrid Connection."
type = string
default = null
}

variable "authorization_rules" {
description = "A map of authorization rules to create for the Hybrid Connection."
type = map(object({
listen = optional(bool, false)
send = optional(bool, false)
manage = optional(bool, false)
}))
default = {}

validation {
condition = alltrue([
for name, rule in var.authorization_rules :
can(regex("^[a-zA-Z0-9]([a-zA-Z0-9-._]{0,254}[a-zA-Z0-9])?$", name))
])
error_message = "Authorization rule names must be 1-256 characters, start and end with an alphanumeric character, and can only contain letters, numbers, hyphens, underscores, and periods."
}

validation {
condition = alltrue([
for name, rule in var.authorization_rules :
rule.manage == false || (rule.listen == true && rule.send == true)
])
error_message = "When 'manage' is true, both 'listen' and 'send' must also be true."
}
}
2 changes: 1 addition & 1 deletion infrastructure/modules/relay-namespace/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This module creates:
- Optional private endpoint for secure connectivity
- Diagnostic settings for monitoring and logging

**Note:** This module only creates the namespace. Hybrid connections and authorization rules should be created separately using dedicated modules to support the pattern of one namespace with many hybrid connections.
**Note:** This module only creates the namespace. Hybrid connections and authorization rules should be created separately using the [relay-hybrid-connection](../relay-hybrid-connection/README.md) module to support the pattern of one namespace with many hybrid connections.

## Private DNS Zone

Expand Down
Loading