Deep dive · Files & flags

Configuration reference

Every file the exam expects you to read, in one place. Each sample is annotated with the options that turn into answers — the ones that secure an endpoint, block a dangerous deployment or scope an instruction — and checked against the current versions of the tools.

Skills measured as of March 12, 2026
flowchart LR
  subgraph REPO["Repository"]
    direction TB
    GH[".github/<br/>copilot-instructions.md · instructions/ · prompts/ · CODEOWNERS · workflows/"] ~~~ VS[".vscode/mcp.json<br/>MCP servers for agent mode"]
    VS ~~~ DB["database/<br/>Contoso.Database.sqlproj · object scripts · publish profiles"]
    DB ~~~ API["api/<br/>dab-config.json"]
  end
Where each file lives in a typical repository. The exam usually shows one of them with a line missing or wrong.

Versions these samples target

Data API builder 2.1, Microsoft.Build.Sql 2.3.0, SqlPackage 170.5, actions/checkout@v7, actions/setup-dotnet@v6, actions/upload-artifact@v7, actions/download-artifact@v8, azure/login@v3 and azure/sql-action@v2 — the current majors in November 2026. Older Learn samples pin earlier majors; the syntax that matters for the exam is the same.

dab-config.json

One file configures the data source, the three endpoints, security, caching, paging and every entity. Comments are shown for annotation — remove them in a real file, or keep the file valid JSON.

dab-config.json
{
  "$schema": "https://github.com/Azure/data-api-builder/releases/download/v2.1.5/dab.draft.schema.json",
  "data-source": {
    "database-type": "mssql",
    // Never a literal: @env() reads an environment variable, @akv() a Key Vault secret
    "connection-string": "@env('SQL_CONNECTION_STRING')"
  },
  "runtime": {
    "rest":    { "enabled": true, "path": "/api" },
    "graphql": { "enabled": true, "path": "/graphql", "allow-introspection": false },
    "mcp": {
      "enabled": true,
      "path": "/mcp",
      "description": "Product catalog for the Contoso storefront",   // sent to MCP clients as instructions
      "dml-tools": {
        "describe-entities": true,
        "read-records": true,
        "create-record": false,
        "update-record": false,
        "delete-record": false,                                     // a common production choice
        "execute-entity": true,
        "aggregate-records": { "enabled": true, "query-timeout": 30 }
      }
    },
    "host": {
      "mode": "production",                                         // development enables Simulator and richer errors
      "cors": { "origins": ["https://shop.contoso.com"], "allow-credentials": false },
      "authentication": {
        "provider": "EntraId",                                      // formerly AzureAd; default is Unauthenticated
        "jwt": {
          "audience": "api://contoso-catalog",
          "issuer": "https://login.microsoftonline.com/<tenant-id>/v2.0"
        }
      }
    },
    "cache":      { "enabled": true, "ttl-seconds": 30 },
    "pagination": { "default-page-size": 100, "max-page-size": 1000 },
    "telemetry":  { "application-insights": { "enabled": true, "connection-string": "@env('APPLICATIONINSIGHTS_CONNECTION_STRING')" } }
  },
  "entities": {
    "Product": {
      "source": { "object": "dbo.Product", "type": "table", "object-description": "Products for sale" },
      "fields": [
        { "name": "ProductID", "primary-key": true },
        { "name": "ListPrice", "alias": "price", "description": "Price in EUR" }   // DAB 2.x: replaces "mappings"
      ],
      "rest":    { "enabled": true, "path": "/products" },
      "graphql": { "enabled": true, "type": { "singular": "Product", "plural": "Products" } },
      "cache":   { "enabled": true, "ttl-seconds": 60, "level": "L1" },
      "permissions": [
        { "role": "anonymous",     "actions": [ { "action": "read", "fields": { "exclude": ["Cost", "Embedding"] } } ] },
        { "role": "catalogEditor", "actions": [ "create", "read", "update" ] }
      ],
      "relationships": {
        "category": {
          "cardinality": "one",
          "target.entity": "Category",
          "source.fields": ["CategoryID"],
          "target.fields": ["CategoryID"]
        }
      }
    },
    "Category": {
      "source": { "object": "dbo.Category", "type": "table" },
      "permissions": [ { "role": "anonymous", "actions": [ "read" ] } ],
      "relationships": {
        "products": { "cardinality": "many", "target.entity": "Product" }   // inferred from the foreign key
      }
    },
    "MyOrders": {
      "source": { "object": "dbo.vw_OrderSummary", "type": "view" },
      "fields": [ { "name": "OrderNo", "primary-key": true } ],            // views need a key (was "key-fields")
      "permissions": [
        {
          "role": "authenticated",
          "actions": [ { "action": "read",
                         "policy": { "database": "@item.CustomerOid eq @claims.oid" } } ]   // per-user rows
        }
      ]
    },
    "PlaceOrder": {
      "source": {
        "object": "dbo.usp_PlaceOrder",
        "type": "stored-procedure",
        "parameters": [ { "name": "CustomerID", "required": true, "description": "Buyer" } ]   // DAB 2.x array format
      },
      "rest":    { "methods": ["post"] },
      "graphql": { "operation": "mutation" },
      "mcp":     { "custom-tool": true },                                   // exposed as a named MCP tool
      "permissions": [ { "role": "authenticated", "actions": [ "execute" ] } ]
    }
  }
}
SectionWhat the exam asks about it
data-source.connection-stringComes from @env() or @akv() (Key Vault, with a root azure-key-vault.endpoint), ideally with Authentication=Active Directory Managed Identity — never a literal secret
Column typesDAB doesn't serialize json, vector, xml, spatial, hierarchyid or sql_variant columns — expose them through a view or procedure that casts or omits them
runtime.host.authenticationEntraId/Custom validate JWTs; AppService trusts EasyAuth; Unauthenticated (default) is anonymous-only; Simulator needs development mode
runtime.host.modeproduction for deployed APIs
runtime.mcp.dml-toolsGlobal on/off per tool; a disabled tool is hidden from list_tools whatever the entity permissions
entities.*.permissionsRole → actions, field include/exclude, database policy with @item and @claims
entities.*.fieldsDAB 2.x replacement for mappings (alias) and key-fields (primary-key) — the two can't be mixed
entities.*.relationshipscardinality one/many, target.entity, optional field lists, linking.object for many-to-many (GraphQL)
entities.*.cacheenabled, ttl-seconds (default 5, or the runtime value) and level L1 or L1L2 (Redis-backed)

DAB CLI

bash
# Create the file (the connection string stays in an environment variable)
dab init --database-type mssql --connection-string "@env('SQL_CONNECTION_STRING')" --host-mode development

# Add entities: a table, a view with a key, a stored procedure
dab add Product   --source dbo.Product --permissions "anonymous:read"
dab add MyOrders  --source dbo.vw_OrderSummary --source.type view \
                  --fields.name OrderNo --fields.primary-key true --permissions "authenticated:read"
dab add PlaceOrder --source dbo.usp_PlaceOrder --source.type stored-procedure \
                  --permissions "authenticated:execute" --rest.methods "post" --graphql.operation "mutation"

# Evolve them
dab update Product  --relationship category --target.entity Category --cardinality one
dab update Product  --cache.enabled true --cache.ttl-seconds 60
dab configure --runtime.host.authentication.provider EntraId
dab configure --runtime.mcp.enabled true

# Check and run
dab validate                      # schema, permissions and database connectivity
dab start                         # REST /api, GraphQL /graphql, MCP /mcp on http://localhost:5000
dab start --mcp-stdio role:anonymous --config ./dab-config.json   # MCP over stdio for a local client

SDK-style .sqlproj

.sqlproj
<Project DefaultTargets="Build">
  <Sdk Name="Microsoft.Build.Sql" Version="2.3.0" />

  <PropertyGroup>
    <Name>Contoso.Database</Name>
    <!-- Target platform: Sql170 = SQL Server 2025, SqlAzureV12 = Azure SQL Database,
         SqlDbFabric = SQL database in Fabric (preview) -->
    <DSP>Microsoft.Data.Tools.Schema.Sql.SqlAzureV12DatabaseSchemaProvider</DSP>
    <ModelCollation>1033, CI</ModelCollation>
    <RunSqlCodeAnalysis>True</RunSqlCodeAnalysis>
    <TreatTSqlWarningsAsErrors>False</TreatTSqlWarningsAsErrors>
  </PropertyGroup>

  <!-- Every .sql file under the folder is part of the model by default (globbing).
       Scripts that are not schema objects are taken out of the build and wired as deployment scripts. -->
  <ItemGroup>
    <Build Remove="Scripts\**\*.sql" />
    <PreDeploy  Include="Scripts\PreDeployment.sql" />
    <PostDeploy Include="Scripts\PostDeployment.sql" />
  </ItemGroup>

  <!-- Per-environment values, referenced in scripts as $(Environment) -->
  <ItemGroup>
    <SqlCmdVariable Include="Environment">
      <DefaultValue>dev</DefaultValue>
    </SqlCmdVariable>
  </ItemGroup>

  <!-- Database references as NuGet packages, for example the master database -->
  <ItemGroup>
    <PackageReference Include="Microsoft.SqlServer.Dacpacs.Azure.Master" Version="170.0.4" />
  </ItemGroup>
</Project>
  • Globbing: every .sql file is in the model unless removed — that is why deployment scripts are taken out with Build Remove and wired as PreDeploy/PostDeploy.
  • One pre- and one post-deployment script per project; include further files from them with SQLCMD :r.
  • SQLCMD variables carry per-environment values; set them in a publish profile or with /v:Name=value.
  • Build with dotnet build to get bin/<config>/Contoso.Database.dacpac.

Publish profile

Production.publish.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- publish/Production.publish.xml — deployment options, never credentials -->
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <TargetDatabaseName>ContosoDb</TargetDatabaseName>
    <BlockOnPossibleDataLoss>True</BlockOnPossibleDataLoss>
    <DropObjectsNotInSource>False</DropObjectsNotInSource>
    <BlockWhenDriftDetected>True</BlockWhenDriftDetected>
    <RegisterDataTierApplication>True</RegisterDataTierApplication>
    <IncludeTransactionalScripts>True</IncludeTransactionalScripts>
  </PropertyGroup>
  <ItemGroup>
    <SqlCmdVariable Include="Environment">
      <Value>prod</Value>
    </SqlCmdVariable>
  </ItemGroup>
</Project>
BlockOnPossibleDataLoss (default true)
Stops a deployment that would drop a column, narrow a type or otherwise lose data.
DropObjectsNotInSource (default false)
Deletes objects in the target that are not in the project — powerful, dangerous on shared databases.
RegisterDataTierApplication and BlockWhenDriftDetected
Register the deployed schema so later deployments (and DriftReport) can detect out-of-band changes.

GitHub Actions deploy workflow

.github/workflows/database.yml
name: Database CI/CD

on:
  pull_request:
    branches: [main]
    paths: ['database/**']
  push:
    branches: [main]
    paths: ['database/**']
  workflow_dispatch:

permissions:
  contents: read

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    services:
      sql:                                            # throwaway SQL Server for integration tests
        image: mcr.microsoft.com/mssql/server:2025-latest
        env:
          ACCEPT_EULA: 'Y'
          MSSQL_SA_PASSWORD: ${{ secrets.CI_SA_PASSWORD }}
        ports: ['1433:1433']
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-dotnet@v6
        with:
          dotnet-version: '10.0.x'
      - name: Build the model (validates every reference)
        run: dotnet build database/Contoso.Database.sqlproj -c Release
      - name: Deploy to the CI container and run tests
        run: |
          dotnet tool install -g microsoft.sqlpackage
          sqlpackage /Action:Publish \
            /SourceFile:database/bin/Release/Contoso.Database.dacpac \
            /TargetConnectionString:"Server=localhost;Database=CiDb;User ID=sa;Password=${{ secrets.CI_SA_PASSWORD }};TrustServerCertificate=True"
          # run unit and integration tests here (for example tSQLt via sqlcmd)
      - uses: actions/upload-artifact@v7
        with:
          name: dacpac
          path: database/bin/Release/Contoso.Database.dacpac

  deploy-production:
    if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
    needs: build-and-test
    runs-on: ubuntu-latest
    environment: production                          # required reviewers, branch rule, env secrets
    permissions:
      contents: read
      id-token: write                                # OIDC token for azure/login: no client secret
    steps:
      - uses: actions/checkout@v7
      - uses: actions/download-artifact@v8
        with:
          name: dacpac
      - uses: azure/login@v3
        with:
          client-id: ${{ vars.AZURE_CLIENT_ID }}
          tenant-id: ${{ vars.AZURE_TENANT_ID }}
          subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
      - name: Review the plan
        uses: azure/sql-action@v2
        with:
          connection-string: ${{ secrets.PROD_SQL_CONNECTION_STRING }}   # Authentication=Active Directory Default
          path: ./Contoso.Database.dacpac
          action: DeployReport
      - name: Publish
        uses: azure/sql-action@v2
        with:
          connection-string: ${{ secrets.PROD_SQL_CONNECTION_STRING }}
          path: ./Contoso.Database.dacpac
          action: Publish
          arguments: '/p:BlockOnPossibleDataLoss=true'

What each control in the workflow does

  • pull_request + paths: validate only database changes; the PR can't merge until this check passes (a required status check in the branch ruleset).
  • environment: production: the job waits for the environment's required reviewers and can only read that environment's secrets.
  • id-token: write + azure/login with client-id, tenant-id, subscription-id: OIDC federation — no client secret anywhere.
  • One .dacpac artifact, built once and deployed to every environment.
  • azure/sql-action actions: Publish, Script, DeployReport, DriftReport; it builds a .sqlproj itself when given one.

CODEOWNERS

CODEOWNERS
# .github/CODEOWNERS — the last matching pattern wins
*                      @contoso/app-team
/database/             @contoso/dba-team
/database/Security/    @contoso/dba-team @contoso/security
/.github/workflows/    @contoso/platform-team

CODEOWNERS only requests reviews; it becomes a control when the branch ruleset requires "review from Code Owners" before merging.

GitHub Copilot instruction files

.github/copilot-instructions.md
# Contoso database — instructions for GitHub Copilot

## Platform
- Azure SQL Database, compatibility level 170. Do not use features that need PREVIEW_FEATURES.

## T-SQL conventions
- Schema-qualify every object (dbo.Customer). List columns explicitly; never SELECT *.
- New procedures: CREATE OR ALTER, SET NOCOUNT ON, SET XACT_ABORT ON, TRY...CATCH with THROW.
- Store semi-structured data in json columns; add a JSON index for queried paths.
- Embeddings: vector(1536) from the external model Embed3Small; search with VECTOR_SEARCH
  and SELECT TOP (N) WITH APPROXIMATE, never the deprecated TOP_N parameter.

## Security
- Parameterize all input; dynamic SQL only through sp_executesql with parameters.
- Never generate GRANT ... TO public, logins with passwords, credentials or connection strings.

## Project
- Schema lives in database/ as an SDK-style SQL project; one object per file under Schema/ObjectType/.
- Reference data goes in Scripts/PostDeployment.sql as an idempotent MERGE.
.github/instructions/sql-objects.instructions.md
---
applyTo: "database/**/*.sql"
excludeAgent: "code-review"
---
# Rules for database object scripts
- File name equals the object name: database/dbo/Tables/Customer.sql.
- Tables need a named primary key constraint (PK_<Table>) and named defaults (DF_<Table>_<Column>).
- Foreign key columns get a nonclustered index.
.github/prompts/new-procedure.prompt.md
---
description: Generate a stored procedure that follows the team template
agent: agent
tools: ['mssql/*']
---
Create a stored procedure named ${input:procName:usp_VerbNoun} that ${input:purpose}.

Follow .github/copilot-instructions.md. Include:
- SET NOCOUNT ON and SET XACT_ABORT ON
- an explicit transaction for any data modification, inside TRY...CATCH with THROW
- a header comment describing parameters and result sets

Write it to database/dbo/StoredProcedures/${input:procName}.sql.
FileFrontmatterApplied
copilot-instructions.mdNoneAutomatically, to every request in the repository
*.instructions.mdapplyTo (comma-separated globs), optional excludeAgent (code-review or cloud-agent)Automatically, when a matching file is in context — together with the repository-wide file
*.prompt.mddescription, name, agent, model, tools, argument-hintOn demand, as /name in chat; inputs with ${input:name:placeholder}
AGENTS.mdNoneFor agents; the nearest file up the tree takes precedence

Precedence when several sets apply: personal instructions first, then repository, then organization — all are sent, so avoid conflicts rather than relying on order. Background in Skill 1.

MCP server configuration

.vscode/mcp.json
{
  "inputs": [
    { "id": "catalog_token", "type": "promptString", "description": "Token for the catalog API", "password": true }
  ],
  "servers": {
    // SQL MCP Server started by VS Code as a child process (local development)
    "contoso-sql-local": {
      "type": "stdio",
      "command": "dab",
      "args": ["start", "--mcp-stdio", "role:anonymous", "--config", "${workspaceFolder}/api/dab-config.json", "--LogLevel", "error"]
    },
    // SQL MCP Server already running in Azure Container Apps (HTTP transport)
    "contoso-sql-remote": {
      "type": "http",
      "url": "https://contoso-catalog.azurecontainerapps.io/mcp",
      "headers": { "Authorization": "Bearer ${input:catalog_token}" }   // prompted, never stored
    },
    // A Fabric data agent published as an MCP server (lakehouse or warehouse data)
    "fabric-sales-agent": {
      "type": "http",
      "url": "<MCP server URL copied from the data agent's Settings → Model Context Protocol tab>"
    }
  }
}
  • stdio: the client launches the server process (no port, no network); DAB forces Simulator authentication and runs every call as the role after --mcp-stdio.
  • http: connect to a running server's /mcp URL; authenticate with headers fed from inputs (prompted, optionally masked) or an Entra sign-in.
  • Start, stop and inspect servers with MCP: List Servers; enable or disable individual tools in agent mode's tools picker.
  • Visual Studio reads .mcp.json and .vscode/mcp.json; GitHub Copilot in SSMS 22.7+ supports MCP servers in agent mode.