Tools

Azure Deployment Builder

Choose the workload and generate the deployment files.

Generic output

Generated files use neutral names and public Azure patterns. Review them before use.

Azure Function workload

gatesift-sample / orders

.NET 8 isolated on Flex Consumption with identity-based runtime connections.

8Resource types
3Environments
12Generated files
Managed identity Shared-key storage disabled TLS 1.2 minimum RBAC-based access What-if before deployment
Review before deploymentReplace the Azure DevOps service connection placeholder before running the pipeline.
Review before deploymentThe current builder generates public Azure endpoints with identity-based authentication. Private endpoints and DNS integration are intentionally not generated yet.
infra/main.bicepResource-group orchestration template
targetScope = 'resourceGroup'

@description('Short product or solution name used in resource names.')
@minLength(2)
@maxLength(32)
param projectName string

@description('Short workload name used in resource names.')
@minLength(2)
@maxLength(32)
param workloadName string

@allowed([
  'dev'
  'test'
  'prod'
])
param environment string

@description('Azure region for all resources.')
param location string = resourceGroup().location

@description('Deploy an Azure Service Bus namespace and queue.')
param includeServiceBus bool = true

@description('Queue name when Service Bus is enabled.')
param serviceBusQueueName string = 'messages'

@allowed([
  'send'
  'receive'
  'send-receive'
])
param serviceBusAccess string = 'send-receive'

@description('Deploy an Azure Key Vault using Azure RBAC.')
param includeKeyVault bool = true

@description('Tags applied to generated Azure resources.')
param tags object = {}

var compactProjectName = replace(toLower(projectName), '-', '')
var compactWorkloadName = replace(toLower(workloadName), '-', '')
var uniqueSuffix = take(uniqueString(subscription().id, resourceGroup().id, projectName, workloadName, environment), 6)
var readablePrefix = take(toLower('${projectName}-${workloadName}-${environment}'), 42)
var compactPrefix = take('${compactProjectName}${compactWorkloadName}${environment}', 17)

var generatedFunctionAppName = take('func-${readablePrefix}-${uniqueSuffix}', 60)
var functionPlanName = take('plan-${readablePrefix}-${uniqueSuffix}', 60)
var identityName = take('id-${readablePrefix}-${uniqueSuffix}', 64)
var generatedStorageAccountName = take('st${compactPrefix}${uniqueSuffix}', 24)
var deploymentContainerName = take('app-package-${compactPrefix}', 63)
var logAnalyticsName = take('log-${readablePrefix}-${uniqueSuffix}', 63)
var generatedApplicationInsightsName = take('appi-${readablePrefix}-${uniqueSuffix}', 255)
var generatedServiceBusNamespaceName = take('sbns-${readablePrefix}-${uniqueSuffix}', 50)
var generatedKeyVaultName = take('kv-${compactPrefix}-${uniqueSuffix}', 24)

var commonTags = union(tags, {
  environment: environment
  workload: workloadName
  managedBy: 'GateSift deployment builder'
})

var storageBlobDataOwnerRoleId = 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'
var storageQueueDataContributorRoleId = '974c5e8b-45b9-4653-ba55-5f855dd0fb88'
var storageTableDataContributorRoleId = '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3'
var monitoringMetricsPublisherRoleId = '3913510d-42f4-4e42-8a64-420c390055eb'
var serviceBusDataSenderRoleId = '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39'
var serviceBusDataReceiverRoleId = '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0'
var keyVaultSecretsUserRoleId = '4633458b-17de-408a-b874-0445c86b69e6'

module monitoring './modules/monitoring.bicep' = {
  name: 'monitoring-${uniqueSuffix}'
  params: {
    location: location
    logAnalyticsName: logAnalyticsName
    applicationInsightsName: generatedApplicationInsightsName
    tags: commonTags
  }
}

module storage './modules/storage.bicep' = {
  name: 'storage-${uniqueSuffix}'
  params: {
    location: location
    storageAccountName: generatedStorageAccountName
    deploymentContainerName: deploymentContainerName
    tags: commonTags
  }
}

resource workloadIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
  name: identityName
  location: location
  tags: commonTags
}

module serviceBus './modules/service-bus.bicep' = if (includeServiceBus) {
  name: 'service-bus-${uniqueSuffix}'
  params: {
    location: location
    namespaceName: generatedServiceBusNamespaceName
    queueName: serviceBusQueueName
    tags: commonTags
  }
}

module keyVault './modules/key-vault.bicep' = if (includeKeyVault) {
  name: 'key-vault-${uniqueSuffix}'
  params: {
    location: location
    keyVaultName: generatedKeyVaultName
    enablePurgeProtection: environment == 'prod'
    tags: commonTags
  }
}

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = {
  name: storage.outputs.deployedStorageAccountName
}

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

resource storageBlobOwner 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(storageAccount.id, workloadIdentity.id, storageBlobDataOwnerRoleId)
  scope: storageAccount
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataOwnerRoleId)
    principalId: workloadIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource storageQueueContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(storageAccount.id, workloadIdentity.id, storageQueueDataContributorRoleId)
  scope: storageAccount
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageQueueDataContributorRoleId)
    principalId: workloadIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource storageTableContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(storageAccount.id, workloadIdentity.id, storageTableDataContributorRoleId)
  scope: storageAccount
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageTableDataContributorRoleId)
    principalId: workloadIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource monitoringPublisher 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(applicationInsights.id, workloadIdentity.id, monitoringMetricsPublisherRoleId)
  scope: applicationInsights
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', monitoringMetricsPublisherRoleId)
    principalId: workloadIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2024-01-01' existing = if (includeServiceBus) {
  name: serviceBus.outputs.deployedNamespaceName
}

resource serviceBusSender 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (includeServiceBus && (serviceBusAccess == 'send' || serviceBusAccess == 'send-receive')) {
  name: guid(serviceBusNamespace.id, workloadIdentity.id, serviceBusDataSenderRoleId)
  scope: serviceBusNamespace
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', serviceBusDataSenderRoleId)
    principalId: workloadIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource serviceBusReceiver 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (includeServiceBus && (serviceBusAccess == 'receive' || serviceBusAccess == 'send-receive')) {
  name: guid(serviceBusNamespace.id, workloadIdentity.id, serviceBusDataReceiverRoleId)
  scope: serviceBusNamespace
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', serviceBusDataReceiverRoleId)
    principalId: workloadIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource vault 'Microsoft.KeyVault/vaults@2023-07-01' existing = if (includeKeyVault) {
  name: keyVault.outputs.deployedKeyVaultName
}

resource keyVaultSecretsUser 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (includeKeyVault) {
  name: guid(vault.id, workloadIdentity.id, keyVaultSecretsUserRoleId)
  scope: vault
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', keyVaultSecretsUserRoleId)
    principalId: workloadIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

var serviceBusSettings = includeServiceBus ? {
  'ServiceBus__fullyQualifiedNamespace': serviceBus.outputs.fullyQualifiedNamespace
  'ServiceBus__queueName': serviceBusQueueName
} : {}

var keyVaultSettings = includeKeyVault ? {
  'KeyVault__vaultUri': keyVault.outputs.vaultUri
} : {}

module functionApp './modules/function-app-flex.bicep' = {
  name: 'function-app-${uniqueSuffix}'
  params: {
    location: location
    functionAppName: generatedFunctionAppName
    planName: functionPlanName
    identityResourceId: workloadIdentity.id
    identityClientId: workloadIdentity.properties.clientId
    storageAccountName: storage.outputs.deployedStorageAccountName
    deploymentContainerUri: storage.outputs.deploymentContainerUri
    applicationInsightsConnectionString: monitoring.outputs.applicationInsightsConnectionString
    additionalAppSettings: union(serviceBusSettings, keyVaultSettings)
    tags: commonTags
  }
  dependsOn: [
    storageBlobOwner
    storageQueueContributor
    storageTableContributor
    monitoringPublisher
  ]
}

output functionAppName string = functionApp.outputs.deployedFunctionAppName
output functionAppHostName string = functionApp.outputs.defaultHostName
output managedIdentityName string = workloadIdentity.name
output storageAccountName string = storage.outputs.deployedStorageAccountName
output applicationInsightsName string = monitoring.outputs.deployedApplicationInsightsName
output serviceBusNamespaceName string = includeServiceBus ? serviceBus.outputs.deployedNamespaceName : ''
output keyVaultName string = includeKeyVault ? keyVault.outputs.deployedKeyVaultName : ''
Download the complete deployment packageBicep modules, environment parameters, documentation and the selected pipeline.

Early-access boundary: private endpoints, private DNS, existing landing zones and organization-specific naming rules are not guessed. The generated README explains what still needs deliberate design.