/#blog-post:deploy-an-azure-function-app-connected-to-cosmos-db-with-secrets-on-key-vault-using-bicep
Last active
January 28, 2022 17:31
-
-
Save mariomeyrelles/d0c59b6ea7c48354ccdd738a37647052 to your computer and use it in GitHub Desktop.
Code samples from blog post: Deploy an Azure Function App connected to Cosmos DB with secrets on Key Vault using Bicep
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Example of a complete deployment for a more complex app |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@description('Application Name - change it!') | |
param appName string = 'sampleApp' | |
param location string = resourceGroup().location | |
param tenantId string = tenant().tenantId | |
@description('This is the object id of the user who will do the deployment on Azure. Can be your user id on AAD. Discover it running [az ad signed-in-user show] and get the [objectId] property.') | |
param deploymentOperatorId string | |
// a 4-char suffix to add to the various names of azure resources to help them be unique, but still, predictable | |
var appSuffix = substring(uniqueString(resourceGroup().id),0,4) | |
// grants access to the operator of this deployment with specific roles like key vault access. | |
module operatorSetup 'operatorSetup.bicep' = { | |
name: 'operatorSetup-deployment' | |
params: { | |
operatorPrincipalId: deploymentOperatorId | |
appName: appName | |
} | |
} | |
// creates an user-assigned managed identity that will used by different azure resources to access each other. | |
module msi 'msi.bicep' = { | |
name: 'msi-deployment' | |
params: { | |
location: location | |
managedIdentityName: '${appName}Identity' | |
operatorRoleDefinitionId: operatorSetup.outputs.roleId | |
} | |
} | |
// creates a key vault in this resource group | |
module keyvault 'keyvault.bicep' = { | |
name: 'keyvault-deployment' | |
params: { | |
location: location | |
appName: appName | |
tenantId: tenantId | |
} | |
} | |
// creates the cosmos db account and database with some containers configured. Saves connection string in keyvault. | |
module cosmos 'cosmosDb.bicep' = { | |
name: 'cosmos-deployment' | |
params:{ | |
cosmosAccountId: '${appName}-${appSuffix}' | |
location: location | |
cosmosDbName: appName | |
keyVaultName: keyvault.outputs.keyVaultName | |
} | |
} | |
// creates a Log Analytics + Application Insights instance | |
module logAnalytics 'logAnalytics.bicep' = { | |
name: 'log-analytics-deployment' | |
params: { | |
appName: appName | |
} | |
} | |
// creates an azure function, with secrets stored in the key vault | |
module azureFunctions_api 'functionApp.bicep' = { | |
name: 'functions-app-deployment-api' | |
params: { | |
appName: appName | |
appInternalServiceName: 'api' | |
appNameSuffix: appSuffix | |
appInsightsInstrumentationKey: logAnalytics.outputs.instrumentationKey | |
keyVaultName: keyvault.outputs.keyVaultName | |
msiRbacId: msi.outputs.id | |
} | |
dependsOn: [ | |
keyvault | |
logAnalytics | |
] | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
az ad signed-in-user show |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
resource roledefinition_deploymentOperator 'Microsoft.Authorization/roleDefinitions@2018-07-01' = { | |
name: guid('deployment-operator', appName) | |
properties: { | |
roleName: 'Operator role for app ${appName}' | |
description: 'This role orchestrates this deployment and allows the communication between the components in this solution.' | |
assignableScopes: [ | |
resourceGroup().id | |
] | |
permissions: [ | |
{ | |
actions: [ | |
'Microsoft.Authorization/*/read' | |
'Microsoft.Insights/alertRules/*' | |
'Microsoft.Resources/deployments/*' | |
'Microsoft.Resources/subscriptions/resourceGroups/read' | |
'Microsoft.Support/*' | |
'Microsoft.KeyVault/checkNameAvailability/read' | |
'Microsoft.KeyVault/deletedVaults/read' | |
'Microsoft.KeyVault/locations/*/read' | |
'Microsoft.KeyVault/vaults/*' | |
'Microsoft.KeyVault/operations/read' | |
] | |
dataActions:[ | |
'Microsoft.KeyVault/vaults/*' | |
] | |
notActions: [] | |
notDataActions: [] | |
} | |
] | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
resource roledefinition_deploymentOperator 'Microsoft.Authorization/roleDefinitions@2018-07-01' = { | |
name: guid('deployment-operator', appName) | |
properties: { | |
roleName: 'Operator role for app ${appName}' | |
description: 'This role orchestrates this deployment and allows the communication between the components in this solution.' | |
assignableScopes: [ | |
resourceGroup().id | |
] | |
permissions: [ | |
{ | |
actions: [ | |
'Microsoft.Authorization/*/read' | |
'Microsoft.Insights/alertRules/*' | |
'Microsoft.Resources/deployments/*' | |
'Microsoft.Resources/subscriptions/resourceGroups/read' | |
'Microsoft.Support/*' | |
'Microsoft.KeyVault/checkNameAvailability/read' | |
'Microsoft.KeyVault/deletedVaults/read' | |
'Microsoft.KeyVault/locations/*/read' | |
'Microsoft.KeyVault/vaults/*' | |
'Microsoft.KeyVault/operations/read' | |
] | |
dataActions:[ | |
'Microsoft.KeyVault/vaults/*' | |
] | |
notActions: [] | |
notDataActions: [] | |
} | |
] | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// operatorSetup.bicep, called by main.bicep | |
@description('principalId of the user that will be given the permissions needed to operate this deployment.') | |
param operatorPrincipalId string | |
@description('Application name, used to compose the name of the role definitions.') | |
param appName string | |
var roleAssignmentName = guid(resourceGroup().id, roledefinition_deploymentOperator.id, operatorPrincipalId, appName) | |
// assigns the role definition above to the user who will perform the deployment. | |
resource keyvault_roleAssignment_deploymentOperator 'Microsoft.Authorization/roleAssignments@2020-08-01-preview' = { | |
name: roleAssignmentName | |
scope: resourceGroup() | |
properties: { | |
roleDefinitionId: roledefinition_deploymentOperator.id // remember that this the user who is doing the deployment! | |
principalId: operatorPrincipalId | |
} | |
} | |
output roleId string = roledefinition_deploymentOperator.id | |
output roleName string = roledefinition_deploymentOperator.name // just for troubleshooting if you need it | |
output roleType string = roledefinition_deploymentOperator.type // just for troubleshooting if you need it |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
az deployment group create --resource-group sample-app-blog-post-eastus2 --template-file .\src\main.bicep --parameters deploymentOperatorId=aaaabbbb-ccdd-ee11-2233-444455667788 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// keyvault.bicep, called by main.bicep | |
param appName string | |
@maxLength(24) | |
param vaultName string = '${'kv-'}${appName}-${substring(uniqueString(resourceGroup().id), 0, 23 - (length(appName) + 3))}' // must be globally unique | |
param location string = resourceGroup().location | |
param sku string = 'Standard' | |
param tenantId string // replace with your tenantId | |
param enabledForDeployment bool = true | |
param enabledForTemplateDeployment bool = true | |
param enabledForDiskEncryption bool = true | |
param enableRbacAuthorization bool = true | |
param softDeleteRetentionInDays int = 90 | |
param networkAcls object = { | |
ipRules: [] | |
virtualNetworkRules: [] | |
} | |
resource keyvault 'Microsoft.KeyVault/vaults@2021-06-01-preview' = { | |
name: vaultName | |
location: location | |
properties: { | |
tenantId: tenantId | |
sku: { | |
family: 'A' | |
name: sku | |
} | |
enabledForDeployment: enabledForDeployment | |
enabledForDiskEncryption: enabledForDiskEncryption | |
enabledForTemplateDeployment: enabledForTemplateDeployment | |
softDeleteRetentionInDays: softDeleteRetentionInDays | |
enableRbacAuthorization: enableRbacAuthorization | |
networkAcls: networkAcls | |
// note: no need for access policies here! | |
} | |
} | |
output keyVaultName string = keyvault.name | |
output keyVaultId string = keyvault.id |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@maxLength(30) | |
param cosmosAccountId string | |
param location string | |
param cosmosDbName string | |
param keyVaultName string | |
param tags object = { | |
'deploymentGroup': 'cosmosdb' | |
} | |
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2021-10-15' = { | |
name: toLower(cosmosAccountId) | |
location: location | |
kind: 'GlobalDocumentDB' | |
properties: { | |
publicNetworkAccess: 'Enabled' | |
enableFreeTier: false | |
databaseAccountOfferType: 'Standard' | |
consistencyPolicy: { | |
defaultConsistencyLevel: 'Session' | |
maxIntervalInSeconds: 5 | |
maxStalenessPrefix: 100 | |
} | |
locations: [ | |
{ | |
locationName: location | |
failoverPriority: 0 | |
isZoneRedundant: false | |
} | |
] | |
capabilities: [ | |
{ | |
name: 'EnableServerless' | |
} | |
] | |
} | |
} | |
resource cosmosDb_database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2021-06-15' = { | |
name: '${cosmosAccount.name}/${cosmosDbName}' | |
tags: tags | |
properties: { | |
resource: { | |
id: cosmosDbName | |
} | |
} | |
} | |
resource container_leases 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2021-06-15' = { | |
name: '${cosmosDb_database.name}/leases' | |
tags: tags | |
dependsOn: [ | |
cosmosAccount | |
] | |
properties: { | |
resource: { | |
id: 'leases' | |
partitionKey: { | |
paths: [ | |
'/id' | |
] | |
} | |
} | |
} | |
} | |
resource container_employees 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2021-06-15' = { | |
name: '${cosmosDb_database.name}/Employees' | |
tags: tags | |
dependsOn: [ | |
cosmosAccount | |
] | |
properties: { | |
resource: { | |
id: 'Employees' | |
partitionKey: { | |
paths: [ | |
'/EmployeeId' | |
] | |
} | |
uniqueKeyPolicy: { | |
uniqueKeys: [ | |
{ | |
paths: [ | |
'/EmployeeId' | |
] | |
} | |
] | |
} | |
} | |
} | |
} | |
module setCosmosConnectionString 'setSecret.bicep' = { | |
name: 'setCosmosConnectionString' | |
params: { | |
keyVaultName: keyVaultName | |
secretName: 'CosmosDbConnectionString' | |
secretValue: cosmosAccount.listConnectionStrings().connectionStrings[0].connectionString | |
} | |
} | |
output cosmosAccountId string = cosmosAccountId |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
param keyVaultName string | |
param secretName string | |
param secretValue string | |
resource secret 'Microsoft.KeyVault/vaults/secrets@2021-06-01-preview' = { | |
name: '${keyVaultName}/${secretName}' | |
properties: { | |
value: secretValue | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// logAnalytics.bicep, called by main.bicep | |
param appName string | |
param location string = resourceGroup().location | |
var logAnalyticsWorkspaceName = 'log-${appName}' | |
var appInsightsName = 'appi-${appName}' | |
// Log Analytics workspace is required for new Application Insights deployments | |
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { | |
location: location | |
name: logAnalyticsWorkspaceName | |
properties: { | |
retentionInDays: 30 | |
features: { | |
enableLogAccessUsingOnlyResourcePermissions: true | |
} | |
sku: { | |
name: 'PerGB2018' | |
} | |
publicNetworkAccessForIngestion: 'Enabled' | |
publicNetworkAccessForQuery: 'Enabled' | |
} | |
} | |
// App Insights resource | |
resource appInsights 'Microsoft.Insights/components@2020-02-02' = { | |
name: appInsightsName | |
location: location | |
kind: 'web' | |
properties: { | |
Application_Type: 'web' | |
WorkspaceResourceId: logAnalyticsWorkspace.id | |
publicNetworkAccessForIngestion: 'Enabled' | |
publicNetworkAccessForQuery: 'Enabled' | |
} | |
} | |
output instrumentationKey string = appInsights.properties.InstrumentationKey |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// functionApp.bicep, called by main.bicep | |
// note: https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations | |
param location string = resourceGroup().location | |
param functionRuntime string = 'dotnet' | |
@description('A name for this whole project, used to help name individual resources') | |
param appName string | |
@description('The name of the role or service of this function. Example: Api CommandHandler, EventHandler') | |
param appInternalServiceName string | |
@description('Id of a existing keyvault that will be used to store and retrieve keys in this deployment') | |
param keyVaultName string | |
@description('User-assigned managed identity that will be attached to this function and will have power to connect to different resources.') | |
param msiRbacId string | |
@description('Application insights instrumentation key.') | |
param appInsightsInstrumentationKey string | |
param deploymentDate string = utcNow() | |
param appNameSuffix string | |
var functionAppName = 'func-${appName}-${appInternalServiceName}-${appNameSuffix}' | |
var appServiceName = 'ASP-${appName}${appInternalServiceName}-${appNameSuffix}' | |
// remove dashes for storage account name | |
var storageAccountName = toLower(format('st{0}', replace('${appInternalServiceName}-${appNameSuffix}', '-', ''))) | |
var appTags = { | |
AppID: '${appName}-${appInternalServiceName}' | |
AppName: '${appName}-${appInternalServiceName}' | |
} | |
// Storage Account - I am using 1 storage account for each function. It would be potentially shared across many function apps. | |
resource storageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' = { | |
name: storageAccountName | |
location: location | |
sku: { | |
name: 'Standard_LRS' | |
} | |
kind: 'StorageV2' | |
properties: { | |
supportsHttpsTrafficOnly: true | |
minimumTlsVersion: 'TLS1_2' | |
encryption: { | |
services: { | |
file: { | |
keyType: 'Account' | |
enabled: true | |
} | |
blob: { | |
keyType: 'Account' | |
enabled: true | |
} | |
} | |
keySource: 'Microsoft.Storage' | |
} | |
accessTier: 'Hot' | |
} | |
tags: appTags | |
} | |
// Blob Services for Storage Account | |
resource blobServices 'Microsoft.Storage/storageAccounts/blobServices@2019-06-01' = { | |
parent: storageAccount | |
name: 'default' | |
properties: { | |
cors: { | |
corsRules: [] | |
} | |
deleteRetentionPolicy: { | |
enabled: true | |
days: 7 | |
} | |
} | |
} | |
resource keyVault 'Microsoft.KeyVault/vaults@2019-09-01' existing = { | |
name: keyVaultName | |
scope: resourceGroup() | |
} | |
module setStorageAccountSecret 'setSecret.bicep' = { | |
name: 'stgSecret-${appInternalServiceName}-${deploymentDate}' | |
params: { | |
keyVaultName: keyVault.name | |
secretName: '${storageAccount.name}-${appInternalServiceName}-ConnectionString' | |
secretValue: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}' | |
} | |
} | |
// App Service | |
resource appService 'Microsoft.Web/serverfarms@2020-12-01' = { | |
name: appServiceName | |
location: location | |
kind: 'functionapp' | |
sku: { | |
name: 'Y1' | |
tier: 'Dynamic' | |
size: 'Y1' | |
family: 'Y' | |
capacity: 0 | |
} | |
properties: { | |
maximumElasticWorkerCount: 1 | |
targetWorkerCount: 0 | |
targetWorkerSizeId: 0 | |
} | |
tags: appTags | |
} | |
// Function App | |
resource functionApp 'Microsoft.Web/sites@2020-12-01' = { | |
name: functionAppName | |
location: location | |
identity: { | |
type: 'SystemAssigned, UserAssigned' | |
userAssignedIdentities: { | |
'${msiRbacId}': {} | |
} | |
} | |
kind: 'functionapp' | |
properties: { | |
keyVaultReferenceIdentity: msiRbacId | |
enabled: true | |
hostNameSslStates: [ | |
{ | |
name: '${functionAppName}.azurewebsites.net' | |
sslState: 'Disabled' | |
hostType: 'Standard' | |
} | |
{ | |
name: '${functionAppName}.scm.azurewebsites.net' | |
sslState: 'Disabled' | |
hostType: 'Standard' | |
} | |
] | |
serverFarmId: appService.id | |
siteConfig: { | |
appSettings: [ | |
{ | |
name: 'AzureWebJobsStorage' | |
value: '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=${storageAccount.name}-${appInternalServiceName}-ConnectionString)' | |
} | |
{ | |
name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' | |
value: '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=${storageAccount.name}-${appInternalServiceName}-ConnectionString)' | |
} | |
{ | |
name: 'APPINSIGHTS_INSTRUMENTATIONKEY' | |
value: appInsightsInstrumentationKey | |
} | |
{ | |
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' | |
value: 'InstrumentationKey=${appInsightsInstrumentationKey}' | |
} | |
{ | |
name: 'FUNCTIONS_WORKER_RUNTIME' | |
value: functionRuntime | |
} | |
{ | |
name: 'FUNCTIONS_EXTENSION_VERSION' | |
value: '~3' | |
} | |
{ | |
name: 'CosmosDbConnectionString' | |
value: '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=CosmosDbConnectionString)' | |
} | |
] | |
use32BitWorkerProcess: true | |
} | |
scmSiteAlsoStopped: false | |
clientAffinityEnabled: false | |
clientCertEnabled: false | |
hostNamesDisabled: false | |
dailyMemoryTimeQuota: 0 | |
httpsOnly: false | |
redundancyMode: 'None' | |
} | |
tags: appTags | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
identity: { | |
type: 'SystemAssigned, UserAssigned' | |
userAssignedIdentities: { | |
'${msiRbacId}': {} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'@Microsoft.KeyVault(VaultName=kv-some-keyVault;SecretName=CosmosDbConnectionString)' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
az keyvault purge -n kv-sampleApp-1234 --no-wait |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
az ad signed-in-user show |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
az role definition list > roleDefs.out |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
az role assignment list > roleAssignments.out |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
az role assignment delete --id "/subscriptions/xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/providers/Microsoft.Authorization/roleAssignments/<object id of the identity>" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment