Skip to content

Commit 28ad228

Browse files
BenjaminEngesetCopilotBernieWhite
authored
Added Azure.Redis.MigrateAMR (#3607)
* Initial plan * Add Azure.Redis.Retirement rule Co-authored-by: BenjaminEngeset <99641908+BenjaminEngeset@users.noreply.github.com> * Fix resource type naming in documentation Co-authored-by: BenjaminEngeset <99641908+BenjaminEngeset@users.noreply.github.com> * Address feedback: update rule reference, synopsis, and documentation Co-authored-by: BenjaminEngeset <99641908+BenjaminEngeset@users.noreply.github.com> * Address feedback: rename rule, update dates, and clarify changelog Co-authored-by: BenjaminEngeset <99641908+BenjaminEngeset@users.noreply.github.com> * Address feedback: improve timeline clarity and rename localized string Co-authored-by: BenjaminEngeset <99641908+BenjaminEngeset@users.noreply.github.com> * fixes * Rename localized string to CacheRedisMigrateAMR Co-authored-by: BenjaminEngeset <99641908+BenjaminEngeset@users.noreply.github.com> * Updates --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Bernie White <bewhite@microsoft.com>
1 parent 488f1d8 commit 28ad228

File tree

7 files changed

+290
-0
lines changed

7 files changed

+290
-0
lines changed

docs/changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers
3434
- App Configuration:
3535
- Check that replica locations are in allowed regions by @BernieWhite.
3636
[#3441](https://github.com/Azure/PSRule.Rules.Azure/issues/3441)
37+
- Azure Cache for Redis:
38+
- Check for legacy Azure Cache for Redis instances by @BenjaminEngeset.
39+
[#3605](https://github.com/Azure/PSRule.Rules.Azure/issues/3605)
3740
- Managed Instance for Apache Cassandra:
3841
- Check that Managed Instance for Apache Cassandra clusters have availability zones enabled by @BenjaminEngeset.
3942
[#3592](https://github.com/Azure/PSRule.Rules.Azure/issues/3592)
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
---
2+
reviewed: 2025-11-23
3+
severity: Important
4+
pillar: Operational Excellence
5+
category: OE:05 Infrastructure as code
6+
resource: Azure Cache for Redis
7+
resourceType: Microsoft.Cache/redis
8+
online version: https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.Redis.MigrateAMR/
9+
---
10+
11+
# Migrate to Azure Managed Redis
12+
13+
## SYNOPSIS
14+
15+
Azure Cache for Redis is being retired. Migrate to Azure Managed Redis.
16+
17+
## DESCRIPTION
18+
19+
Microsoft has announced the retirement timeline for Azure Cache for Redis across all SKUs.
20+
The recommended replacement going forward is Azure Managed Redis.
21+
22+
Azure Cache for Redis (Basic, Standard, Premium) will be retired according to the following timeline:
23+
24+
- Creation blocked for new customers: April 1, 2026.
25+
- Creation blocked for existing customers: October 1, 2026.
26+
- Retirement Date: September 30, 2028.
27+
- Instances will be disabled starting October 1, 2028.
28+
29+
To avoid service disruption, migrate your workloads to Azure Managed Redis.
30+
31+
## RECOMMENDATION
32+
33+
Plan and execute migration from Azure Cache for Redis to Azure Managed Redis before the retirement dates to avoid service disruption.
34+
35+
## EXAMPLES
36+
37+
### Configure with Bicep
38+
39+
To deploy resource that pass this rule:
40+
41+
- Create resources of type `Microsoft.Cache/redisEnterprise` and an Azure Managed Redis SKU, such as:
42+
- `Balanced_*`
43+
- `MemoryOptimized_*`
44+
- `ComputeOptimized_*`
45+
46+
For example:
47+
48+
```bicep
49+
resource primary 'Microsoft.Cache/redisEnterprise@2025-07-01' = {
50+
name: name
51+
location: location
52+
properties: {
53+
highAvailability: 'Enabled'
54+
publicNetworkAccess: 'Disabled'
55+
}
56+
sku: {
57+
name: 'Balanced_B10'
58+
}
59+
}
60+
```
61+
62+
### Configure with Azure template
63+
64+
To deploy resource that pass this rule:
65+
66+
- Create resources of type `Microsoft.Cache/redisEnterprise` and an Azure Managed Redis SKU, such as:
67+
- `Balanced_*`
68+
- `MemoryOptimized_*`
69+
- `ComputeOptimized_*`
70+
71+
For example:
72+
73+
```json
74+
{
75+
"type": "Microsoft.Cache/redisEnterprise",
76+
"apiVersion": "2025-07-01",
77+
"name": "[parameters('name')]",
78+
"location": "[parameters('location')]",
79+
"properties": {
80+
"highAvailability": "Enabled",
81+
"publicNetworkAccess": "Disabled"
82+
},
83+
"sku": {
84+
"name": "Balanced_B10"
85+
}
86+
}
87+
```
88+
89+
## NOTES
90+
91+
Redis Enterprise and Enterprise Flash used the `Microsoft.Cache/redisEnterprise` resource type.
92+
Redis Enterprise and Enterprise Flash SKUs `Enterprise_*` and `EnterpriseFlash_*` are also deprecated.
93+
94+
## LINKS
95+
96+
- [OE:05 Infrastructure as code](https://learn.microsoft.com/azure/architecture/framework/devops/automation-infrastructure)
97+
- [Azure Cache for Redis retirement: What to know and how to prepare](https://techcommunity.microsoft.com/blog/azure-managed-redis/azure-cache-for-redis-retirement-what-to-know-and-how-to-prepare/4458721)
98+
- [Azure Cache for Redis retirement FAQ](https://learn.microsoft.com/azure/azure-cache-for-redis/retirement-faq)
99+
- [Azure Managed Redis documentation](https://learn.microsoft.com/azure/azure-cache-for-redis/managed-redis/managed-redis-overview)
100+
- [Azure deployment reference](https://learn.microsoft.com/azure/templates/microsoft.cache/redisenterprise)

docs/examples/resources/amr.bicep

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
// Bicep documentation examples
5+
6+
@minLength(1)
7+
@maxLength(63)
8+
@sys.description('The name of the resource.')
9+
param name string
10+
11+
@sys.description('The location resources will be deployed.')
12+
param location string = resourceGroup().location
13+
14+
@sys.description('The location of a secondary replica.')
15+
param secondaryLocation string = location
16+
17+
// An example Azure Managed Redis instance with availability zones.
18+
resource primary 'Microsoft.Cache/redisEnterprise@2025-07-01' = {
19+
name: name
20+
location: location
21+
properties: {
22+
highAvailability: 'Enabled'
23+
publicNetworkAccess: 'Disabled'
24+
}
25+
sku: {
26+
name: 'Balanced_B10'
27+
}
28+
}
29+
30+
// An example secondary replica in an alternative region.
31+
resource secondary 'Microsoft.Cache/redisEnterprise@2025-07-01' = {
32+
name: name
33+
location: secondaryLocation
34+
properties: {
35+
highAvailability: 'Enabled'
36+
publicNetworkAccess: 'Disabled'
37+
}
38+
sku: {
39+
name: 'Balanced_B10'
40+
}
41+
}
42+
43+
// An example database replicated across the primary and secondary instances.
44+
resource database 'Microsoft.Cache/redisEnterprise/databases@2025-07-01' = {
45+
parent: primary
46+
name: 'default'
47+
properties: {
48+
clientProtocol: 'Encrypted'
49+
evictionPolicy: 'VolatileLRU'
50+
clusteringPolicy: 'OSSCluster'
51+
deferUpgrade: 'NotDeferred'
52+
modules: [
53+
{
54+
name: 'RedisJSON'
55+
}
56+
]
57+
persistence: {
58+
aofEnabled: false
59+
rdbEnabled: true
60+
rdbFrequency: '12h'
61+
}
62+
accessKeysAuthentication: 'Disabled'
63+
geoReplication: {
64+
groupNickname: 'group'
65+
linkedDatabases: [
66+
{
67+
id: resourceId('Microsoft.Cache/redisEnterprise/databases', secondary.name, 'default')
68+
}
69+
]
70+
}
71+
}
72+
}

docs/examples/resources/amr.json

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3+
"contentVersion": "1.0.0.0",
4+
"metadata": {
5+
"_generator": {
6+
"name": "bicep",
7+
"version": "0.39.26.7824",
8+
"templateHash": "6517319720095351040"
9+
}
10+
},
11+
"parameters": {
12+
"name": {
13+
"type": "string",
14+
"minLength": 1,
15+
"maxLength": 63,
16+
"metadata": {
17+
"description": "The name of the resource."
18+
}
19+
},
20+
"location": {
21+
"type": "string",
22+
"defaultValue": "[resourceGroup().location]",
23+
"metadata": {
24+
"description": "The location resources will be deployed."
25+
}
26+
},
27+
"secondaryLocation": {
28+
"type": "string",
29+
"defaultValue": "[parameters('location')]",
30+
"metadata": {
31+
"description": "The location of a secondary replica."
32+
}
33+
}
34+
},
35+
"resources": [
36+
{
37+
"type": "Microsoft.Cache/redisEnterprise",
38+
"apiVersion": "2025-07-01",
39+
"name": "[parameters('name')]",
40+
"location": "[parameters('location')]",
41+
"properties": {
42+
"highAvailability": "Enabled",
43+
"publicNetworkAccess": "Disabled"
44+
},
45+
"sku": {
46+
"name": "Balanced_B10"
47+
}
48+
},
49+
{
50+
"type": "Microsoft.Cache/redisEnterprise",
51+
"apiVersion": "2025-07-01",
52+
"name": "[parameters('name')]",
53+
"location": "[parameters('secondaryLocation')]",
54+
"properties": {
55+
"highAvailability": "Enabled",
56+
"publicNetworkAccess": "Disabled"
57+
},
58+
"sku": {
59+
"name": "Balanced_B10"
60+
}
61+
},
62+
{
63+
"type": "Microsoft.Cache/redisEnterprise/databases",
64+
"apiVersion": "2025-07-01",
65+
"name": "[format('{0}/{1}', parameters('name'), 'default')]",
66+
"properties": {
67+
"clientProtocol": "Encrypted",
68+
"evictionPolicy": "VolatileLRU",
69+
"clusteringPolicy": "OSSCluster",
70+
"deferUpgrade": "NotDeferred",
71+
"modules": [
72+
{
73+
"name": "RedisJSON"
74+
}
75+
],
76+
"persistence": {
77+
"aofEnabled": false,
78+
"rdbEnabled": true,
79+
"rdbFrequency": "12h"
80+
},
81+
"accessKeysAuthentication": "Disabled",
82+
"geoReplication": {
83+
"groupNickname": "group",
84+
"linkedDatabases": [
85+
{
86+
"id": "[resourceId('Microsoft.Cache/redisEnterprise/databases', parameters('name'), 'default')]"
87+
}
88+
]
89+
}
90+
},
91+
"dependsOn": [
92+
"[resourceId('Microsoft.Cache/redisEnterprise', parameters('name'))]",
93+
"[resourceId('Microsoft.Cache/redisEnterprise', parameters('name'))]"
94+
]
95+
}
96+
]
97+
}

src/PSRule.Rules.Azure/en/PSRule-rules.psd1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,5 @@
131131
ResourceHasNoTags = "The resource does not have any tags. Expected tags: {0}."
132132
ActiveSecurityAlerts = "There are {0} active security alerts of high or medium severity."
133133
KeyValueShouldNotContainSecrets = "The key value '{0}' property should not contain secrets."
134+
CacheRedisMigrateAMR = "Azure Cache for Redis is being retired. Migrate to Azure Managed Redis."
134135
}

src/PSRule.Rules.Azure/rules/Azure.Redis.Rule.ps1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ Rule 'Azure.Redis.Version' -Ref 'AZR-000347' -Type 'Microsoft.Cache/redis' -Tag
123123
).Reason($LocalizedData.AzureCacheRedisVersion)
124124
}
125125

126+
# Synopsis: Azure Cache for Redis is being retired. Migrate to Azure Managed Redis.
127+
Rule 'Azure.Redis.MigrateAMR' -Ref 'AZR-000533' -Type 'Microsoft.Cache/redis' -Tag @{ release = 'GA'; ruleSet = '2025_12'; 'Azure.WAF/pillar' = 'Operational Excellence'; } {
128+
$Assert.Fail($LocalizedData.CacheRedisMigrateAMR)
129+
}
130+
126131
#region Helper functions
127132

128133
function global:GetCacheMemory {

tests/PSRule.Rules.Azure.Tests/Azure.Redis.Tests.ps1

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,18 @@ Describe 'Azure.Redis' -Tag 'Redis' {
353353
$ruleResult.TargetName | Should -BeIn 'redis-R';
354354
$ruleResult.Length | Should -Be 1;
355355
}
356+
357+
It 'Azure.Redis.MigrateAMR' {
358+
$filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.Redis.MigrateAMR' };
359+
360+
# Fail - all instances should fail
361+
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
362+
$ruleResult | Should -Not -BeNullOrEmpty;
363+
$ruleResult.Length | Should -Be 12;
364+
$ruleResult.TargetName | Should -BeIn 'redis-A', 'redis-B', 'redis-C', 'redis-D', 'redis-E', 'redis-F', 'redis-G', 'redis-H', 'redis-I', 'redis-J', 'redis-Q', 'redis-R';
365+
366+
$ruleResult[0].Reason | Should -BeExactly "Azure Cache for Redis is being retired. Migrate to Azure Managed Redis.";
367+
}
356368
}
357369

358370
Context 'With Configuration Option' -Tag 'Configuration' {

0 commit comments

Comments
 (0)