Semantic model data connections
Every Direct Lake semantic model in every workspace is bound to a shareable cloud connection that authenticates as sp-fabric-platform-admin. This is the fixed-identity pattern — at query time, data access runs as the SPN, not as the viewer. Developers open any model and it works, without needing a role on the underlying warehouse.
There is one SCC per environment / warehouse (DEV, UAT, PROD, and each feature env). Binding requires the SCC's server + database to match the model's M expression exactly, and each env has its own Gold Warehouse GUID — so a single global SCC can't bind feature models.
Why this exists
Direct Lake defaults to SSO. Under SSO, the viewer's identity is checked against the SQL analytics endpoint. Developers (members of FP GER Fabric Developers) have no direct role on DEV-Gold, so SSO queries fail and Fabric falls back to DirectQuery — every table shows the warning "Only DAX queries may fall back to DirectQuery" and visuals degrade.
Ownership doesn't matter at query time — only the effective identity the connection uses.
The per-env connection
One SCC per env, created on demand and reused across every deploy of that env:
| Field | Value |
|---|---|
| Name | fabric-dev-gold-warehouse-fixed (DEV) · fabric-feat-<name>-gold-warehouse-fixed (feature) · analogous for UAT/PROD |
| Type | SQL (Shareable Cloud) |
| Server | The env's Gold Warehouse SQL analytics endpoint (from deployment/<env>.yml → connection_overrides.sql_database.server) |
| Database | The env's Gold Warehouse GUID (from connection_overrides.sql_database.database_id) |
| Credentials | sp-fabric-platform-admin (ServicePrincipal) |
| SSO | disabled |
| Encryption | Encrypted |
The SPN is Admin on every Gold Warehouse, so it can read whichever env it's pointed at. All semantic models bound to their env's connection query through the SPN's identity, and developers need zero direct access to the warehouse workspace.
When the binding happens
- New feature env →
scripts/fabric dev startcallsfabric_connections.py bindafterfabric-cicddeploy. No manual step. - DEV → one-time retrofit already applied. New DEV deploys keep the binding because
fabric-cicduses workspace-Admin (not ownership) to update definitions; ownership stays on the SPN. - Manual re-bind →
bash scripts/fabric dev bind-connections <feature-name|dev>.
Models with no SQL data source (static DATATABLE slicers, import-mode) are automatically skipped.
Ownership strategy
- Model owner:
sp-fabric-platform-admin. Durable —fabric-cicddeploys do not change ownership. - Query-time identity: same SPN, via the fixed-identity connection.
- User access: Build permission on the semantic model. No workspace role on
DEV-Goldrequired.
If a model was previously owned by a user, the binder calls the Power BI Takeover API (POST /v1.0/myorg/groups/{ws}/datasets/{id}/Default.TakeOver) before binding, so the SPN can configure the connection.
Recycling and stale-connection audit
DEV/UAT/PROD SCCs are durable and reused across deploys. Feature-env SCCs live as long as the feature env does — scripts/teardown_feature_env.sh only touches workspace-scoped items (warehouse, lakehouse, shortcuts, role assignments, workspace git connection), so orphaned feature SCCs accumulate over time and should be pruned periodically.
To audit connections that aren't referenced by any item (drift from manual UI creation):
bash scripts/fabric dev connections # list all
python scripts/fabric_connections.py prune # show unreferenced
prune never auto-deletes. Review the list, then delete via the Fabric UI (Manage connections and gateways) or a one-off REST call.
Adding a new data source
The per-env SCC covers that env's Gold Warehouse (SQL endpoint). If a new Direct Lake model references a different source (e.g. a Bronze lakehouse SQL endpoint), create a second SCC with the same pattern — same naming convention, SPN credentials, SSO off — and extend fabric_connections.py to try both when binding.
Troubleshooting
| Symptom | Check | Fix |
|---|---|---|
| Warning on every table in Fabric UI | Model's binding in Settings → Gateway and cloud connections | Re-run fabric dev bind-connections <env> |
BindNotModelOwner error | Model is owned by another user | The binder auto-takes-over; if it still fails, check SPN permissions on workspace |
BindConnectionDetailNotFound on a feature env | Binder was pointed at the wrong env's SCC (server/database_id mismatch with the model's M expression) | Re-run fabric dev bind-connections <feature-name> — it now passes --env feat-<name> so the SCC matches the env's warehouse. |
FAIL — all N model(s) skipped, none bound | Regression guard: the SCC's server+database didn't match any model. Almost always an --env / config mismatch. | Verify deployment/<env>.yml's connection_overrides.sql_database.{server,database_id} matches the actual feature warehouse, then re-run. |
BindConnectionDetailNotFound on a Gold model | Model references a different data source than Gold (e.g. Bronze lakehouse, import mode) | Expected — skipped. Add a new SCC if needed. |
| Connection missing entirely | Initial bootstrap failed | python scripts/fabric_connections.py ensure --env <dev|feat-name|uat|prod> |