Azure Managed Redis — replacing Azure Cache for Redis, tiers, clustering policies, Entra authentication from Node.js, Bicep
Using Azure Managed Redis (the Microsoft.Cache/redisEnterprise resource) instead of Azure Cache for Redis, which retires on September 30, 2028 (Enterprise tiers on March 31, 2027) — the four tiers, port 10000 and TLS, OSS vs Enterprise vs non-clustered policies and what each means for clients, Entra authentication (object ID as user, no groups, re-AUTH before expiry, keys disabled), access policy assignments, Bicep where publicNetworkAccess is required, and node-redis with @redis/entraid.
markdownreferencev139J7-4RFFstage: buildtrust: officialservice: redisinterface: iacinterface: sdkAzure Managed RedisAzure Cache for Redis retirement September 30 2028Azure Cache for Redis Enterprise retirement March 31 2027Microsoft.Cache/redisEnterprise@2025-07-01Balanced_B1Memory OptimizedCompute OptimizedFlash Optimizedport 10000clusteringPolicy OSSCluster EnterpriseCluster NoClusterCROSSSLOTRediSearch Enterprise clustering85xx portsnodeAddressMapcreateClusteraccessKeysAuthentication DisabledaccessPolicyAssignmentsaccessPolicyName defaultEntra object ID usernameEntra groups not supported Redishttps://redis.azure.com/.defaultREDIS_SCOPE_DEFAULT@redis/entraidEntraIdCredentialsProviderFactory createForDefaultAzureCredentialexpirationRefreshRatioredisenterprise test-connection --auth entrapublicNetworkAccess requiredprivatelink.redis.azure.nethighAvailability Disabled data lossminimumTlsVersion 1.2
Azure Managed Redis
Retirement: move off Azure Cache for Redis
| Azure Cache for Redis tier | Retires | Instances disabled from |
|---|---|---|
| Basic, Standard, Premium | September 30, 2028 | October 1, 2028 |
| Enterprise, Enterprise Flash | March 31, 2027 | April 1, 2027 |
The replacement is Azure Managed Redis: resource type Microsoft.Cache/redisEnterprise (with a databases/default child), Redis 7.4, host <name>.<region>.redis.azure.net, port 10000, TLS 1.2 or 1.3. New work should target it.
Tiers and clustering
- Tiers (memory : vCPU): Memory Optimized 8:1, Balanced 4:1, Compute Optimized 2:1, Flash Optimized (cold keys on NVMe; no Search, Bloom or TimeSeries). SKUs look like
Balanced_B1,MemoryOptimized_M10,ComputeOptimized_X5. - High availability (on by default) keeps replicas across zones where available; turning it off loses data on failure: dev/test only.
clusteringPolicy | Clients | Notes |
|---|---|---|
OSSCluster (default) | Must be cluster-aware: first connection on 10000, then shards on 85xx ports found with CLUSTER NODES (don't hard-code them) | Best throughput; multi-key commands need keys in the same hash slot (else CROSSSLOT); no RediSearch |
EnterpriseCluster | Any client: one endpoint, a proxy routes | Required for RediSearch; only DEL, MSET, MGET, EXISTS, UNLINK, TOUCH may span slots |
NoCluster | Any client | Caches of 25 GB or less only |
The policy can't change once it's OSSCluster or EnterpriseCluster without deleting the database (only NoCluster can change), and not at all with geo-replication.
Entra authentication
- User name = the object ID of the managed identity or service principal; password = a token for
https://redis.azure.com/.default. Entra groups aren't supported; TLS is required; some commands are blocked. - Grant access with an access policy assignment (
accessPolicyName: 'default', the only one) per principal, or the portal's Authentication blade. Test with theredisenterpriseCLI extension (not in core CLI): itstest-connectioncommand with--auth entrasigns in as youraz loginidentity. - Clients must re-send
AUTHwith a new token at least three minutes before expiry, with jitter:@redis/entraiddoes this when configured. - Then set
accessKeysAuthentication: 'Disabled'. Changing it drops every existing connection, Entra ones included; geo-replicated caches must be unlinked first.
Bicep
// Azure Managed Redis (Balanced B1) with access keys off and Entra access for an app identity
param location string = resourceGroup().location
param name string
param appPrincipalId string // object (principal) ID of the app's managed identity
resource cluster 'Microsoft.Cache/redisEnterprise@2025-07-01' = {
name: name
location: location
sku: {
name: 'Balanced_B1'
}
properties: {
minimumTlsVersion: '1.2'
publicNetworkAccess: 'Disabled' // required in this API version; reach it through a private endpoint
highAvailability: 'Enabled' // Disabled is for dev/test only: no replication
}
}
resource db 'Microsoft.Cache/redisEnterprise/databases@2025-07-01' = {
parent: cluster
name: 'default'
properties: {
clientProtocol: 'Encrypted'
port: 10000
clusteringPolicy: 'OSSCluster' // the default; changing OSS or Enterprise later means deleting the database
evictionPolicy: 'VolatileLRU'
accessKeysAuthentication: 'Disabled'
}
}
resource appAccess 'Microsoft.Cache/redisEnterprise/databases/accessPolicyAssignments@2025-07-01' = {
parent: db
name: 'app${uniqueString(appPrincipalId)}'
properties: {
accessPolicyName: 'default' // the only policy supported
user: {
objectId: appPrincipalId
}
}
}
output endpoint string = '${cluster.properties.hostName}:10000'
publicNetworkAccess is required on create in API 2025-07-01 (Bicep warns BCP035 without it). Private endpoint: groupId redisEnterprise, zone privatelink.redis.azure.net.
Node.js (node-redis 6 with Entra)
import { isIP } from "node:net";
import { DefaultAzureCredential } from "@azure/identity";
import { EntraIdCredentialsProviderFactory, REDIS_SCOPE_DEFAULT } from "@redis/entraid";
import { createCluster } from "redis";
// REDIS_ENDPOINT looks like "<name>.<region>.redis.azure.net:10000"
const endpoint = process.env.REDIS_ENDPOINT ?? "";
const [host] = endpoint.split(":");
const credentialsProvider = EntraIdCredentialsProviderFactory.createForDefaultAzureCredential({
credential: new DefaultAzureCredential(),
scopes: REDIS_SCOPE_DEFAULT, // https://redis.azure.com/.default
tokenManagerConfig: { expirationRefreshRatio: 0.8 }, // re-authenticate before the token expires
});
// OSSCluster (the default clustering policy) needs a cluster-aware client.
export const redis = createCluster({
rootNodes: [{ url: `rediss://${endpoint}` }],
defaults: { credentialsProvider, socket: { tls: true, connectTimeout: 15_000 } },
// Shards announce IP addresses; map them back to the host name so TLS validation passes.
nodeAddressMap(address: string) {
const [h, port] = address.split(":");
return { host: isIP(h ?? "") !== 0 ? host ?? "" : h ?? "", port: Number(port) };
},
});
export async function main(): Promise<void> {
redis.on("error", (e) => console.error("redis", e));
await redis.connect();
await redis.set("greeting", "hello", { EX: 60 });
console.log(await redis.get("greeting"));
await redis.close();
}
For EnterpriseCluster or NoCluster, use createClient({ url, credentialsProvider, socket }) instead of createCluster. Microsoft's sample maps shard addresses back to the host name with nodeAddressMap; the hub's reading is that without it an OSS-cluster client connects to shard IPs and TLS host-name checks fail.