Skip to content

Instantly share code, notes, and snippets.

@dazfuller
Last active September 10, 2024 19:44
Show Gist options
  • Save dazfuller/7a61bf3dbf7be1b977c9fa54e7cdf17d to your computer and use it in GitHub Desktop.
Save dazfuller/7a61bf3dbf7be1b977c9fa54e7cdf17d to your computer and use it in GitHub Desktop.
Example template for deploying an Azure Function app with KeyVault references all set in Bicep
/*
** Parameters
*/
@minLength(3)
@maxLength(6)
@description('Prefix to be used by all resources deployed by this template')
param resourcePrefix string = 'demo'
@allowed([
'Standard_LRS'
'Standard_GRS'
'Standard_ZRS'
'Standard_RAGRS'
'Premium_LRS'
'Premium_ZRS'
])
@description('Storage account SKU name')
param storageSkuName string = 'Standard_LRS'
@description('API key for external service')
@secure()
param apiKey string
/*
** Variables
*/
var location = resourceGroup().location
var resourceSuffix = substring(uniqueString(resourceGroup().id), 0, 7)
var storageAccountName = '${resourcePrefix}storage${resourceSuffix}'
var storageConnectionString = 'DefaultEndpointsProtocol=https;AccountName=${storage.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storage.listKeys().keys[0].value}'
var appInsightsName = '${resourcePrefix}-appinsights-${resourceSuffix}'
var appServicePlanName = '${resourcePrefix}-plan-${resourceSuffix}'
var keyVaultName = '${resourcePrefix}-kv-${resourceSuffix}'
var functionAppName = '${resourcePrefix}-funcapp-${resourceSuffix}'
var logAnalyticsName = '${resourcePrefix}-loganalytics-${resourceSuffix}'
var storageBlobDataContributorRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
var keyVaultSecretsUserRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')
/*
** Resources
*/
// Deploy the storage account and create a container to hold resources which might be used by the Function App
// later on
resource storage 'Microsoft.Storage/storageAccounts@2021-06-01' = {
name: storageAccountName
location: location
sku: {
name: storageSkuName
}
kind: 'StorageV2'
properties: {
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
encryption: {
keySource: 'Microsoft.Storage'
services: {
blob: {
enabled: true
}
file: {
enabled: true
}
queue: {
enabled: true
}
table: {
enabled: true
}
}
}
}
resource blobService 'blobServices' = {
name: 'default'
resource content 'containers' = {
name: 'content'
}
}
}
// Assign permissions to the storage account for the Azure Function app
resource storageFunctionAppPermissions 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
name: guid(storage.id, funcApp.name, storageBlobDataContributorRole)
scope: storage
properties: {
principalId: funcApp.identity.principalId
principalType: 'ServicePrincipal'
roleDefinitionId: storageBlobDataContributorRole
}
}
// Create a new Log Analytics workspace to back the Azure Application Insights instance
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
name: logAnalyticsName
location: location
properties: {
sku: {
name: 'PerGB2018'
}
retentionInDays: 30
features: {
enableLogAccessUsingOnlyResourcePermissions: true
}
workspaceCapping: {
dailyQuotaGb: 1
}
publicNetworkAccessForIngestion: 'Enabled'
publicNetworkAccessForQuery: 'Enabled'
}
}
// Application Insights instance
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: appInsightsName
location: location
kind: 'web'
properties: {
Application_Type: 'web'
publicNetworkAccessForIngestion: 'Enabled'
publicNetworkAccessForQuery: 'Enabled'
WorkspaceResourceId: logAnalytics.id
}
}
// Deploy a consumption plan instance
resource plan 'Microsoft.Web/serverfarms@2021-02-01' = {
name: appServicePlanName
location: location
kind: 'functionapp'
sku: {
name: 'Y1'
}
properties: {
}
}
// Create the KeyVault instance and add the API key as a secret to it. Enable RBAC for authorization rather than using
// access policies
resource kv 'Microsoft.KeyVault/vaults@2021-06-01-preview' = {
name: keyVaultName
location: location
properties: {
sku: {
family: 'A'
name: 'standard'
}
tenantId: subscription().tenantId
enableRbacAuthorization: true
enabledForDeployment: false
enabledForDiskEncryption: true
enabledForTemplateDeployment: false
}
resource storageNameSecret 'secrets' = {
name: 'ExternalServiceApiKey'
properties: {
contentType: 'text/plain'
value: apiKey
}
}
}
// Assign secret user permissions to the Azure Function app
resource kvFunctionAppPermissions 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
name: guid(kv.id, funcApp.name, keyVaultSecretsUserRole)
scope: kv
properties: {
principalId: funcApp.identity.principalId
principalType: 'ServicePrincipal'
roleDefinitionId: keyVaultSecretsUserRole
}
}
// Deploy the Azure Function app with application settings including one which references the API Key
// held in KeyVault
resource funcApp 'Microsoft.Web/sites@2021-02-01' = {
name: functionAppName
location: location
kind: 'functionapp'
identity: {
type: 'SystemAssigned'
}
properties: {
httpsOnly: true
serverFarmId: plan.id
siteConfig: {
ftpsState: 'Disabled'
minTlsVersion: '1.2'
netFrameworkVersion: 'v6.0'
appSettings: [
{
name: 'AzureWebJobsStorage'
value: storageConnectionString
}
{
name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
value: storageConnectionString
}
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsights.properties.InstrumentationKey
}
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: 'dotnet'
}
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: '~4'
}
{
name: 'ApiKey'
value: '@Microsoft.KeyVault(VaultName=${kv.name};SecretName=${kv::storageNameSecret.name})'
}
{
name: 'ContentStorageAccount'
value: storage.name
}
{
name: 'ContentContainer'
value: storage::blobService::content.name
}
]
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment