Skip to content
Draft
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
100 changes: 100 additions & 0 deletions datastore/redis/azure_managed_redis/1.0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Azure Managed Redis Module v1.0

## Overview

This module provisions Azure Managed Redis, Microsoft's next-generation Redis service built on Redis 7.4+. It provides enhanced performance, enterprise security features, and automatic high availability. All connections are secured via Private Endpoint for VNet-only access.

## Environment as Dimension

The module is environment-aware through:
- Resource naming includes environment unique identifier for isolation
- Cloud tags from environment applied to all resources
- Private DNS zone and endpoint are unique per environment

## Resources Created

- Azure Managed Redis instance (Balanced tier SKUs)
- Private Endpoint for secure VNet access
- Private DNS Zone (`privatelink.redisenterprise.cache.azure.net`)
- Private DNS Zone VNet Link
- Private DNS A Record for endpoint resolution

## Network Integration

This module consumes network resources from an Azure network module:
- Automatically uses database subnet when available for better isolation
- Falls back to private subnet if database subnet is not configured
- Public network access is disabled by default
- All traffic flows through Private Endpoint within the VNet

## Key Features

### Size Options

| Size | SKU | Memory | Use Case |
|------|-----|--------|----------|
| small | Balanced_B1 | ~1GB | Development/Testing |
| medium | Balanced_B5 | ~6GB | Small production workloads |
| large | Balanced_B50 | ~30GB | High-traffic applications |
| xlarge | Balanced_B100 | ~60GB | Enterprise workloads |

### Clustering Modes

- **standard** (OSS Cluster): Recommended for new applications, uses native Redis clustering
- **legacy_compatible** (Enterprise Cluster): For migration from older Redis deployments

### Security Features

- TLS encryption enforced (Encrypted client protocol)
- Public network access disabled
- Private Endpoint for VNet-only connectivity
- Optional password authentication (enabled by default)
- Private DNS resolution within VNet

## Differences from Azure Cache for Redis

Azure Managed Redis is the next-generation offering with key differences:

| Feature | Azure Managed Redis | Azure Cache for Redis |
|---------|--------------------|-----------------------|
| Redis Version | 7.4+ | Up to 6.x |
| Port | 10000 | 6380 (TLS) |
| SKU Types | Balanced, Memory Optimized, Compute Optimized | Basic, Standard, Premium |
| Network | Private Endpoint only | VNet injection or Public |
| Availability | Built-in HA | Depends on tier |

## Security Considerations

- All connections use TLS encryption
- Public network access is disabled
- Access limited to VNet through Private Endpoint
- Access keys marked as sensitive in outputs
- Private DNS ensures hostname resolution stays within VNet

## Subnet Selection Logic

The module intelligently selects subnets:
1. **Database Subnet Priority**: Uses general database subnet if available
2. **Private Subnet Fallback**: Uses first private subnet otherwise
3. **Private Endpoint**: Deployed in selected subnet for secure access

## Connection Details

Applications connect using:
- **Host**: `{cache-name}.privatelink.redisenterprise.cache.azure.net`
- **Port**: `10000`
- **Protocol**: `rediss://` (TLS-encrypted)

## Terraform State Import

To import existing Azure Managed Redis resources into Terraform state:

```bash
# Import the Redis instance
terraform import azurerm_managed_redis.main /subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Cache/redisenterprise/{cache-name}

# Import the Private Endpoint
terraform import azurerm_private_endpoint.redis /subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Network/privateEndpoints/{endpoint-name}
```

Note: This imports resource configuration only, not the cached data.
104 changes: 104 additions & 0 deletions datastore/redis/azure_managed_redis/1.0/facets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
intent: redis
flavor: azure_managed_redis
version: '1.0'
clouds:
- azure
description: |
Azure Managed Redis - Next-generation Redis on Azure with Redis 7.4+,
enhanced performance, and enterprise security. Uses Private Endpoint
for secure VNet-only access.

spec:
title: Azure Managed Redis Configuration
description: Configure Azure Managed Redis with developer-friendly settings
type: object
properties:
size:
type: string
title: Cache Size
description: Select the appropriate cache size for your workload
enum:
- small
- medium
- large
- xlarge
default: small
x-ui-help: |
Size guidelines based on workload:
- small: Development/Testing (~1GB, Balanced_B1)
- medium: Small production workloads (~6GB, Balanced_B5)
- large: High-traffic applications (~30GB, Balanced_B50)
- xlarge: Enterprise workloads (~60GB, Balanced_B100)
advanced:
type: object
title: Advanced Settings
x-ui-toggle: true
description: Advanced Redis configuration options
properties:
clustering_mode:
type: string
title: Clustering Mode
description: Redis clustering policy for data distribution
enum:
- standard
- legacy_compatible
default: standard
x-ui-help: |
- standard: OSS Cluster mode (recommended for new applications)
- legacy_compatible: Enterprise Cluster mode (for migration from older Redis)
enable_password_auth:
type: boolean
title: Enable Password Authentication
description: Enable access key authentication for Redis connections
default: true
required:
- size
x-ui-order:
- size
- advanced

inputs:
azure_provider:
type: '@facets/azure_cloud_account'
optional: false
displayName: Azure Cloud Account
description: Azure cloud provider configuration
providers:
- azurerm
network_details:
type: '@facets/azure-network-details'
optional: false
displayName: Azure Network Details
description: Azure VNet and subnet configuration for Private Endpoint

outputs:
default:
type: '@facets/redis'
title: Azure Managed Redis
secrets:
- interfaces.cluster.auth_token
- interfaces.cluster.connection_string

imports:
- name: redis_resource_id
resource_address: azurerm_managed_redis.main
required: false
- name: private_endpoint_resource_id
resource_address: azurerm_private_endpoint.redis
required: false

iac:
validated_files:
- main.tf
- locals.tf
- variables.tf
- outputs.tf
- versions.tf

sample:
kind: redis
flavor: azure_managed_redis
version: '1.0'
disabled: true
spec:
size: small
65 changes: 65 additions & 0 deletions datastore/redis/azure_managed_redis/1.0/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
locals {
# Spec configuration
spec = lookup(var.instance, "spec", {})

# Tags
metadata = lookup(var.instance, "metadata", {})
user_defined_tags = lookup(local.metadata, "tags", {})
tags = merge(local.user_defined_tags, var.environment.cloud_tags)

# Network configuration from @facets/azure-network-details
resource_group_name = var.inputs.network_details.attributes.resource_group_name
region = var.inputs.network_details.attributes.region

# Use database subnet if available, otherwise fall back to private subnet
subnet_id = coalesce(
var.inputs.network_details.attributes.database_general_subnet_id,
var.inputs.network_details.attributes.private_subnet_ids[0]
)

# VNet ID for Private DNS Zone linking
vnet_id = var.inputs.network_details.attributes.vnet_id

# Resource naming - inline approach (same as azure_cache_custom)
cache_name = "${var.instance_name}-${var.environment.unique_name}"

# Size to SKU mapping - Developer-friendly names to Azure Managed Redis SKUs
# Balanced SKUs provide good mix of compute and memory
size_to_sku = {
small = "Balanced_B1" # ~1GB, suitable for dev/test
medium = "Balanced_B5" # ~6GB, small production
large = "Balanced_B50" # ~30GB, high-traffic
xlarge = "Balanced_B100" # ~60GB, enterprise
}

size = lookup(local.spec, "size", "small")
sku_name = lookup(local.size_to_sku, local.size, "Balanced_B1")

# Advanced settings
advanced = lookup(local.spec, "advanced", {})

# Clustering mode mapping
clustering_mode_to_policy = {
standard = "OSSCluster"
legacy_compatible = "EnterpriseCluster"
}

clustering_mode = lookup(local.advanced, "clustering_mode", "standard")
clustering_policy = lookup(local.clustering_mode_to_policy, local.clustering_mode, "OSSCluster")

# Authentication
enable_password_auth = lookup(local.advanced, "enable_password_auth", true)
access_keys_authentication_enabled = local.enable_password_auth

# Protocol configuration
client_protocol = "Encrypted" # Always use TLS

# Eviction policy
eviction_policy = "VolatileLRU" # Standard Redis eviction for volatile keys

# Azure Managed Redis port (different from legacy Redis Cache)
redis_port = 10000

# Username for connection strings
username = "default"
}
74 changes: 74 additions & 0 deletions datastore/redis/azure_managed_redis/1.0/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Azure Managed Redis (Redis Enterprise)
resource "azurerm_managed_redis" "main" {
name = local.cache_name
location = local.region
resource_group_name = local.resource_group_name
sku_name = local.sku_name

# VNet-only access - disable public network access
public_network_access = "Disabled"

tags = local.tags

# Database configuration
default_database {
client_protocol = local.client_protocol
clustering_policy = local.clustering_policy
eviction_policy = local.eviction_policy
access_keys_authentication_enabled = local.access_keys_authentication_enabled
}

lifecycle {
prevent_destroy = false
ignore_changes = [
# Immutable attributes that cannot be changed after creation
name,
location,
sku_name,
]
}
}

# Private Endpoint for secure VNet access
resource "azurerm_private_endpoint" "redis" {
name = "${local.cache_name}-pe"
location = local.region
resource_group_name = local.resource_group_name
subnet_id = local.subnet_id

private_service_connection {
name = "${local.cache_name}-psc"
private_connection_resource_id = azurerm_managed_redis.main.id
is_manual_connection = false
subresource_names = ["redisCache"]
}

tags = local.tags
}

# Private DNS Zone for Private Endpoint resolution
resource "azurerm_private_dns_zone" "redis" {
name = "privatelink.redisenterprise.cache.azure.net"
resource_group_name = local.resource_group_name

tags = local.tags
}

# Link Private DNS Zone to VNet
resource "azurerm_private_dns_zone_virtual_network_link" "redis" {
name = "${local.cache_name}-dns-link"
resource_group_name = local.resource_group_name
private_dns_zone_name = azurerm_private_dns_zone.redis.name
virtual_network_id = local.vnet_id

tags = local.tags
}

# DNS A record for Private Endpoint
resource "azurerm_private_dns_a_record" "redis" {
name = local.cache_name
zone_name = azurerm_private_dns_zone.redis.name
resource_group_name = local.resource_group_name
ttl = 300
records = [azurerm_private_endpoint.redis.private_service_connection[0].private_ip_address]
}
38 changes: 38 additions & 0 deletions datastore/redis/azure_managed_redis/1.0/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
locals {
# Private Endpoint IP for connections
private_ip = azurerm_private_endpoint.redis.private_service_connection[0].private_ip_address

# Hostname that resolves via Private DNS to the private IP
# Clients in the VNet can use this hostname directly
private_hostname = "${local.cache_name}.privatelink.redisenterprise.cache.azure.net"

output_attributes = {
# Cache details
cache_name = azurerm_managed_redis.main.name
cache_id = azurerm_managed_redis.main.id

# Private Endpoint details
private_endpoint_ip = local.private_ip
private_endpoint_id = azurerm_private_endpoint.redis.id
private_endpoint_name = azurerm_private_endpoint.redis.name

# Private DNS details
private_dns_zone_id = azurerm_private_dns_zone.redis.id
private_dns_zone_name = azurerm_private_dns_zone.redis.name
private_hostname = local.private_hostname

# Original public hostname (for reference, not accessible)
public_hostname = azurerm_managed_redis.main.hostname
}

output_interfaces = {
cluster = {
host = local.private_hostname
port = tostring(local.redis_port)
endpoint = "${local.private_hostname}:${local.redis_port}"
auth_token = azurerm_managed_redis.main.default_database[0].primary_access_key
connection_string = "rediss://:${azurerm_managed_redis.main.default_database[0].primary_access_key}@${local.private_hostname}:${local.redis_port}"
secrets = ["auth_token", "connection_string"]
}
}
}
Loading