Skip to content

Commit

Permalink
Provide optional parameters to support AZ redundancy for MySQL and We…
Browse files Browse the repository at this point in the history
…b App and HA for MySQL (#89)

* Add MySQL High Availability support; AZ support for MySQL and App Svc
* Add support for adding an NSG to all subnets
* Default to P0v3 App Service tier.
* Correct REDCap capitalization
* Update API version of Microsoft.Web provider
* Remove allowed values for environment param
* Add support for specifying REDCap version downloaded from community
* Set default MySQL SKU to B1ms
  • Loading branch information
SvenAelterman authored Nov 12, 2024
1 parent 7434d50 commit 2c53beb
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 27 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ To deploy the REDCap source to Azure App Service, you must supply your REDCap Co

(1) Review <https://learn.microsoft.com/azure/mysql/flexible-server/concepts-service-tiers-storage> for details on available features, regions, and pricing models for Azure DB for MySQL.

Advanced deployments can enable high availability for the MySQL Flexible Server and availability zone redundancy for the MySQL Flexible Server and web app. These capabilities can be controlled using parameters for the Bicep deployment.

<!--(2) SendGrid is a paid service with a free tier offering 25k messages per month, with additional paid tiers offering more volume, whitelisting, custom domains, etc. There is a limit of two instances per subscription using the free tier. For more information see <https://docs.microsoft.com/en-us/azure/store-sendgrid-php-how-to-send-email#create-a-sendgrid-account>. The service will be accessed initially using the password you enter in the deployment template. You can click "Manage" on the SendGrid service after deployment to administrate the service in their portal, including options to create an API key that can be used for access instead of the password.
If after deployment, you would instead like to use a different SMTP relay, edit the values "smtp_fqdn_name", "smtp_port", "smtp_user_name", and "smtp_password" to point to your preferred endpoint. You can then delete the SendGrid service from this resource group.
Expand Down
28 changes: 25 additions & 3 deletions main-sample.bicepparam
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ param vnetAddressSpace = '10.0.0.0/24'
// Do not specify a URL if you are using this option. The deployment script will download the zip file from the REDCap community.
param redcapZipUrl = '<Valid Redcap Zip URL>'
// -- OR --
param redcapCommunityUsername = '<Valid Redcap Community Username>'
param redcapCommunityPassword = '<Valid Redcap Community Password>'
param redcapCommunityUsername = '<Valid REDCap Community Username>'
param redcapCommunityPassword = '<Valid REDCap Community Password>'

// These values are used to configure the App Service Deployment Center.
// The defaults below are the Microsoft-maintained Azure REDCap PaaS repository.
Expand All @@ -41,4 +41,26 @@ param smtpFromEmailAddress = '<Specify valid SMTP From Email Address>'
// A new password is generated for each deployment and stored in Key Vault.
param sqlPassword = ''

param appServiceTimeZone = 'UTC'
// param existingVirtualNetworkId = '/subscriptions/c236b7b3-dec1-47a6-856c-8c1f45d88575/resourceGroups/redcap-networkexisting-test-rg-cnc-01/providers/Microsoft.Network/virtualNetworks/redcap-existing-demo-vnet-cnc-01'
// param existingPrivateDnsZonesResourceGroupId = '/subscriptions/c236b7b3-dec1-47a6-856c-8c1f45d88575/resourceGroups/redcap-networkexisting-test-rg-cnc-01'

// param subnets = {
// PrivateLinkSubnet: {
// existingSubnetName: 'PlSubnet'
// }
// MySQLFlexSubnet: {
// existingSubnetName: 'SqlSubnet'
// }
// IntegrationSubnet: {
// existingSubnetName: 'WebAppSubnet'
// }
// }

// param mySqlSkuTier = 'GeneralPurpose'
// param mySqlHighAvailability = 'Enabled'
// param mySqlSkuName = 'Standard_D4ds_v4'
// param availabilityZonesEnabled = true
// param mySqlStorageSizeGB = 200
// param mySqlStorageIops = 1200

// param appServiceTimeZone = 'UTC'
51 changes: 40 additions & 11 deletions main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ targetScope = 'subscription'
param location string = 'eastus'

@description('The environment designator for the deployment. Replaces {env} in namingConvention.')
@allowed([
'test'
'demo'
'prod'
])
// @allowed([
// 'test'
// 'demo'
// 'prod'
// ])
param environment string = 'demo'
@description('The workload name. Replaces {workloadName} in namingConvention.')
param workloadName string = 'redcap'
Expand All @@ -34,6 +34,8 @@ param redcapCommunityUsername string
@description('REDCap Community site password for downloading the REDCap zip file.')
@secure()
param redcapCommunityPassword string
@description('The version of REDCap to download from the REDCap Community. This is not used when specifying a ZIP URL.')
param redcapVersion string = ''
@description('Github Repo URL where build scripts are downloaded from')
param scmRepoUrl string = 'https://github.com/microsoft/azure-redcap-paas'
@description('Github Repo Branch where build scripts are downloaded from')
Expand All @@ -49,6 +51,25 @@ param enableAppServicePrivateEndpoint bool = true
@secure()
param sqlPassword string

@description('Whether High Availability is enabled for the MySQL Flexible Server. Zone redundant or same zone HA is determined by the value of availabilityZonesEnabled.')
@allowed([
'Enabled'
'Disabled'
])
param mySqlHighAvailability string = 'Disabled'

param mySqlSkuName string = 'Standard_B1ms'

@allowed([
'GeneralPurpose'
'MemoryOptimized'
'Burstable'
])
param mySqlSkuTier string = 'Burstable'
@description('The size of the MySQL Flexible Server storage in GB. This cannot be scaled down after server creation.')
param mySqlStorageSizeGB int = 20
param mySqlStorageIops int = 396

@description('The MySQL Flexible Server admin user account name. Defaults to \'sqladmin\'.')
param sqlAdmin string = 'sqladmin'

Expand All @@ -59,6 +80,10 @@ param smtpPort string = ''
@description('The email address to use as the sender for outgoing emails.')
param smtpFromEmailAddress string = ''

param appServiceSkuName string = 'P0v3'

@description('Determines whether availability zone redundancy is enabled for the MySQL Flexible Server and the app service. The region must support availability zones.')
param availabilityZonesEnabled bool = false
param existingPrivateDnsZonesResourceGroupId string = ''
param existingVirtualNetworkId string = ''

Expand Down Expand Up @@ -358,10 +383,10 @@ module mySqlModule './modules/sql/main.bicep' = {
customTags: {
workloadType: 'mySqlFlexibleServer'
}
skuName: 'Standard_B1ms'
SkuTier: 'Burstable'
StorageSizeGB: 20
StorageIops: 396
skuName: mySqlSkuName
SkuTier: mySqlSkuTier
StorageSizeGB: mySqlStorageSizeGB
StorageIops: mySqlStorageIops
peSubnetId: empty(existingVirtualNetworkId)
? virtualNetworkModule.outputs.subnets.MySQLFlexSubnet.id
: '${existingVirtualNetworkId}/subnets/${subnets.MySQLFlexSubnet.existingSubnetName}'
Expand All @@ -373,6 +398,9 @@ module mySqlModule './modules/sql/main.bicep' = {
// TODO: Consider using workloadname + 'db'
databaseName: 'redcapdb'

highAvailability: mySqlHighAvailability
availabilityZonesEnabled: availabilityZonesEnabled

roles: rolesModule.outputs.roles

uamiId: uamiModule.outputs.id
Expand Down Expand Up @@ -405,8 +433,7 @@ module webAppModule './modules/webapp/main.bicep' = {
webAppName: webAppName
appServicePlanName: planName
location: location
// Deploy as P0V3 to ensure the deployment runs on a scale unit that supports P_v3 for future upgrades. GH issue #50
skuName: 'P0V3'
skuName: appServiceSkuName
peSubnetId: privateEndpointSubnetId
appInsights_connectionString: monitoring.outputs.appInsightsResourceId
appInsights_instrumentationKey: monitoring.outputs.appInsightsInstrumentationKey
Expand All @@ -429,6 +456,7 @@ module webAppModule './modules/webapp/main.bicep' = {

redcapCommunityUsernameSecretRef: kvSecretReferencesModule.outputs.keyVaultRefs[1]
redcapCommunityPasswordSecretRef: kvSecretReferencesModule.outputs.keyVaultRefs[0]
redcapVersion: redcapVersion

storageAccountKeySecretRef: kvSecretReferencesModule.outputs.keyVaultRefs[4]
storageAccountContainerName: storageAccountModule.outputs.containerName
Expand All @@ -451,6 +479,7 @@ module webAppModule './modules/webapp/main.bicep' = {

uamiId: uamiModule.outputs.id

availabilityZonesEnabled: availabilityZonesEnabled
enablePrivateEndpoint: enableAppServicePrivateEndpoint

timeZone: appServiceTimeZone
Expand Down
6 changes: 6 additions & 0 deletions modules/networking/vnet.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ param subnets object
param tags object

param customDnsIPs array
param networkSecurityGroupId string = ''

var subnetDefsArray = items(subnets)

Expand All @@ -30,6 +31,11 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-05-01' = {
name: subnet.key
properties: {
addressPrefix: subnet.value.addressPrefix
networkSecurityGroup: !empty(networkSecurityGroupId)
? {
id: networkSecurityGroupId
}
: null
serviceEndpoints: subnet.value.?serviceEndpoints
delegations: contains(subnet.value, 'delegation') && !empty(subnet.value.delegation)
? [
Expand Down
11 changes: 11 additions & 0 deletions modules/sql/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ param skuName string = 'Standard_B1ms'
])
param SkuTier string

@allowed([
'Enabled'
'Disabled'
])
param highAvailability string = 'Disabled'

param availabilityZonesEnabled bool = false

@description('Azure database for MySQL storage Size ')
param StorageSizeGB int = 20

Expand Down Expand Up @@ -81,6 +89,9 @@ module mysqlDbserver './sql.bicep' = {
database_charset: database_charset
database_collation: database_collation

highAvailability: (highAvailability == 'Enabled') ? true : false
availabilityZonesEnabled: availabilityZonesEnabled

roles: roles
uamiId: uamiId
uamiPrincipalId: uamiPrincipalId
Expand Down
16 changes: 8 additions & 8 deletions modules/sql/sql.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ param tags object

// TODO: skuName and SkuTier are related; should be specified as a single object param, IMHO
@description('Azure database for MySQL sku name ')
param skuName string = 'Standard_B1s'
param skuName string = 'Standard_B1ms'

@description('Azure database for MySQL pricing tier')
@allowed([
Expand Down Expand Up @@ -53,11 +53,9 @@ param geoRedundantBackup string = 'Disabled'

param backupRetentionDays int = 7

@allowed([
'Enabled'
'Disabled'
])
param highAvailability string = 'Disabled'
param highAvailability bool = false

param availabilityZonesEnabled bool = false

@allowed([
'Enabled'
Expand Down Expand Up @@ -90,7 +88,9 @@ resource server 'Microsoft.DBforMySQL/flexibleServers@2022-09-30-preview' = {
geoRedundantBackup: geoRedundantBackup
}
highAvailability: {
mode: highAvailability
mode: (highAvailability && availabilityZonesEnabled)
? 'ZoneRedundant'
: (highAvailability) ? 'SameZone' : 'Disabled'
}
network: {
delegatedSubnetResourceId: peSubnetId
Expand Down Expand Up @@ -143,7 +143,7 @@ resource dbConfigDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10
scriptContent: 'az mysql flexible-server parameter set -g ${resourceGroup().name} --server-name ${server.name} --name sql_generate_invisible_primary_key --value OFF'
}
tags: tags
dependsOn: [ uamiMySqlRoleAssignmentModule ]
dependsOn: [uamiMySqlRoleAssignmentModule]
}

output mySqlServerName string = server.name
Expand Down
5 changes: 5 additions & 0 deletions modules/webapp/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ param privateDnsZoneName string
param virtualNetworkId string
param integrationSubnetId string

param availabilityZonesEnabled bool = false

param smtpFQDN string = ''
param smtpPort string = ''
param smtpFromEmailAddress string = ''
Expand All @@ -33,6 +35,7 @@ param scmRepoUrl string
param scmRepoBranch string
@secure()
param redcapZipUrl string
param redcapVersion string = ''
#disable-next-line secure-secrets-in-params
param redcapCommunityUsernameSecretRef string
#disable-next-line secure-secrets-in-params
Expand Down Expand Up @@ -78,6 +81,7 @@ module appService 'webapp.bicep' = {
redcapZipUrl: redcapZipUrl
redcapCommunityUsernameSecretRef: redcapCommunityUsernameSecretRef
redcapCommunityPasswordSecretRef: redcapCommunityPasswordSecretRef
redcapVersion: redcapVersion

scmRepoUrl: scmRepoUrl
scmRepoBranch: scmRepoBranch
Expand All @@ -93,6 +97,7 @@ module appService 'webapp.bicep' = {

uamiId: uamiId

availabiltyZonesEnabled: availabilityZonesEnabled
enablePrivateEndpoint: enablePrivateEndpoint

timeZone: timeZone
Expand Down
16 changes: 11 additions & 5 deletions modules/webapp/webapp.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ param redcapZipUrl string
param redcapCommunityUsernameSecretRef string
#disable-next-line secure-secrets-in-params
param redcapCommunityPasswordSecretRef string
param redcapVersion string = ''
param scmRepoUrl string
param scmRepoBranch string
param prerequisiteCommand string

param appInsights_connectionString string
param appInsights_instrumentationKey string

param availabiltyZonesEnabled bool = false
param enablePrivateEndpoint bool

param smtpFQDN string = ''
Expand All @@ -45,23 +47,23 @@ param minTlsVersion string = '1.2'

param uamiId string

resource appSrvcPlan 'Microsoft.Web/serverfarms@2022-03-01' = {
resource appSrvcPlan 'Microsoft.Web/serverfarms@2023-12-01' = {
name: appServicePlanName
location: location
tags: tags
sku: {
name: skuName
//tier: skuTier
}
kind: 'linux'
properties: {
reserved: true
zoneRedundant: availabiltyZonesEnabled
}
}

var DBSslCa = '/home/site/wwwroot/DigiCertGlobalRootCA.crt.pem'

resource webApp 'Microsoft.Web/sites@2022-03-01' = {
resource webApp 'Microsoft.Web/sites@2023-12-01' = {
name: webAppName
location: location
tags: tags
Expand Down Expand Up @@ -101,6 +103,10 @@ resource webApp 'Microsoft.Web/sites@2022-03-01' = {
name: 'redcapAppZip'
value: redcapZipUrl
}
{
name: 'zipVersion'
value: redcapVersion
}
{
name: 'redcapCommunityUsername'
value: redcapCommunityUsernameSecretRef
Expand Down Expand Up @@ -179,15 +185,15 @@ resource webApp 'Microsoft.Web/sites@2022-03-01' = {

// SCM Basic Authentication is required when using the App Service Build Service
// Per https://learn.microsoft.com/en-us/azure/app-service/deploy-continuous-deployment?tabs=github%2Cappservice#what-are-the-build-providers
resource basicScmCredentials 'Microsoft.Web/sites/basicPublishingCredentialsPolicies@2023-01-01' = {
resource basicScmCredentials 'Microsoft.Web/sites/basicPublishingCredentialsPolicies@2023-12-01' = {
parent: webApp
name: 'scm'
properties: {
allow: true
}
}

resource sourcecontrol 'Microsoft.Web/sites/sourcecontrols@2022-09-01' = {
resource sourcecontrol 'Microsoft.Web/sites/sourcecontrols@2023-12-01' = {
parent: webApp
name: 'web'
properties: {
Expand Down

0 comments on commit 2c53beb

Please sign in to comment.