Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!--
---
name: Azure Functions Python HTTP Trigger using Azure Developer CLI
description: This repository contains an Azure Functions HTTP trigger quickstart written in Python and deployed to Azure Functions Flex Consumption using the Azure Developer CLI (azd). The sample uses managed identity and a virtual network to make sure deployment is secure by default. You can opt out of a VNet being used in the sample by setting SKIP_VNET to true in the parameters.
description: This repository contains an Azure Functions HTTP trigger quickstart written in Python and deployed to Azure Functions Flex Consumption using the Azure Developer CLI (azd). The sample uses managed identity and a virtual network to make sure deployment is secure by default. You can opt out of a VNet being used in the sample by setting VNET_ENABLED to false in the parameters.
page_type: sample
languages:
- azdeveloper
Expand Down Expand Up @@ -162,10 +162,16 @@ Run this command to provision the function app, with any required Azure resource
azd up
```

Alternatively, you can opt-out of a VNet being used in the sample. To do so, use `azd env` to configure `SKIP_VNET` to `true` before running `azd up`:
By default, this sample deploys with a virtual network (VNet) for enhanced security, ensuring that the function app and related resources are isolated within a private network.
The `VNET_ENABLED` parameter controls whether a VNet is used during deployment:
- When `VNET_ENABLED` is `true` (default), the function app is deployed with a VNet for secure communication and resource isolation.
- When `VNET_ENABLED` is `false`, the function app is deployed without a VNet, allowing public access to resources.

This parameter replaces the previous `SKIP_VNET` parameter. If you were using `SKIP_VNET` in earlier versions, set `VNET_ENABLED` to `false` to achieve the same behavior.

To disable the VNet for this sample, set `VNET_ENABLED` to `false` before running `azd up`:
```bash
azd env set SKIP_VNET true
azd env set VNET_ENABLED false
azd up
```

Expand Down
2 changes: 1 addition & 1 deletion azure.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

name: functions-quickstart-python-azd
metadata:
template: functions-quickstart-python-azd@1.0.0
template: functions-quickstart-python-azd@1.0.1
services:
api:
project: .
Expand Down
101 changes: 82 additions & 19 deletions infra/app/api.bicep
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
param name string
@description('Primary location for all resources & Flex Consumption Function App')
param location string = resourceGroup().location
param tags object = {}
param applicationInsightsName string = ''
Expand All @@ -14,33 +15,95 @@ param instanceMemoryMB int = 2048
param maximumInstanceCount int = 100
param identityId string = ''
param identityClientId string = ''
param enableBlob bool = true
param enableQueue bool = false
param enableTable bool = false
param enableFile bool = false

@allowed(['SystemAssigned', 'UserAssigned'])
param identityType string = 'UserAssigned'

var applicationInsightsIdentity = 'ClientId=${identityClientId};Authorization=AAD'
var kind = 'functionapp,linux'

// Create base application settings
var baseAppSettings = {
// Only include required credential settings unconditionally
AzureWebJobsStorage__credential: 'managedidentity'
AzureWebJobsStorage__clientId: identityClientId

// Application Insights settings are always included
APPLICATIONINSIGHTS_AUTHENTICATION_STRING: applicationInsightsIdentity
APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString
}

// Dynamically build storage endpoint settings based on feature flags
var blobSettings = enableBlob ? { AzureWebJobsStorage__blobServiceUri: stg.properties.primaryEndpoints.blob } : {}
var queueSettings = enableQueue ? { AzureWebJobsStorage__queueServiceUri: stg.properties.primaryEndpoints.queue } : {}
var tableSettings = enableTable ? { AzureWebJobsStorage__tableServiceUri: stg.properties.primaryEndpoints.table } : {}
var fileSettings = enableFile ? { AzureWebJobsStorage__fileServiceUri: stg.properties.primaryEndpoints.file } : {}

// Merge all app settings
var allAppSettings = union(
appSettings,
blobSettings,
queueSettings,
tableSettings,
fileSettings,
baseAppSettings
)

resource stg 'Microsoft.Storage/storageAccounts@2022-09-01' existing = {
name: storageAccountName
}

resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) {
name: applicationInsightsName
}

module api '../core/host/functions-flexconsumption.bicep' = {
name: '${serviceName}-functions-module'
// Create a Flex Consumption Function App to host the API
module api 'br/public:avm/res/web/site:0.15.1' = {
name: '${serviceName}-flex-consumption'
params: {
kind: kind
name: name
location: location
tags: union(tags, { 'azd-service-name': serviceName })
identityType: 'UserAssigned'
identityId: identityId
appSettings: union(appSettings,
{
AzureWebJobsStorage__clientId : identityClientId
APPLICATIONINSIGHTS_AUTHENTICATION_STRING: applicationInsightsIdentity
})
applicationInsightsName: applicationInsightsName
appServicePlanId: appServicePlanId
runtimeName: runtimeName
runtimeVersion: runtimeVersion
storageAccountName: storageAccountName
deploymentStorageContainerName: deploymentStorageContainerName
virtualNetworkSubnetId: virtualNetworkSubnetId
instanceMemoryMB: instanceMemoryMB
maximumInstanceCount: maximumInstanceCount
serverFarmResourceId: appServicePlanId
managedIdentities: {
systemAssigned: identityType == 'SystemAssigned'
userAssignedResourceIds: [
'${identityId}'
]
}
functionAppConfig: {
deployment: {
storage: {
type: 'blobContainer'
value: '${stg.properties.primaryEndpoints.blob}${deploymentStorageContainerName}'
authentication: {
type: identityType == 'SystemAssigned' ? 'SystemAssignedIdentity' : 'UserAssignedIdentity'
userAssignedIdentityResourceId: identityType == 'UserAssigned' ? identityId : ''
}
}
}
scaleAndConcurrency: {
instanceMemoryMB: instanceMemoryMB
maximumInstanceCount: maximumInstanceCount
}
runtime: {
name: runtimeName
version: runtimeVersion
}
}
siteConfig: {
alwaysOn: false
}
virtualNetworkSubnetId: !empty(virtualNetworkSubnetId) ? virtualNetworkSubnetId : null
appSettingsKeyValuePairs: allAppSettings
}
}

output SERVICE_API_NAME string = api.outputs.name
output SERVICE_API_IDENTITY_PRINCIPAL_ID string = api.outputs.identityPrincipalId
// Ensure output is always string, handle potential null from module output if SystemAssigned is not used
output SERVICE_API_IDENTITY_PRINCIPAL_ID string = identityType == 'SystemAssigned' ? api.outputs.?systemAssignedMIPrincipalId ?? '' : ''
110 changes: 110 additions & 0 deletions infra/app/rbac.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
param storageAccountName string
param appInsightsName string
param managedIdentityPrincipalId string // Principal ID for the Managed Identity
param userIdentityPrincipalId string = '' // Principal ID for the User Identity
param allowUserIdentityPrincipal bool = false // Flag to enable user identity role assignments
param enableBlob bool = true
param enableQueue bool = false
param enableTable bool = false

// Define Role Definition IDs internally
var storageRoleDefinitionId = 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b' //Storage Blob Data Owner role
var queueRoleDefinitionId = '974c5e8b-45b9-4653-ba55-5f855dd0fb88' // Storage Queue Data Contributor role
var tableRoleDefinitionId = '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3' // Storage Table Data Contributor role
var monitoringRoleDefinitionId = '3913510d-42f4-4e42-8a64-420c390055eb' // Monitoring Metrics Publisher role ID

resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' existing = {
name: storageAccountName
}

resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
name: appInsightsName
}

// Role assignment for Storage Account (Blob) - Managed Identity
resource storageRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (enableBlob) {
name: guid(storageAccount.id, managedIdentityPrincipalId, storageRoleDefinitionId) // Use managed identity ID
scope: storageAccount
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', storageRoleDefinitionId)
principalId: managedIdentityPrincipalId // Use managed identity ID
principalType: 'ServicePrincipal' // Managed Identity is a Service Principal
}
}

// Role assignment for Storage Account (Blob) - User Identity
resource storageRoleAssignment_User 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (enableBlob && allowUserIdentityPrincipal && !empty(userIdentityPrincipalId)) {
name: guid(storageAccount.id, userIdentityPrincipalId, storageRoleDefinitionId)
scope: storageAccount
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', storageRoleDefinitionId)
principalId: userIdentityPrincipalId // Use user identity ID
principalType: 'User' // User Identity is a User Principal
}
}

// Role assignment for Storage Account (Queue) - Managed Identity
resource queueRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (enableQueue) {
name: guid(storageAccount.id, managedIdentityPrincipalId, queueRoleDefinitionId) // Use managed identity ID
scope: storageAccount
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', queueRoleDefinitionId)
principalId: managedIdentityPrincipalId // Use managed identity ID
principalType: 'ServicePrincipal' // Managed Identity is a Service Principal
}
}

// Role assignment for Storage Account (Queue) - User Identity
resource queueRoleAssignment_User 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (enableQueue && allowUserIdentityPrincipal && !empty(userIdentityPrincipalId)) {
name: guid(storageAccount.id, userIdentityPrincipalId, queueRoleDefinitionId)
scope: storageAccount
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', queueRoleDefinitionId)
principalId: userIdentityPrincipalId // Use user identity ID
principalType: 'User' // User Identity is a User Principal
}
}

// Role assignment for Storage Account (Table) - Managed Identity
resource tableRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (enableTable) {
name: guid(storageAccount.id, managedIdentityPrincipalId, tableRoleDefinitionId) // Use managed identity ID
scope: storageAccount
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', tableRoleDefinitionId)
principalId: managedIdentityPrincipalId // Use managed identity ID
principalType: 'ServicePrincipal' // Managed Identity is a Service Principal
}
}

// Role assignment for Storage Account (Table) - User Identity
resource tableRoleAssignment_User 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (enableTable && allowUserIdentityPrincipal && !empty(userIdentityPrincipalId)) {
name: guid(storageAccount.id, userIdentityPrincipalId, tableRoleDefinitionId)
scope: storageAccount
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', tableRoleDefinitionId)
principalId: userIdentityPrincipalId // Use user identity ID
principalType: 'User' // User Identity is a User Principal
}
}

// Role assignment for Application Insights - Managed Identity
resource appInsightsRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(applicationInsights.id, managedIdentityPrincipalId, monitoringRoleDefinitionId) // Use managed identity ID
scope: applicationInsights
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', monitoringRoleDefinitionId)
principalId: managedIdentityPrincipalId // Use managed identity ID
principalType: 'ServicePrincipal' // Managed Identity is a Service Principal
}
}

// Role assignment for Application Insights - User Identity
resource appInsightsRoleAssignment_User 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (allowUserIdentityPrincipal && !empty(userIdentityPrincipalId)) {
name: guid(applicationInsights.id, userIdentityPrincipalId, monitoringRoleDefinitionId)
scope: applicationInsights
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', monitoringRoleDefinitionId)
principalId: userIdentityPrincipalId // Use user identity ID
principalType: 'User' // User Identity is a User Principal
}
}
20 changes: 0 additions & 20 deletions infra/app/storage-Access.bicep

This file was deleted.

Loading