Skip to content

Commit 6ab5431

Browse files
committed
Switching Azure Cache for Redis to Azure Manged Redis
1 parent 4785364 commit 6ab5431

File tree

5 files changed

+172
-175
lines changed

5 files changed

+172
-175
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ App Service Environment must always be deployed in its own subnet in the enterpr
195195
echo $JUMPBOX_SUBNET_NAME
196196
```
197197

198-
### 8. Deploy services: cosmos, sql, servicebus and storage
198+
### 8. Deploy services: cosmos, sql, azure managed redis, servicebus and storage
199199
This module provisions the essential backend services required for the application. It includes a Cosmos DB instance for distributed caching, a secure SQL Server and database for relational data, and an Azure Key Vault for managing secrets using role-based access control. A premium-tier Service Bus is configured to enable reliable messaging between application components. Additionally, a storage account with a blob container provides scalable storage for static resources, while a second storage account is designated for use by the Function App as Azure WebJob storage. All services are deployed with public network access disabled where applicable, ensuring a secure and private infrastructure.
200200

201201
```bash
@@ -221,6 +221,8 @@ App Service Environment must always be deployed in its own subnet in the enterpr
221221
echo $RESOURCES_CONTAINER_NAME
222222
export SERVICEBUS_NAMESPACE_NAME=$(az deployment group show -g rg-app-service-environments-centralus -n services --query properties.outputs.serviceBusName.value -o tsv)
223223
echo $SERVICEBUS_NAMESPACE_NAME
224+
export AMR_NAME=$(az deployment group show -g rg-app-service-environments-centralus -n services --query properties.outputs.amrName.value -o tsv)
225+
echo $AMR_NAME
224226
```
225227

226228
### 9. Uploads image to the storage account
@@ -241,7 +243,7 @@ App Service Environment must always be deployed in its own subnet in the enterpr
241243
# [This takes about thirty minutes to run.]
242244
# For HA change zoneRedundant to true
243245
az deployment group create -g rg-app-service-environments-centralus --template-file templates/sites.bicep -n sites --parameters aseName=$ASE_NAME \
244-
vnetName=$VNET_NAME cosmosDbName=$COSMOSDB_NAME sqlServerName=$SQL_SERVER sqlDatabaseName=$SQL_DATABASE keyVaultName=$KEYVAULT_NAME serviceBusNamespace=$SERVICEBUS_NAMESPACE_NAME storageAccountName=$RESOURCES_STORAGE_ACCOUNT_FUNCTION_APP \
246+
amrName=$AMR_NAME cosmosDbName=$COSMOSDB_NAME sqlServerName=$SQL_SERVER sqlDatabaseName=$SQL_DATABASE keyVaultName=$KEYVAULT_NAME serviceBusNamespace=$SERVICEBUS_NAMESPACE_NAME storageAccountName=$RESOURCES_STORAGE_ACCOUNT_FUNCTION_APP \
245247
aseDnsSuffix=$ASE_DNS_SUFFIX zoneRedundant=false
246248

247249
export INTERNAL_APP1_URL=$(az deployment group show -g rg-app-service-environments-centralus -n sites --query properties.outputs.votingAppUrl.value -o tsv) && \

code/web-app-ri/VotingWeb/Program.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,8 @@
7575
c.DefaultRequestHeaders.Add(HeaderNames.Accept, MediaTypeNames.Application.Json);
7676
});
7777

78-
var host = configuration["RedisHost"];
79-
var port = configuration["RedisPort"];
80-
var options = ConfigurationOptions.Parse($"{host}:{port}");
78+
var redisConnectionString = configuration["ConnectionStrings:Redis"];
79+
var options = ConfigurationOptions.Parse(redisConnectionString);
8180
await options.ConfigureForAzureWithTokenCredentialAsync(new DefaultAzureCredential());
8281
var redis = await ConnectionMultiplexer.ConnectAsync(options);
8382

deployment/templates/privateendpoints.bicep

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ param akvName string
2424
@description('The name of the existing storage account for creating the private endpoint.')
2525
param storageAccountName string
2626

27+
@description('The name of the existing azure managed redis namespace for creating the private endpoint.')
28+
param amrName string
29+
2730
@description('The ip address prefix that services subnet will use.')
2831
param subnetAddressPrefix string = '10.0.50.0/24'
2932

@@ -38,6 +41,7 @@ param sqlServerId string = '/subscriptions/${SubId}/resourceGroups/${resourceGro
3841
param cosmosId string = '/subscriptions/${SubId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosDBName}'
3942
param akvId string = '/subscriptions/${SubId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.KeyVault/vaults/${akvName}'
4043
param storageId string = '/subscriptions/${SubId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Storage/storageAccounts/${storageAccountName}'
44+
param amrId string = '/subscriptions/${SubId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Cache/redisEnterprise/${amrName}'
4145

4246
//1. Create a private endpoint for the SQL Server
4347

@@ -373,7 +377,7 @@ resource privateDnsZoneARecordAKV 'Microsoft.Network/privateDnsZones/A@2024-06-0
373377
}
374378
}
375379

376-
// 5. Create a private endpoint for the Storage Account
380+
//5. Create a private endpoint for the Storage Account
377381

378382
//Create variables for the private endpoint
379383
var storageHostName = '.blob.${environment().suffixes.storage}'
@@ -454,3 +458,81 @@ resource privateDnsZoneARecordStorage 'Microsoft.Network/privateDnsZones/A@2024-
454458
]
455459
}
456460
}
461+
462+
//6. Create private endpoint for Azure Managed Redis
463+
464+
// Create variables for the private endpoint
465+
var amrHostName = ''
466+
var privateEndpointAMRName = 'voting-AMR-PE-${servicesSubnetName}'
467+
var privateDnsZoneAMRName = 'privatelink${amrHostName}'
468+
var pvtEndpointDnsGroupAMRName = '${privateEndpointAMRName}/sbdnsgroupname'
469+
470+
// Create private endpoint
471+
resource privateEndpointAMR 'Microsoft.Network/privateEndpoints@2024-07-01' = {
472+
name: privateEndpointAMRName
473+
location: location
474+
properties: {
475+
subnet: {
476+
id: servicesSubnet.id
477+
}
478+
privateLinkServiceConnections: [
479+
{
480+
name: privateEndpointAMRName
481+
properties: {
482+
privateLinkServiceId: amrId
483+
groupIds: [
484+
'redisEnterprise'
485+
]
486+
}
487+
}
488+
]
489+
}
490+
}
491+
492+
resource privateDnsZoneAMR 'Microsoft.Network/privateDnsZones@2024-06-01' = {
493+
name: privateDnsZoneAMRName
494+
location: 'global'
495+
properties: {}
496+
}
497+
498+
resource privateDnsZoneLinkAMR 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = {
499+
parent: privateDnsZoneCosmos
500+
name: '${privateDnsZoneAMRName}-link'
501+
location: 'global'
502+
properties: {
503+
registrationEnabled: false
504+
virtualNetwork: {
505+
id: vnetId
506+
}
507+
}
508+
}
509+
510+
resource pvtEndpointDnsGroupAMR 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2024-07-01' = {
511+
name: pvtEndpointDnsGroupAMRName
512+
properties: {
513+
privateDnsZoneConfigs: [
514+
{
515+
name: 'config1'
516+
properties: {
517+
privateDnsZoneId: privateDnsZoneAMR.id
518+
}
519+
}
520+
]
521+
}
522+
dependsOn: [
523+
privateEndpointAMR
524+
]
525+
}
526+
527+
resource privateDnsZoneARecordAMR 'Microsoft.Network/privateDnsZones/A@2024-06-01' = {
528+
parent: privateDnsZoneAMR
529+
name: '${privateEndpointAMRName}.${privateDnsZoneAMRName}'
530+
properties: {
531+
ttl: 3600
532+
aRecords: [
533+
{
534+
ipv4Address: privateEndpointAMR.properties.customDnsConfigs[0].ipAddresses[0]
535+
}
536+
]
537+
}
538+
}

deployment/templates/services.bicep

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,23 @@ var resourcesStorageAccountName = toLower('resources${uniqueString(resourceGroup
2828
var resourcesStorageAccountFunctionAppName = toLower('stfa${uniqueString(resourceGroup().id)}')
2929
var resourcesContainerName = 'rscontainer'
3030
var keyVaultName = 'akeyvault1-${uniqueString(resourceGroup().id)}'
31-
31+
var amrName = 'amr-${uniqueString(resourceGroup().id)}'
32+
var amrDbName = 'default'
33+
var amrOptions = {
34+
clusteringPolicy: 'EnterpriseCluster'
35+
evictionPolicy: 'NoEviction'
36+
modulesEnabled: [
37+
'RedisBloom'
38+
'RedisTimeSeries'
39+
'RedisJSON'
40+
'RedisSearch'
41+
]
42+
aofPersistence: false
43+
aofFrequency: '1s'
44+
rdbPersistence: true
45+
rdbFrequency: '6h' // 12h, 1h, 6h
46+
}
47+
3248
resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2025-04-15' = {
3349
name: cosmosName
3450
location: location
@@ -260,6 +276,48 @@ resource resourcesStorageAccountFunctionApp 'Microsoft.Storage/storageAccounts@2
260276
}
261277
}
262278

279+
resource amr 'Microsoft.Cache/redisEnterprise@2025-05-01-preview' = {
280+
location: location
281+
name: amrName
282+
properties: {
283+
highAvailability: zoneRedundant ? 'Enabled' : 'Disabled'
284+
minimumTlsVersion: '1.2'
285+
}
286+
identity:{
287+
type: 'SystemAssigned'
288+
}
289+
sku: {
290+
name: 'Balanced_B1'
291+
}
292+
}
293+
294+
resource amrDb 'Microsoft.Cache/redisEnterprise/databases@2024-09-01-preview' = {
295+
name: amrDbName
296+
parent: amr
297+
properties: {
298+
clientProtocol:'Encrypted'
299+
port: 10000
300+
clusteringPolicy: amrOptions.clusteringPolicy
301+
evictionPolicy: amrOptions.evictionPolicy
302+
persistence: {
303+
aofEnabled: amrOptions.aofPersistence
304+
aofFrequency: amrOptions.aofPersistence ? amrOptions.aofFrequency : null
305+
rdbEnabled: amrOptions.rdbPersistence
306+
rdbFrequency: amrOptions.rdbPersistence ? amrOptions.rdbFrequency : null
307+
}
308+
modules: [for module in amrOptions.modulesEnabled: {
309+
name: module
310+
}]
311+
}
312+
}
313+
314+
resource keyVaultAmrConnectionStringSecret 'Microsoft.KeyVault/vaults/secrets@2024-11-01' = {
315+
parent: keyVault
316+
name: 'RedisConnectionString'
317+
properties: {
318+
value: '${amr.properties.hostName}:10000,abortConnect=false,ssl=true,password=${listKeys(amr.id, '2015-08-01').primaryKey}'
319+
}
320+
}
263321

264322
output cosmosDbName string = cosmosName
265323
output sqlServerName string = sqlServerName
@@ -269,3 +327,4 @@ output resourcesStorageAccountFunctionAppName string = resourcesStorageAccountFu
269327
output resourcesContainerName string = resourcesContainerName
270328
output keyVaultName string = keyVaultName
271329
output serviceBusName string = serviceBusName
330+
output amrName string = amr.name

0 commit comments

Comments
 (0)