SQL Database - Bicep Patterns
⛔ CRITICAL — SQL Server Bicep MUST use Entra-only authentication. NEVER include
administratorLoginoradministratorLoginPasswordanywhere in a Bicep file — not even inside a conditional (condition ? { ... } : { administratorLoginPassword: ... }) branch. If either property name appears anywhere in the file, the deployment will be rejected. Always use the pattern below.
Basic Setup (Entra-Only Authentication)
Required approach — Uses Microsoft Entra ID authentication only. Required for subscriptions with Entra-only policies; SQL admin authentication is disabled by policy in those environments.
param principalId string
param principalName string
@allowed(['User', 'Group', 'Application'])
param principalType string = 'User'
resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = {
name: '${resourcePrefix}-sql-${uniqueHash}'
location: location
properties: {
administrators: {
administratorType: 'ActiveDirectory'
principalType: principalType
login: principalName
sid: principalId
tenantId: subscription().tenantId
azureADOnlyAuthentication: true
}
minimalTlsVersion: '1.2'
}
}
resource sqlDatabase 'Microsoft.Sql/servers/databases@2022-05-01-preview' = {
parent: sqlServer
name: 'appdb'
location: location
sku: {
name: 'Basic'
tier: 'Basic'
}
properties: {
collation: 'SQL_Latin1_General_CP1_CI_AS'
maxSizeBytes: 2147483648 // 2 GB
}
}
resource sqlFirewallAzure 'Microsoft.Sql/servers/firewallRules@2022-05-01-preview' = {
parent: sqlServer
name: 'AllowAzureServices'
properties: {
startIpAddress: '0.0.0.0'
endIpAddress: '0.0.0.0'
}
}Set Entra admin parameters:
- Get current user info:
az ad signed-in-user show --query "{id:id, name:displayName}" -o json⚠️ Warning: If deploying from CI/CD with a service principal, set
principalTypeto'Application'. The default'User'only works for interactive (human) deployments. MismatchedprincipalTypecausesUnmatchedPrincipalTypeerrors during provisioning.
- Set as azd environment variables:
PRINCIPAL_INFO=$(az ad signed-in-user show --query "{id:id, name:displayName}" -o json)
azd env set AZURE_PRINCIPAL_ID $(echo $PRINCIPAL_INFO | jq -r '.id')
azd env set AZURE_PRINCIPAL_NAME $(echo $PRINCIPAL_INFO | jq -r '.name')💡 Tip: Set these variables immediately after
azd initto avoid deployment failures. The BicepprincipalIdandprincipalNameparameters will automatically use these environment variables.
⚠️ MANDATORY: Connection String with Entra Auth Parameter
CRITICAL: When outputting SQL connection strings in Bicep (e.g., in App Service
connectionStringsor as outputs), ALWAYS include anAuthenticationparameter. UseAuthentication=Active Directory Defaultfor general scenarios orAuthentication=Active Directory Managed Identitywhen a user-assigned managed identity with a specificUser Idis required. A bareServer=tcp:...;Database=...;connection string without this parameter will not authenticate via Entra ID and the app will fail to connect.
Correct pattern in Bicep (Active Directory Default):
connectionStrings: [
{
name: 'DefaultConnection'
connectionString: 'Server=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Database=${sqlDatabase.name};Authentication=Active Directory Default;Encrypt=True;TrustServerCertificate=False;'
type: 'SQLAzure'
}
]With user-assigned managed identity:
connectionStrings: [
{
name: 'DefaultConnection'
connectionString: 'Server=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Database=${sqlDatabase.name};Authentication=Active Directory Managed Identity;User Id=${uamiClientId};Encrypt=True;TrustServerCertificate=False;'
type: 'SQLAzure'
}
]❌ DO NOT generate connection strings without an
Authentication=Active Directory DefaultorAuthentication=Active Directory Managed Identityparameter. ❌ DO NOT use manualAccessTokenassignment orDefaultAzureCredential().GetToken()in application code as an alternative to the connection string parameter.
Serverless Configuration
resource sqlDatabase 'Microsoft.Sql/servers/databases@2022-05-01-preview' = {
parent: sqlServer
name: 'appdb'
location: location
sku: {
name: 'GP_S_Gen5'
tier: 'GeneralPurpose'
family: 'Gen5'
capacity: 2
}
properties: {
autoPauseDelay: 60 // minutes
minCapacity: json('0.5')
}
}Private Endpoint
resource sqlPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-05-01' = {
name: '${sqlServer.name}-pe'
location: location
properties: {
subnet: {
id: subnet.id
}
privateLinkServiceConnections: [
{
name: '${sqlServer.name}-connection'
properties: {
privateLinkServiceId: sqlServer.id
groupIds: ['sqlServer']
}
}
]
}
}⛔ MANDATORY: SQL Data-Plane Access via postprovision Hook
CRITICAL: ARM/Bicep role assignments (
SQL DB Contributor) only grant control-plane access. They do not grant the app data-plane access to the database. Without the T-SQL grant below, apps usingAuthentication=Active Directory Defaultwill crash on startup with a login failure.
>
When you generate SQL + Managed Identity infrastructure you MUST also:
- Add a
postprovisionhook toazure.yamlthat runs the SQL grant script- Copy the pre-built scripts from scripts/grant-sql-access.sh and scripts/grant-sql-access.ps1 to
scripts/in the user's project root
azure.yaml hooks section (add or merge):
hooks:
postprovision:
posix:
shell: sh
run: ./scripts/grant-sql-access.sh
windows:
shell: pwsh
run: ./scripts/grant-sql-access.ps1Copy the pre-built scripts — Read scripts/grant-sql-access.sh and scripts/grant-sql-access.ps1 and write them verbatim to the project's scripts/ folder. Do not regenerate them from scratch.
Key behaviours of the scripts:
- Installs the
rdbms-connectAzure CLI extension if not already present (required foraz sql db query) - Loads
azd env get-valuessafely (noeval) - Grants
db_datareader+db_datawriterby default (idempotent) - Set
SQL_GRANT_DDLADMIN=truein the azd env to also grantdb_ddladmin(needed for EF Core migrations) SERVICE_WEB_NAMEtakes priority overSERVICE_API_NAMEwhen resolving the app identity
💡 Make executable:
chmod +x scripts/*.sh
💡 If the app also uses EF Core migrations, combine this with the migration hook — see the
azure-deployskill'sreferences/recipes/azd/sql-managed-identity.md.
Legacy SQL Authentication (⛔ DO NOT USE - EVER)
❌ PROHIBITED — NEVER generate
administratorLoginoradministratorLoginPasswordproperties. This applies unconditionally: do NOT include them in direct assignments, conditional expressions, ternary branches, parameter defaults, or any other construct. The text of these property names must not appear anywhere in a generated.bicepfile. SQL admin authentication is disabled in all subscriptions with Entra-only policies and violates Azure security best practices. Always use the Entra-only authentication pattern at the top of this file.
>
❌ DO NOT generate conditional patterns like:
properties: useEntraAuth ? { administrators: { azureADOnlyAuthentication: true // ... } } : { administratorLogin: sqlAdminLogin // ❌ PROHIBITED administratorLoginPassword: sqlAdminPassword // ❌ PROHIBITED }Use only the unconditional Entra-only pattern. No fallback. No toggle. No conditional.