Skip to content

Commit

Permalink
feat(azure): add redis resource (#518)
Browse files Browse the repository at this point in the history
Related to #275

- Added Redis resource to infrastructure
- Added `host name` to the key vault and app configuration to make it
accessible to the container app.
- Using managed identity to connect the web apis to Redis

~~Doesn't seem like managed identity is possible for apps that
read/write to the redis instance. We could use a pre-defined role, but
that would only enable control plane level permissions.. ¯\_(ツ)_/¯~~
  • Loading branch information
arealmaas authored Mar 6, 2024
1 parent f671b5d commit 1b2c013
Show file tree
Hide file tree
Showing 17 changed files with 178 additions and 11 deletions.
26 changes: 26 additions & 0 deletions .azure/applications/web-api-eu/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ param apimIp string
param containerAppEnvironmentName string
@minLength(3)
@secure()
param redisName string
@minLength(3)
@secure()
param appInsightConnectionString string
@minLength(5)
@secure()
Expand All @@ -33,6 +36,10 @@ resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2023-05-01'
name: containerAppEnvironmentName
}

resource redis 'Microsoft.Cache/redis@2023-08-01' existing = {
name: redisName
}

var containerAppEnvVars = [
{
name: 'ASPNETCORE_ENVIRONMENT'
Expand Down Expand Up @@ -70,6 +77,25 @@ module containerApp '../../modules/containerApp/main.bicep' = {
}
}

resource redisCustomAccessPolicy 'Microsoft.Cache/redis/accessPolicies@2023-08-01' = {
parent: redis
name: containerAppName
properties: {
permissions: 'Contributor'
}
}

resource redisCustomAccessPolicyAssignment 'Microsoft.Cache/redis/accessPolicyAssignments@2023-08-01' = {
parent: redis
name: containerAppName
properties: {
accessPolicyName: containerAppName
objectId: containerApp.outputs.identityPrincipalId
objectIdAlias: '${containerAppName}-access-policy-redis'
}
dependsOn: [redisCustomAccessPolicy]
}

module keyVaultReaderAccessPolicy '../../modules/keyvault/addReaderRoles.bicep' = {
name: 'keyVaultReaderAccessPolicy-${containerAppName}'
params: {
Expand Down
1 change: 1 addition & 0 deletions .azure/applications/web-api-eu/staging.bicepparam
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ param environmentKeyVaultName = readEnvironmentVariable('ENVIRONMENT_KEY_VAULT_N
param containerAppEnvironmentName = readEnvironmentVariable('CONTAINER_APP_ENVIRONMENT_NAME')
param appInsightConnectionString = readEnvironmentVariable('APP_INSIGHTS_CONNECTION_STRING')
param appConfigurationName = readEnvironmentVariable('APP_CONFIGURATION_NAME')
param redisName = readEnvironmentVariable('REDIS_NAME')
1 change: 1 addition & 0 deletions .azure/applications/web-api-eu/test.bicepparam
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ param environmentKeyVaultName = readEnvironmentVariable('ENVIRONMENT_KEY_VAULT_N
param containerAppEnvironmentName = readEnvironmentVariable('CONTAINER_APP_ENVIRONMENT_NAME')
param appInsightConnectionString = readEnvironmentVariable('APP_INSIGHTS_CONNECTION_STRING')
param appConfigurationName = readEnvironmentVariable('APP_CONFIGURATION_NAME')
param redisName = readEnvironmentVariable('REDIS_NAME')
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ param containerAppEnvironmentName = readEnvironmentVariable('CONTAINER_APP_ENVIR

//secrets
param adoConnectionStringSecretUri = readEnvironmentVariable('ADO_CONNECTION_STRING_SECRET_URI')
param redisName = readEnvironmentVariable('REDIS_NAME')
26 changes: 26 additions & 0 deletions .azure/applications/web-api-so/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ param apimIp string
param containerAppEnvironmentName string
@minLength(3)
@secure()
param redisName string
@minLength(3)
@secure()
param appInsightConnectionString string
@minLength(5)
@secure()
Expand All @@ -33,6 +36,10 @@ resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2023-05-01'
name: containerAppEnvironmentName
}

resource redis 'Microsoft.Cache/redis@2023-08-01' existing = {
name: redisName
}

var containerAppEnvVars = [
{
name: 'ASPNETCORE_ENVIRONMENT'
Expand Down Expand Up @@ -74,6 +81,25 @@ module containerApp '../../modules/containerApp/main.bicep' = {
}
}

resource redisCustomAccessPolicy 'Microsoft.Cache/redis/accessPolicies@2023-08-01' = {
parent: redis
name: containerAppName
properties: {
permissions: 'Contributor'
}
}

resource redisCustomAccessPolicyAssignment 'Microsoft.Cache/redis/accessPolicyAssignments@2023-08-01' = {
parent: redis
name: containerAppName
properties: {
accessPolicyName: containerAppName
objectId: containerApp.outputs.identityPrincipalId
objectIdAlias: '${containerAppName}-access-policy-redis'
}
dependsOn: [redisCustomAccessPolicy]
}

module keyVaultReaderAccessPolicy '../../modules/keyvault/addReaderRoles.bicep' = {
name: 'keyVaultReaderAccessPolicy-${containerAppName}'
params: {
Expand Down
1 change: 1 addition & 0 deletions .azure/applications/web-api-so/staging.bicepparam
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ param environmentKeyVaultName = readEnvironmentVariable('ENVIRONMENT_KEY_VAULT_N
param containerAppEnvironmentName = readEnvironmentVariable('CONTAINER_APP_ENVIRONMENT_NAME')
param appInsightConnectionString = readEnvironmentVariable('APP_INSIGHTS_CONNECTION_STRING')
param appConfigurationName = readEnvironmentVariable('APP_CONFIGURATION_NAME')
param redisName = readEnvironmentVariable('REDIS_NAME')
1 change: 1 addition & 0 deletions .azure/applications/web-api-so/test.bicepparam
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ param environmentKeyVaultName = readEnvironmentVariable('ENVIRONMENT_KEY_VAULT_N
param containerAppEnvironmentName = readEnvironmentVariable('CONTAINER_APP_ENVIRONMENT_NAME')
param appInsightConnectionString = readEnvironmentVariable('APP_INSIGHTS_CONNECTION_STRING')
param appConfigurationName = readEnvironmentVariable('APP_CONFIGURATION_NAME')
param redisName = readEnvironmentVariable('REDIS_NAME')
44 changes: 36 additions & 8 deletions .azure/infrastructure/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ param slackNotifierSku SlackNotifierSku
import {Sku as PostgresSku} from '../modules/postgreSql/create.bicep'
param postgresSku PostgresSku

import {Sku as RedisSku} from '../modules/redis/main.bicep'
param redisSku RedisSku
@minLength(1)
param redisVersion string

var secrets = {
dialogportenPgAdminPassword: dialogportenPgAdminPassword
sourceKeyVaultSubscriptionId: sourceKeyVaultSubscriptionId
Expand All @@ -49,7 +54,7 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = {
location: location
}

module keyVaultModule '../modules/keyvault/create.bicep' = {
module environmentKeyVault '../modules/keyvault/create.bicep' = {
scope: resourceGroup
name: 'keyVault'
params: {
Expand Down Expand Up @@ -104,14 +109,26 @@ module postgresql '../modules/postgreSql/create.bicep' = {
params: {
namePrefix: namePrefix
location: location
keyVaultName: keyVaultModule.outputs.name
environmentKeyVaultName: environmentKeyVault.outputs.name
srcKeyVault: srcKeyVault
srcSecretName: 'dialogportenPgAdminPassword${environment}'
administratorLoginPassword: contains(keyVaultSourceKeys, 'dialogportenPgAdminPassword${environment}') ? srcKeyVaultResource.getSecret('dialogportenPgAdminPassword${environment}') : secrets.dialogportenPgAdminPassword
sku: postgresSku
}
}

module redis '../modules/redis/main.bicep' = {
scope: resourceGroup
name: 'redis'
params: {
namePrefix: namePrefix
location: location
environmentKeyVaultName: environmentKeyVault.outputs.name
sku: redisSku
version: redisVersion
}
}

module copyEnvironmentSecrets '../modules/keyvault/copySecrets.bicep' = {
scope: resourceGroup
name: 'copyEnvironmentSecrets'
Expand All @@ -120,7 +137,7 @@ module copyEnvironmentSecrets '../modules/keyvault/copySecrets.bicep' = {
srcKeyVaultName: secrets.sourceKeyVaultName
srcKeyVaultRGNName: secrets.sourceKeyVaultResourceGroup
srcKeyVaultSubId: secrets.sourceKeyVaultSubscriptionId
destKeyVaultName: keyVaultModule.outputs.name
destKeyVaultName: environmentKeyVault.outputs.name
secretPrefix: 'dialogporten--${environment}--'
}
}
Expand All @@ -132,7 +149,7 @@ module copyCrossEnvironmentSecrets '../modules/keyvault/copySecrets.bicep' = {
srcKeyVaultName: secrets.sourceKeyVaultName
srcKeyVaultRGNName: secrets.sourceKeyVaultResourceGroup
srcKeyVaultSubId: secrets.sourceKeyVaultSubscriptionId
destKeyVaultName: keyVaultModule.outputs.name
destKeyVaultName: environmentKeyVault.outputs.name
secretPrefix: 'dialogporten--any--'
}
}
Expand All @@ -142,7 +159,7 @@ module slackNotifier '../modules/functionApp/slackNotifier.bicep' = {
scope: resourceGroup
params: {
location: location
keyVaultName: keyVaultModule.outputs.name
keyVaultName: environmentKeyVault.outputs.name
namePrefix: namePrefix
applicationInsightsName: appInsights.outputs.appInsightsName
sku: slackNotifierSku
Expand All @@ -168,7 +185,7 @@ module appInsightsReaderAccessPolicy '../modules/applicationInsights/addReaderRo
}
}

module appConfigConfigurations '../modules/appConfiguration/upsertKeyValue.bicep' = {
module postgresConnectionStringAppConfig '../modules/appConfiguration/upsertKeyValue.bicep' = {
scope: resourceGroup
name: 'AppConfig_Add_DialogDbConnectionString'
params: {
Expand All @@ -179,15 +196,26 @@ module appConfigConfigurations '../modules/appConfiguration/upsertKeyValue.bicep
}
}

module redisHostNameAppConfig '../modules/appConfiguration/upsertKeyValue.bicep' = {
scope: resourceGroup
name: 'AppConfig_Add_RedisHostName'
params: {
configStoreName: appConfiguration.outputs.name
key: 'Infrastructure:RedisHostName'
value: redis.outputs.hostName
keyValueType: 'keyVaultReference'
}
}

module keyVaultReaderAccessPolicy '../modules/keyvault/addReaderRoles.bicep' = {
scope: resourceGroup
name: 'keyVaultReaderAccessPolicyFunctions'
params: {
keyvaultName: keyVaultModule.outputs.name
keyvaultName: environmentKeyVault.outputs.name
principalIds: [ slackNotifier.outputs.functionAppPrincipalId ]
}
}

output resourceGroupName string = resourceGroup.name
output containerAppEnvId string = containerAppEnv.outputs.containerAppEnvId
output environmentKeyVaultName string = keyVaultModule.outputs.name
output environmentKeyVaultName string = environmentKeyVault.outputs.name
8 changes: 8 additions & 0 deletions .azure/infrastructure/production.bicepparam
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ param environment = 'production'
param location = 'norwayeast'
param keyVaultSourceKeys = json(readEnvironmentVariable('KEY_VAULT_SOURCE_KEYS'))

param redisVersion = '6.0'

// secrets
param dialogportenPgAdminPassword = readEnvironmentVariable('PG_ADMIN_PASSWORD')
param sourceKeyVaultSubscriptionId = readEnvironmentVariable('SOURCE_KEY_VAULT_SUBSCRIPTION_ID')
Expand All @@ -30,3 +32,9 @@ param postgresSku = {
name: 'Standard_B1ms'
tier: 'Burstable'
}

param redisSku = {
name: 'Basic'
family: 'C'
capacity: 1
}
8 changes: 8 additions & 0 deletions .azure/infrastructure/soak.bicepparam
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ param environment = 'soak'
param location = 'norwayeast'
param keyVaultSourceKeys = json(readEnvironmentVariable('KEY_VAULT_SOURCE_KEYS'))

param redisVersion = '6.0'

// secrets
param dialogportenPgAdminPassword = readEnvironmentVariable('PG_ADMIN_PASSWORD')
param sourceKeyVaultSubscriptionId = readEnvironmentVariable('SOURCE_KEY_VAULT_SUBSCRIPTION_ID')
Expand All @@ -30,3 +32,9 @@ param postgresSku = {
name: 'Standard_B1ms'
tier: 'Burstable'
}

param redisSku = {
name: 'Basic'
family: 'C'
capacity: 1
}
8 changes: 8 additions & 0 deletions .azure/infrastructure/staging.bicepparam
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ param environment = 'staging'
param location = 'norwayeast'
param keyVaultSourceKeys = json(readEnvironmentVariable('KEY_VAULT_SOURCE_KEYS'))

param redisVersion = '6.0'

// secrets
param dialogportenPgAdminPassword = readEnvironmentVariable('PG_ADMIN_PASSWORD')
param sourceKeyVaultSubscriptionId = readEnvironmentVariable('SOURCE_KEY_VAULT_SUBSCRIPTION_ID')
Expand All @@ -30,3 +32,9 @@ param postgresSku = {
name: 'Standard_B1ms'
tier: 'Burstable'
}

param redisSku = {
name: 'Basic'
family: 'C'
capacity: 1
}
8 changes: 8 additions & 0 deletions .azure/infrastructure/test.bicepparam
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ param environment = 'test'
param location = 'norwayeast'
param keyVaultSourceKeys = json(readEnvironmentVariable('KEY_VAULT_SOURCE_KEYS'))

param redisVersion = '6.0'

// secrets
param dialogportenPgAdminPassword = readEnvironmentVariable('PG_ADMIN_PASSWORD')
param sourceKeyVaultSubscriptionId = readEnvironmentVariable('SOURCE_KEY_VAULT_SUBSCRIPTION_ID')
Expand All @@ -30,3 +32,9 @@ param postgresSku = {
name: 'Standard_B1ms'
tier: 'Burstable'
}

param redisSku = {
name: 'Basic'
family: 'C'
capacity: 1
}
6 changes: 3 additions & 3 deletions .azure/modules/postgreSql/create.bicep
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
param namePrefix string
param location string
param keyVaultName string
param environmentKeyVaultName string
param srcSecretName string

@export()
Expand Down Expand Up @@ -81,7 +81,7 @@ resource postgres 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = {
module adoConnectionString '../keyvault/upsertSecret.bicep' = {
name: 'adoConnectionString'
params: {
destKeyVaultName: keyVaultName
destKeyVaultName: environmentKeyVaultName
secretName: 'dialogportenAdoConnectionString'
secretValue: 'Server=${postgres.properties.fullyQualifiedDomainName};Database=${databaseName};Port=5432;User Id=${administratorLogin};Password=${administratorLoginPassword};Ssl Mode=Require;Trust Server Certificate=true;'
}
Expand All @@ -90,7 +90,7 @@ module adoConnectionString '../keyvault/upsertSecret.bicep' = {
module psqlConnectionString '../keyvault/upsertSecret.bicep' = {
name: 'psqlConnectionString'
params: {
destKeyVaultName: keyVaultName
destKeyVaultName: environmentKeyVaultName
secretName: 'dialogportenPsqlConnectionString'
secretValue: 'psql \'host=${postgres.properties.fullyQualifiedDomainName} port=5432 dbname=${databaseName} user=${administratorLogin} password=${administratorLoginPassword} sslmode=require\''
}
Expand Down
45 changes: 45 additions & 0 deletions .azure/modules/redis/main.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
param namePrefix string
param location string
@minLength(1)
param environmentKeyVaultName string
@minLength(1)
param version string

@export()
type Sku = {
name: 'Basic' | 'Standard' | 'Premium'
family: 'C' | 'P'
@minValue(1)
capacity: int
}
param sku Sku

// https://learn.microsoft.com/en-us/azure/templates/microsoft.cache/redis?pivots=deployment-language-bicep
resource redis 'Microsoft.Cache/Redis@2023-08-01' = {
name: '${namePrefix}-redis'
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
sku: sku
enableNonSslPort: false
redisConfiguration: {
'aad-enabled': 'true'
'maxmemory-policy': 'allkeys-lru'
}
redisVersion: version
}
}

module redisConnectionString '../keyvault/upsertSecret.bicep' = {
name: 'redisHostName'
params: {
destKeyVaultName: environmentKeyVaultName
secretName: 'dialogportenRedisHostName'
// disable public access? Use vnet here maybe?
secretValue: redis.properties.hostName
}
}

output hostName string = redis.properties.hostName
3 changes: 3 additions & 0 deletions .github/workflows/action-deploy-apps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ on:
required: true
AZURE_ADO_CONNECTION_STRING_SECRET_URI:
required: true
AZURE_REDIS_NAME:
required: true

inputs:
region:
Expand Down Expand Up @@ -147,6 +149,7 @@ jobs:
APP_INSIGHTS_CONNECTION_STRING: ${{ secrets.AZURE_APP_INSIGHTS_CONNECTION_STRING }}
APP_CONFIGURATION_NAME: ${{ secrets.AZURE_APP_CONFIGURATION_NAME }}
ENVIRONMENT_KEY_VAULT_NAME: ${{ secrets.AZURE_ENVIRONMENT_KEY_VAULT_NAME }}
REDIS_NAME: ${{ secrets.AZURE_REDIS_NAME }}
with:
scope: resourcegroup
template: ./.azure/applications/${{ matrix.name }}/main.bicep
Expand Down
Loading

0 comments on commit 1b2c013

Please sign in to comment.