Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Deploy applications and infrastructure to Azure using Copilot-guided workflows and Azure MCP
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/recipes/azd/sql-managed-identity.md
1# SQL Managed Identity Access23Grant Azure managed identities database permissions on Azure SQL with Entra authentication.45## Prerequisites67- Azure SQL Server with Entra ID admin configured8- App Service/Container App with system-assigned managed identity9- Your account is Entra ID admin on SQL Server10- Azure CLI: `az login`11- Azure CLI `rdbms-connect` extension: `az extension add --name rdbms-connect --yes`1213## Quick Grant1415```bash16eval $(azd env get-values)17APP_NAME=$(echo "$SERVICE_API_NAME") # or SERVICE_WEB_NAME1819az sql db query \20--server "$SQL_SERVER" \21--database "$SQL_DATABASE" \22--resource-group "$AZURE_RESOURCE_GROUP" \23--auth-mode ActiveDirectoryDefault \24--queries "25CREATE USER [$APP_NAME] FROM EXTERNAL PROVIDER;26ALTER ROLE db_datareader ADD MEMBER [$APP_NAME];27ALTER ROLE db_datawriter ADD MEMBER [$APP_NAME];28ALTER ROLE db_ddladmin ADD MEMBER [$APP_NAME];29"30```3132**PowerShell:**33```powershell34azd env get-values | ForEach-Object {35$name, $value = $_.Split('=', 2)36Set-Item "env:$name" $value37}38$AppName = $env:SERVICE_API_NAME # or SERVICE_WEB_NAME3940$SqlQuery = @"41CREATE USER [$AppName] FROM EXTERNAL PROVIDER;42ALTER ROLE db_datareader ADD MEMBER [$AppName];43ALTER ROLE db_datawriter ADD MEMBER [$AppName];44ALTER ROLE db_ddladmin ADD MEMBER [$AppName];45"@4647az sql db query `48--server $env:SQL_SERVER `49--database $env:SQL_DATABASE `50--resource-group $env:AZURE_RESOURCE_GROUP `51--auth-mode ActiveDirectoryDefault `52--queries $SqlQuery53```5455## Database Roles5657| Role | Permissions | Use For |58|------|------------|---------|59| `db_datareader` | SELECT | Read-only queries |60| `db_datawriter` | INSERT, UPDATE, DELETE | CRUD operations |61| `db_ddladmin` | CREATE, ALTER, DROP schema | EF migrations |62| `db_owner` | Full control | Admin (use sparingly) |6364**Standard app (read/write/migrations):** All three roles above.65**Read-only app:** Only `db_datareader`.6667## Automate with azd Hook6869Add `postprovision` hook to `azure.yaml`:7071```yaml72hooks:73postprovision:74shell: sh75run: ./scripts/grant-sql-access.sh76```7778**scripts/grant-sql-access.sh:**7980```bash81#!/bin/bash82set -e83eval $(azd env get-values)8485az sql db query \86--server "$SQL_SERVER" \87--database "$SQL_DATABASE" \88--resource-group "$AZURE_RESOURCE_GROUP" \89--auth-mode ActiveDirectoryDefault \90--queries "91IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = '$SERVICE_API_NAME')92CREATE USER [$SERVICE_API_NAME] FROM EXTERNAL PROVIDER;9394IF NOT EXISTS (95SELECT 1 FROM sys.database_role_members drm96JOIN sys.database_principals r ON drm.role_principal_id = r.principal_id97JOIN sys.database_principals m ON drm.member_principal_id = m.principal_id98WHERE r.name = 'db_datareader' AND m.name = '$SERVICE_API_NAME'99)100ALTER ROLE db_datareader ADD MEMBER [$SERVICE_API_NAME];101102IF NOT EXISTS (103SELECT 1 FROM sys.database_role_members drm104JOIN sys.database_principals r ON drm.role_principal_id = r.principal_id105JOIN sys.database_principals m ON drm.member_principal_id = m.principal_id106WHERE r.name = 'db_datawriter' AND m.name = '$SERVICE_API_NAME'107)108ALTER ROLE db_datawriter ADD MEMBER [$SERVICE_API_NAME];109110IF NOT EXISTS (111SELECT 1 FROM sys.database_role_members drm112JOIN sys.database_principals r ON drm.role_principal_id = r.principal_id113JOIN sys.database_principals m ON drm.member_principal_id = m.principal_id114WHERE r.name = 'db_ddladmin' AND m.name = '$SERVICE_API_NAME'115)116ALTER ROLE db_ddladmin ADD MEMBER [$SERVICE_API_NAME];117"118```119120**scripts/grant-sql-access.ps1:**121122```powershell123$ErrorActionPreference = 'Stop'124azd env get-values | ForEach-Object {125$name, $value = $_.Split('=', 2)126Set-Item "env:$name" $value127}128129$SqlQuery = @"130IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = '$($env:SERVICE_API_NAME)')131CREATE USER [$($env:SERVICE_API_NAME)] FROM EXTERNAL PROVIDER;132133IF NOT EXISTS (134SELECT 1 FROM sys.database_role_members drm135JOIN sys.database_principals r ON drm.role_principal_id = r.principal_id136JOIN sys.database_principals m ON drm.member_principal_id = m.principal_id137WHERE r.name = 'db_datareader' AND m.name = '$($env:SERVICE_API_NAME)'138)139ALTER ROLE db_datareader ADD MEMBER [$($env:SERVICE_API_NAME)];140141IF NOT EXISTS (142SELECT 1 FROM sys.database_role_members drm143JOIN sys.database_principals r ON drm.role_principal_id = r.principal_id144JOIN sys.database_principals m ON drm.member_principal_id = m.principal_id145WHERE r.name = 'db_datawriter' AND m.name = '$($env:SERVICE_API_NAME)'146)147ALTER ROLE db_datawriter ADD MEMBER [$($env:SERVICE_API_NAME)];148149IF NOT EXISTS (150SELECT 1 FROM sys.database_role_members drm151JOIN sys.database_principals r ON drm.role_principal_id = r.principal_id152JOIN sys.database_principals m ON drm.member_principal_id = m.principal_id153WHERE r.name = 'db_ddladmin' AND m.name = '$($env:SERVICE_API_NAME)'154)155ALTER ROLE db_ddladmin ADD MEMBER [$($env:SERVICE_API_NAME)];156"@157158az sql db query `159--server $env:SQL_SERVER `160--database $env:SQL_DATABASE `161--resource-group $env:AZURE_RESOURCE_GROUP `162--auth-mode ActiveDirectoryDefault `163--queries $SqlQuery164```165166> 💡 Make executable: `chmod +x scripts/*.sh`.167168## Verification169170```bash171eval $(azd env get-values)172APP_NAME=$SERVICE_API_NAME # or SERVICE_WEB_NAME173174az sql db query --server "$SQL_SERVER" --database "$SQL_DATABASE" \175--auth-mode ActiveDirectoryDefault --queries "176SELECT dp.name AS UserName, dr.name AS RoleName177FROM sys.database_principals dp178JOIN sys.database_role_members drm ON dp.principal_id = drm.member_principal_id179JOIN sys.database_principals dr ON drm.role_principal_id = dr.principal_id180WHERE dp.name = '$APP_NAME'181"182```183184**PowerShell:**185```powershell186azd env get-values | ForEach-Object {187$name, $value = $_.Split('=', 2)188Set-Item "env:$name" $value189}190$AppName = $env:SERVICE_API_NAME # or SERVICE_WEB_NAME191192$SqlQuery = @"193SELECT dp.name AS UserName, dr.name AS RoleName194FROM sys.database_principals dp195JOIN sys.database_role_members drm ON dp.principal_id = drm.member_principal_id196JOIN sys.database_principals dr ON drm.role_principal_id = dr.principal_id197WHERE dp.name = '$AppName'198"@199200az sql db query --server $env:SQL_SERVER --database $env:SQL_DATABASE `201--auth-mode ActiveDirectoryDefault --queries $SqlQuery202```203204Expected: UserName matches `$APP_NAME`, RoleName includes `db_datareader`, `db_datawriter`, `db_ddladmin`.205206## Troubleshooting207208| Error | Solution |209|-------|----------|210| "Cannot find the user" | Verify identity exists: `az webapp identity show` or `az containerapp identity show` |211| "Principal does not have permission" | Check you're Entra admin: `az sql server ad-admin list` |212| "Login failed for user" | Run CREATE USER commands from this guide |213214**Idempotent Script Pattern:**215216```sql217-- Check if user exists before creating218IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = 'my-app')219CREATE USER [my-app] FROM EXTERNAL PROVIDER;220221-- Check role membership before adding222IF NOT EXISTS (223SELECT 1 FROM sys.database_role_members drm224JOIN sys.database_principals r ON drm.role_principal_id = r.principal_id225JOIN sys.database_principals m ON drm.member_principal_id = m.principal_id226WHERE r.name = 'db_datareader' AND m.name = 'my-app'227)228ALTER ROLE db_datareader ADD MEMBER [my-app];229```230231## References232233- [SQL Entra Authentication](sql-entra-auth.md)234- [EF Core Migrations](ef-migrations.md)235- [Post-Deployment Guide](post-deployment.md)236