Skip to content

Commit

Permalink
convert to key vault secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
cephalin committed Sep 6, 2024
1 parent c600a37 commit b0377e3
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 15 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ Because the Linux .NET container in App Service doesn't come with the .NET SDK,
- In the [.csproj](DotNretCoreSqlDb.csproj) file, include the generated *migrationsbundle* file. During the `azd package` stage, *migrationsbundle* will be added to the deploy package.
- In [infra/resources.bicep](infra/resources.bicep), add the `appCommandLine` property to the web app to run the uploaded *migrationsbundle*.

## How does the AZD template configure passwords?

Two types of secrets are involved: the SQL Database administrator password and the access key for Cache for Redis, and they are both present in the respective connection strings. The [AZD template](infra/resources.bicep) in this repo manages both connection strings in a key vault that's secured behind a private endpoint.
To simplify the scenario, the AZD template generates a new database password each time you run `azd provision` or `azd up`, and the database connection string in the key vault is modified too. If you want to fully utilize `secretOrRandomPassword` in the [parameter file](infra/main.parameters.json) by committing the automatically generated password to the key vault the first time and reading it on subsequent `azd` commands, you must relax the networking restriction of the key vault to allow traffic from public networks. For more information, see [What is the behavior of the `secretOrRandomPassword` function?](https://learn.microsoft.com/azure/developer/azure-developer-cli/faq#what-is-the-behavior-of-the--secretorrandompassword--function).
## Getting help
If you're working with this project and running into issues, please post in [Issues](/issues).
5 changes: 5 additions & 0 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ param name string
@description('Primary location for all resources')
param location string

@secure()
@description('SQL Server administrator password')
param databasePassword string = ''

param principalId string = ''

var resourceToken = toLower(uniqueString(subscription().id, name, location))
Expand All @@ -26,6 +30,7 @@ module resources 'resources.bicep' = {
name: name
location: location
resourceToken: resourceToken
databasePassword: databasePassword
principalId: principalId
}
}
Expand Down
53 changes: 38 additions & 15 deletions infra/resources.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ param name string
param location string
param resourceToken string
param principalId string
@secure()
param databasePassword string

var appName = '${name}-${resourceToken}'

Expand Down Expand Up @@ -211,34 +213,47 @@ resource privateDnsZoneCache 'Microsoft.Network/privateDnsZones@2020-06-01' = {
}
}

// The Key Vault is used to manage Redis secrets.
// The Key Vault is used to manage SQL database and redis secrets.
// Current user has the admin permissions to configure key vault secrets, but by default doesn't have the permissions to read them.
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
name: '${take(replace(appName, '-', ''), 17)}-vault'
location: location
properties: {
enableRbacAuthorization: true
tenantId: subscription().tenantId
sku: { family: 'A', name: 'standard' }
publicNetworkAccess: 'Disabled'
// Only allow requests from the private endpoint in the VNET.
publicNetworkAccess: 'Disabled' // To see the secret in the portal, change to 'Enabled'
networkAcls: {
defaultAction: 'Deny'
bypass: 'AzureServices'
ipRules: []
virtualNetworkRules: []
defaultAction: 'Deny' // To see the secret in the portal, change to 'Allow'
bypass: 'None'
}
}
}

// Grant the current user with key vault secret user role permissions over the key vault. This lets you inspect the secrets, such as in the portal
// If you remove this section, you can't read the key vault secrets, but the app still has access with its managed identity.
resource keyVaultSecretUserRoleRoleDefinition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
scope: subscription()
name: '4633458b-17de-408a-b874-0445c86b69e6' // The built-in Key Vault Secret User role
}
resource keyVaultSecretUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-08-01-preview' = {
scope: keyVault
name: guid(resourceGroup().id, principalId, keyVaultSecretUserRoleRoleDefinition.id)
properties: {
roleDefinitionId: keyVaultSecretUserRoleRoleDefinition.id
principalId: principalId
principalType: 'User'
}
}

// The SQL Database server is configured to be the minimum pricing tier
// It also uses Microsoft Entra authentication with the current user as the administrator
resource dbserver 'Microsoft.Sql/servers@2023-05-01-preview' = {
location: location
name: '${appName}-server'
properties: {
administrators: {
login: '${appName}-server-admin'
sid: principalId
}
administratorLogin: '${appName}-server-admin'
administratorLoginPassword: databasePassword
publicNetworkAccess: 'Disabled'
restrictOutboundNetworkAccess: 'Disabled'
}
Expand Down Expand Up @@ -319,7 +334,7 @@ resource web 'Microsoft.Web/sites@2022-09-01' = {
properties: {
applicationLogs: {
fileSystem: {
level: 'Verbose'
level: 'Information'
}
}
detailedErrorMessages: {
Expand Down Expand Up @@ -356,7 +371,7 @@ resource vaultConnector 'Microsoft.ServiceLinker/linkers@2024-04-01' = {
scope: web
name: 'vaultConnector'
properties: {
clientType: 'springBoot'
clientType: 'dotnet'
targetService: {
type: 'AzureResource'
id: keyVault.id
Expand All @@ -383,7 +398,15 @@ resource dbConnector 'Microsoft.ServiceLinker/linkers@2024-04-01' = {
id: dbserver::db.id
}
authInfo: {
authType: 'systemAssignedIdentity'
authType: 'secret'
name: '${appName}-server-admin'
secretInfo: {
secretType: 'rawValue'
value: databasePassword
}
}
secretStore: {
keyVaultId: keyVault.id // Configure secrets as key vault references. No secret is exposed in App Service.
}
clientType: 'dotnet-connectionString' // Generate a .NET connection string. For app setting, use 'dotnet' instead
vNetSolution: {
Expand All @@ -403,7 +426,7 @@ resource cacheConnector 'Microsoft.ServiceLinker/linkers@2024-04-01' = {
id: resourceId('Microsoft.Cache/Redis/Databases', redisCache.name, '0')
}
authInfo: {
authType: 'accessKey' // Configure secrets as Key Vault references. No secret is exposed in App Service.
authType: 'accessKey' // Configure secrets as key vault references. No secret is exposed in App Service.
}
secretStore: {
keyVaultId: keyVault.id
Expand Down

0 comments on commit b0377e3

Please sign in to comment.