Skip to content

Commit a88146d

Browse files
feat(terraform): add the Azure redis module (#14102)
1 parent 6253ac7 commit a88146d

5 files changed

Lines changed: 713 additions & 0 deletions

File tree

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
locals {
2+
create_private_dns_zone = var.enable_private_endpoint && var.private_dns_zone_id == null
3+
private_dns_zone_id = local.create_private_dns_zone ? azurerm_private_dns_zone.this[0].id : var.private_dns_zone_id
4+
5+
# A cache behind a private endpoint has no reason to answer on its public
6+
# hostname, so that is the default unless the caller says otherwise.
7+
public_network_access_enabled = var.public_network_access_enabled != null ? var.public_network_access_enabled : !var.enable_private_endpoint
8+
9+
alert_frequency = "PT5M"
10+
alert_window_size = "PT15M"
11+
metric_namespace = "Microsoft.Cache/redis"
12+
}
13+
14+
resource "azurerm_redis_cache" "this" {
15+
name = var.name
16+
resource_group_name = var.resource_group_name
17+
location = var.location
18+
19+
sku_name = var.sku_name
20+
family = var.family
21+
capacity = var.capacity
22+
zones = var.zones
23+
24+
minimum_tls_version = var.minimum_tls_version
25+
non_ssl_port_enabled = false
26+
public_network_access_enabled = local.public_network_access_enabled
27+
28+
access_keys_authentication_enabled = var.access_keys_enabled
29+
30+
redis_configuration {
31+
maxmemory_policy = var.maxmemory_policy
32+
active_directory_authentication_enabled = var.enable_entra_authentication
33+
}
34+
35+
tags = var.tags
36+
}
37+
38+
resource "azurerm_private_dns_zone" "this" {
39+
count = local.create_private_dns_zone ? 1 : 0
40+
41+
name = "privatelink.redis.cache.windows.net"
42+
resource_group_name = var.resource_group_name
43+
tags = var.tags
44+
}
45+
46+
resource "azurerm_private_dns_zone_virtual_network_link" "this" {
47+
count = local.create_private_dns_zone ? 1 : 0
48+
49+
name = "${var.name}-dns-link"
50+
resource_group_name = var.resource_group_name
51+
private_dns_zone_name = azurerm_private_dns_zone.this[0].name
52+
virtual_network_id = var.virtual_network_id
53+
registration_enabled = false
54+
tags = var.tags
55+
}
56+
57+
resource "azurerm_private_endpoint" "this" {
58+
count = var.enable_private_endpoint ? 1 : 0
59+
60+
name = "${var.name}-pe"
61+
resource_group_name = var.resource_group_name
62+
location = var.location
63+
subnet_id = var.private_endpoint_subnet_id
64+
tags = var.tags
65+
66+
private_service_connection {
67+
name = "${var.name}-psc"
68+
private_connection_resource_id = azurerm_redis_cache.this.id
69+
subresource_names = ["redisCache"]
70+
is_manual_connection = false
71+
}
72+
73+
private_dns_zone_group {
74+
name = "default"
75+
private_dns_zone_ids = [local.private_dns_zone_id]
76+
}
77+
}
78+
79+
# Memory is the failure mode that actually takes a broker down: keys that never
80+
# expire climb to the limit, eviction cannot free anything, and Redis starts
81+
# rejecting writes, at which point the whole Celery fleet crashloops at once.
82+
resource "azurerm_monitor_metric_alert" "memory_high" {
83+
name = "${var.name}-memory-high"
84+
resource_group_name = var.resource_group_name
85+
scopes = [azurerm_redis_cache.this.id]
86+
description = "Redis ${var.name} memory usage high"
87+
severity = 2
88+
frequency = local.alert_frequency
89+
window_size = local.alert_window_size
90+
tags = var.tags
91+
92+
criteria {
93+
metric_namespace = local.metric_namespace
94+
metric_name = "usedmemorypercentage"
95+
aggregation = "Average"
96+
operator = "GreaterThan"
97+
threshold = var.memory_high_threshold_percent
98+
}
99+
100+
dynamic "action" {
101+
for_each = var.action_group_ids
102+
content {
103+
action_group_id = action.value
104+
}
105+
}
106+
}
107+
108+
resource "azurerm_monitor_metric_alert" "memory_critical" {
109+
name = "${var.name}-memory-critical"
110+
resource_group_name = var.resource_group_name
111+
scopes = [azurerm_redis_cache.this.id]
112+
description = "Redis ${var.name} memory usage critical, writes may be rejected"
113+
severity = 1
114+
frequency = local.alert_frequency
115+
window_size = local.alert_window_size
116+
tags = var.tags
117+
118+
criteria {
119+
metric_namespace = local.metric_namespace
120+
metric_name = "usedmemorypercentage"
121+
aggregation = "Average"
122+
operator = "GreaterThan"
123+
threshold = var.memory_critical_threshold_percent
124+
}
125+
126+
dynamic "action" {
127+
for_each = var.action_group_ids
128+
content {
129+
action_group_id = action.value
130+
}
131+
}
132+
}
133+
134+
resource "azurerm_monitor_metric_alert" "server_load" {
135+
name = "${var.name}-server-load-high"
136+
resource_group_name = var.resource_group_name
137+
scopes = [azurerm_redis_cache.this.id]
138+
description = "Redis ${var.name} server thread saturated"
139+
severity = 2
140+
frequency = local.alert_frequency
141+
window_size = local.alert_window_size
142+
tags = var.tags
143+
144+
criteria {
145+
metric_namespace = local.metric_namespace
146+
metric_name = "serverLoad"
147+
aggregation = "Average"
148+
operator = "GreaterThan"
149+
threshold = var.server_load_threshold_percent
150+
}
151+
152+
dynamic "action" {
153+
for_each = var.action_group_ids
154+
content {
155+
action_group_id = action.value
156+
}
157+
}
158+
}
159+
160+
resource "azurerm_monitor_metric_alert" "evicted_keys" {
161+
name = "${var.name}-evicted-keys"
162+
resource_group_name = var.resource_group_name
163+
scopes = [azurerm_redis_cache.this.id]
164+
description = "Redis ${var.name} is evicting keys, which for a Celery broker means dropped tasks"
165+
severity = 1
166+
frequency = local.alert_frequency
167+
window_size = local.alert_window_size
168+
tags = var.tags
169+
170+
criteria {
171+
metric_namespace = local.metric_namespace
172+
metric_name = "evictedkeys"
173+
aggregation = "Total"
174+
operator = "GreaterThan"
175+
threshold = var.evicted_keys_threshold
176+
}
177+
178+
dynamic "action" {
179+
for_each = var.action_group_ids
180+
content {
181+
action_group_id = action.value
182+
}
183+
}
184+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
output "cache_id" {
2+
description = "Resource ID of the cache"
3+
value = azurerm_redis_cache.this.id
4+
}
5+
6+
output "hostname" {
7+
description = "Hostname of the cache. Behind a private endpoint this resolves to a private address from networks linked to the DNS zone."
8+
value = azurerm_redis_cache.this.hostname
9+
}
10+
11+
output "ssl_port" {
12+
description = "TLS port. The non-TLS port is disabled."
13+
value = azurerm_redis_cache.this.ssl_port
14+
}
15+
16+
# The AWS module takes an auth token as an input; Azure generates the keys and
17+
# offers no way to set them, so the credential comes back out of the module.
18+
output "primary_access_key" {
19+
description = "Generated primary access key, null when access keys are disabled"
20+
value = var.access_keys_enabled ? azurerm_redis_cache.this.primary_access_key : null
21+
sensitive = true
22+
}
23+
24+
output "secondary_access_key" {
25+
description = "Generated secondary access key, for rotating without downtime"
26+
value = var.access_keys_enabled ? azurerm_redis_cache.this.secondary_access_key : null
27+
sensitive = true
28+
}
29+
30+
output "private_dns_zone_id" {
31+
description = "Resource ID of the private DNS zone the cache resolves through, null when no private endpoint is used"
32+
value = local.private_dns_zone_id
33+
}

0 commit comments

Comments
 (0)