Skip to main content

Migrating from cnp-module-redis to terraform-module-azure-managed-redis

Azure Managed Redis is a fully managed, enterprise-tier Redis service that replaces the older Azure Cache for Redis (Standard/Premium) offering that cnp-module-redis provisions. This guide explains the input/output differences and the steps needed to switch.


An agentic coding skill and example PRs are provided to assist adoption of the new module. This should be used to get the new infrastructure up and running in alongside your current Redis in sandbox (side by side).

Further information can be found by viewing the new module’s example folder, ‘inputs-*.tf’ files, the Azure documentation, terraform documentation or getting in touch with Platform Operations.

Input files

Azure Managed Redis Docs

Terraform Managed Redis Docs

Quick navigation

Feature parity

While MS is ending support for Az Cache for Redis, there is not complete feature parity between the offerings.

Please investigate & test these carefully as documentation from MS is hit or miss. MS Migration Docs

Quick Start

1) Build resources with Infrastructure as Code.

We have an agent skill which can assist you through the migration process. It will allow your agentic AI to assist and create Terraform and Flux PRs more easily. Check it out here! platops-agent-skills azure-managed-redis-migration .

Example PRs:

2) Run your infrastructure deployment pipeline for chosen environment

After deployment you should see:
- Creation for new Redis infrastructure, with associated networking and secrets accordingly.

No destroys should've occurred unless you are intentionally planning to replace (destroy forever) your current instance and secrets. If any destroys are planned and you're not sure, please get in touch as this is likely to be an _irreversible_ change. 
- New secret should be added to your keyvault 

OPTIONAL) You may choose to spin up a kubernetes pod to test your redis instance is available. Unsupported documentation is here: [Platops confluence](https://tools.hmcts.net/confluence/spaces/DTSPO/pages/1973497319/Plum+Managed+Redis+%E2%80%94+Connection+Validation+Guide). Alternatively continue below if you're already comfortable writing/reading keys from redis.

3) Update application/ code to target the new resource

Ensure you create the redis connection string as a new Keyvault secret resource. That way, if rollback is needed, you can point back to your preexisting secret via a flux PR.

Please see this reference PR for how to switch connection string using flux config. It uses a new Key Vault secret but same variable name for the application.

Data migration instructions

If data migration is required, please follow the instructions here

Key differences at a glance

Concern cnp-module-redis terraform-module-azure-managed-redis
Redis tier (See Azure docs here ) Basic / Standard / Premium Performant SKUs are still very expensive! Balanced / ComputeOptimized / MemoryOptimized / FlashOptimized
SKU format (SKU Reference table below) sku_name + family + capacity Single sku_name e.g. "Balanced_B1"
Redis version redis_version (4 / 6) Always ‘Current’ (parameter removed)
Authentication Access key by default Entra ID / RBAC by default; keys opt-in
Private endpoint flag private_endpoint_enabled (bool) create_private_endpoint (bool)
Public access flag public_network_access_enabled (bool) public_network_access ("Enabled" / "Disabled")
RDB backup frequency Integer minutes (60, 360, 720) Duration strings ("1h", "6h", "12h")
Backup storage rdb_storage_account_name_prefix Managed internally — parameter no longer needed
Resource group Shared / caller-provided Module creates its own RG by default
Private DNS zone privatelink.redis.cache.windows.net privatelink.redis.azure.net

Input mapping

Removed inputs (no equivalent needed)

Old input Reason
redis_version Managed Redis version is managed by Azure
family Encoded in sku_name
capacity Encoded in sku_name
business_area No longer used by this module
rdb_backup_enabled Backup is enabled by setting persistence_rdb_backup_frequency
rdb_backup_max_snapshot_count Var not supported by Managed Redis
rdb_storage_account_name_prefix Managed internally

Changed inputs

Old input New input Notes
private_endpoint_enabled = true create_private_endpoint = true Also supply subnet_id and private_dns_zone_ids
public_network_access_enabled = false public_network_access = "Disabled" Accepts "Enabled" or "Disabled"
rdb_backup_frequency = 360 persistence_rdb_backup_frequency = "6h" Values: "1h", "6h", "12h"
sku_name = "Premium" + family = "P" + capacity = 1 sku_name = "Balanced_B3" See SKU reference below

New required inputs

Input Description
component HMCTS component name — used to form the resource name
subnet_id Subnet for the private endpoint (required when create_private_endpoint = true)
private_dns_zone_ids List of private DNS zone IDs for the private endpoint

Authentication

cnp-module-redis always issues access keys. The new module defaults to Entra ID / RBAC only. To keep using access keys (e.g. for connection strings), set:

access_keys_authentication_enabled = true

Output mapping

Old output New output Backward-compatible alias
access_key primary_access_key redis_cache_primary_access_key
host_name hostname redis_cache_hostname
redis_port port redis_cache_ssl_port

The backward-compatible aliases allow you to rename the module block without updating every downstream reference at once.


SKU reference

NEW Managed Redis - pricing

OLD Azure Cache for Redis - pricing

Managed Redis SKUs combine tier and size into one string:

Tier Example SKUs Rough equivalent
Balanced (B) Balanced_B1, Balanced_B3, Balanced_B5 General purpose workloads
MemoryOptimized (M) MemoryOptimized_M10, M20 Large datasets, low throughput
ComputeOptimized (X) ComputeOptimized_X3, X5 High-throughput, low-latency
FlashOptimized (A) FlashOptimized_A250 Very large datasets with NVMe

If you were using Premium P1 (family = "P", capacity = 1), start with Balanced_B3 and adjust based on memory and throughput requirements.


Fine-grained Migration steps

1. Add the new module block alongside the old one

Keep reference to old module cnp-module-redis in place during the transition. Add the new module with a for_each guard to deploy only to non-production environments first:

module "managed_redis" {
  for_each = toset(contains(["sandbox", "aat"], var.env) ? [var.env] : [])

  source = "git@github.com:hmcts/terraform-module-azure-managed-redis?ref=main"

  product     = var.product
  component   = var.component
  env         = var.env
  location    = var.location
  common_tags = var.common_tags

  public_network_access   = "Disabled"
  create_private_endpoint = true
  subnet_id               = data.azurerm_subnet.redis_private_endpoint.id
  private_dns_zone_ids    = [
    "/subscriptions/${var.private_dns_subscription_id}/resourceGroups/core-infra-intsvc-rg/providers/Microsoft.Network/privateDnsZones/privatelink.redis.azure.net"
  ]

  access_keys_authentication_enabled = true
  persistence_rdb_backup_frequency   = "6h"
}

Note the DNS zone hostname has changed from privatelink.redis.cache.windows.netprivatelink.redis.azure.net.

2. Update connection string references

Replace any outputs consumed from the old module:

# Before
value = "redis://ignore:${urlencode(module.plum-redis-storage.access_key)}@${module.plum-redis-storage.host_name}:${module.plum-redis-storage.redis_port}?tls=true"

# After (using for_each — pick the single instance)
value = "redis://ignore:${urlencode(one(values(module.managed_redis)).primary_access_key)}@${one(values(module.managed_redis)).hostname}:${one(values(module.managed_redis)).port}?tls=true"

If you are not using for_each, the reference is simpler:

value = "redis://ignore:${urlencode(module.managed_redis.primary_access_key)}@${module.managed_redis.hostname}:${module.managed_redis.port}?tls=true"

3. Update Key Vault secrets

Any azurerm_key_vault_secret resources that read from the old module outputs must be updated to reference the new module. Run terraform plan and verify only the secret value changes, not a destroy/recreate of the secret resource itself.

4. Remove the old module block

Once the new Redis instance is confirmed healthy and applications are pointing at it, remove the module "plum-redis-storage" block and run terraform apply. Terraform will destroy the old azurerm_redis_cache resource.

5. Remove the for_each guard

After a successful rollout to lower environments, extend the for_each to include production (or remove the guard entirely):

# Remove for_each entirely for a simple single-instance deployment
module "managed_redis" {
  source = "git@github.com:hmcts/terraform-module-azure-managed-redis?ref=main"
  ...
}

As mentioned in the example PR, removing this may cause your resources within terraform’s state to change slightly. Please see the PR for remediation steps

Variables to add / remove from variables.tf

Remove (check if these are actually only utilised by cnp-module-redis):

variable "sku_name" {}       
variable "family" {}
variable "redis_capacity" {}
variable "rdb_backup_enabled" {}
variable "redis_backup_frequency" {}
variable "rdb_backup_max_snapshot_count" {}

The new module reuses product, component, env, location, and common_tags which are already standard variables in HMCTS Terraform configurations.


Gotchas /Troubleshooting:

  • Access keys: If you utilise the same terraform secret resource as your current redis infrastructure, the ‘current/old’ secrets will be visible as old versions on the keyvault

    Azure keyvault encodes keys with base64 encoding so you may see %3D (=) padding

  • Clustering (-c flag) Azure Managed Redis runs as a clustered Redis Enterprise instance with two shards (slots 0-8191 and 8192-16383). When you connect with redis-cli, your session lands on one shard. When initiating a connection,remember to connect to the cluster rather than a single shard. Commands against keys owned by the other shard return nothing or a MOVED redirect.

  • Database number Azure Managed Redis supports only a single database (database 0). If your application uses multiple databases, you need to refactor your data model to use a single database or use key prefixes to logically separate data before migrating.

This page was last reviewed on 4 June 2026. It needs to be reviewed again on 4 December 2026 by the page owner platops-build-notices .
This page was set to be reviewed before 4 December 2026 by the page owner platops-build-notices. This might mean the content is out of date.