Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Prepare Azure environments for new workloads—subscriptions, networking, identity, and landing zones
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/services/sql-database/bicep.md
1# SQL Database - Bicep Patterns23> ⛔ **CRITICAL — SQL Server Bicep MUST use Entra-only authentication. NEVER include `administratorLogin` or `administratorLoginPassword` anywhere 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.**45## Basic Setup (Entra-Only Authentication)67**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.89```bicep10param principalId string11param principalName string12@allowed(['User', 'Group', 'Application'])13param principalType string = 'User'1415resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = {16name: '${resourcePrefix}-sql-${uniqueHash}'17location: location18properties: {19administrators: {20administratorType: 'ActiveDirectory'21principalType: principalType22login: principalName23sid: principalId24tenantId: subscription().tenantId25azureADOnlyAuthentication: true26}27minimalTlsVersion: '1.2'28}29}3031resource sqlDatabase 'Microsoft.Sql/servers/databases@2022-05-01-preview' = {32parent: sqlServer33name: 'appdb'34location: location35sku: {36name: 'Basic'37tier: 'Basic'38}39properties: {40collation: 'SQL_Latin1_General_CP1_CI_AS'41maxSizeBytes: 2147483648 // 2 GB42}43}4445resource sqlFirewallAzure 'Microsoft.Sql/servers/firewallRules@2022-05-01-preview' = {46parent: sqlServer47name: 'AllowAzureServices'48properties: {49startIpAddress: '0.0.0.0'50endIpAddress: '0.0.0.0'51}52}53```5455**Set Entra admin parameters:**56571. Get current user info:58```bash59az ad signed-in-user show --query "{id:id, name:displayName}" -o json60```6162> ⚠️ **Warning:** If deploying from CI/CD with a service principal, set `principalType` to `'Application'`. The default `'User'` only works for interactive (human) deployments. Mismatched `principalType` causes `UnmatchedPrincipalType` errors during provisioning.63642. Set as azd environment variables:65```bash66PRINCIPAL_INFO=$(az ad signed-in-user show --query "{id:id, name:displayName}" -o json)67azd env set AZURE_PRINCIPAL_ID $(echo $PRINCIPAL_INFO | jq -r '.id')68azd env set AZURE_PRINCIPAL_NAME $(echo $PRINCIPAL_INFO | jq -r '.name')69```7071> 💡 **Tip:** Set these variables immediately after `azd init` to avoid deployment failures. The Bicep `principalId` and `principalName` parameters will automatically use these environment variables.7273## ⚠️ MANDATORY: Connection String with Entra Auth Parameter7475> **CRITICAL:** When outputting SQL connection strings in Bicep (e.g., in App Service `connectionStrings` or as outputs), ALWAYS include an `Authentication` parameter. Use `Authentication=Active Directory Default` for general scenarios or `Authentication=Active Directory Managed Identity` when a user-assigned managed identity with a specific `User Id` is required. A bare `Server=tcp:...;Database=...;` connection string without this parameter will not authenticate via Entra ID and the app will fail to connect.7677**Correct pattern in Bicep (Active Directory Default):**7879```bicep80connectionStrings: [81{82name: 'DefaultConnection'83connectionString: 'Server=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Database=${sqlDatabase.name};Authentication=Active Directory Default;Encrypt=True;TrustServerCertificate=False;'84type: 'SQLAzure'85}86]87```8889**With user-assigned managed identity:**9091```bicep92connectionStrings: [93{94name: 'DefaultConnection'95connectionString: 'Server=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Database=${sqlDatabase.name};Authentication=Active Directory Managed Identity;User Id=${uamiClientId};Encrypt=True;TrustServerCertificate=False;'96type: 'SQLAzure'97}98]99```100101> ❌ **DO NOT** generate connection strings without an `Authentication=Active Directory Default` or `Authentication=Active Directory Managed Identity` parameter.102> ❌ **DO NOT** use manual `AccessToken` assignment or `DefaultAzureCredential().GetToken()` in application code as an alternative to the connection string parameter.103104## Serverless Configuration105106```bicep107resource sqlDatabase 'Microsoft.Sql/servers/databases@2022-05-01-preview' = {108parent: sqlServer109name: 'appdb'110location: location111sku: {112name: 'GP_S_Gen5'113tier: 'GeneralPurpose'114family: 'Gen5'115capacity: 2116}117properties: {118autoPauseDelay: 60 // minutes119minCapacity: json('0.5')120}121}122```123124## Private Endpoint125126```bicep127resource sqlPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-05-01' = {128name: '${sqlServer.name}-pe'129location: location130properties: {131subnet: {132id: subnet.id133}134privateLinkServiceConnections: [135{136name: '${sqlServer.name}-connection'137properties: {138privateLinkServiceId: sqlServer.id139groupIds: ['sqlServer']140}141}142]143}144}145```146147## ⛔ MANDATORY: SQL Data-Plane Access via postprovision Hook148149> **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 using `Authentication=Active Directory Default` will crash on startup with a login failure.150>151> **When you generate SQL + Managed Identity infrastructure you MUST also:**152> 1. Add a `postprovision` hook to `azure.yaml` that runs the SQL grant script153> 2. Copy the pre-built scripts from [scripts/grant-sql-access.sh](scripts/grant-sql-access.sh) and [scripts/grant-sql-access.ps1](scripts/grant-sql-access.ps1) to `scripts/` in the user's project root154155**azure.yaml hooks section (add or merge):**156157```yaml158hooks:159postprovision:160posix:161shell: sh162run: ./scripts/grant-sql-access.sh163windows:164shell: pwsh165run: ./scripts/grant-sql-access.ps1166```167168**Copy the pre-built scripts** — Read [scripts/grant-sql-access.sh](scripts/grant-sql-access.sh) and [scripts/grant-sql-access.ps1](scripts/grant-sql-access.ps1) and write them verbatim to the project's `scripts/` folder. Do not regenerate them from scratch.169170Key behaviours of the scripts:171- Installs the `rdbms-connect` Azure CLI extension if not already present (required for `az sql db query`)172- Loads `azd env get-values` safely (no `eval`)173- Grants `db_datareader` + `db_datawriter` by default (idempotent)174- Set `SQL_GRANT_DDLADMIN=true` in the azd env to also grant `db_ddladmin` (needed for EF Core migrations)175- `SERVICE_WEB_NAME` takes priority over `SERVICE_API_NAME` when resolving the app identity176177> 💡 Make executable: `chmod +x scripts/*.sh`178179> 💡 If the app also uses EF Core migrations, combine this with the migration hook — see the `azure-deploy` skill's `references/recipes/azd/sql-managed-identity.md`.180181## Legacy SQL Authentication (⛔ DO NOT USE - EVER)182183> ❌ **PROHIBITED — NEVER generate `administratorLogin` or `administratorLoginPassword` properties.**184> 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 `.bicep` file.185> SQL admin authentication is disabled in all subscriptions with Entra-only policies and violates Azure security best practices.186> Always use the Entra-only authentication pattern at the top of this file.187>188> ❌ **DO NOT generate conditional patterns like:**189> ```bicep190> properties: useEntraAuth ? {191> administrators: {192> azureADOnlyAuthentication: true193> // ...194> }195> } : {196> administratorLogin: sqlAdminLogin // ❌ PROHIBITED197> administratorLoginPassword: sqlAdminPassword // ❌ PROHIBITED198> }199> ```200> Use only the unconditional Entra-only pattern. No fallback. No toggle. No conditional.201