14/09/2026
Developing dbt locally against Microsoft Fabric (without losing an afternoon to auth)
If your organization has landed on Microsoft Fabric as its data warehouse and dbt as its transformation tool, you’ll eventually hit the same question every dbt-on-Fabric team hits: how do I actually develop this locally, in VS Code, against a real Fabric Warehouse or Lakehouse — without a slow CI round-trip for every SQL change?
The short answer: dbt-core plus the dbt-fabric adapter, running from WSL2, talking to Fabric’s SQL analytics endpoint over ODBC. The setup is well documented in pieces, scattered across driver docs, adapter READMEs, and forum threads. What’s not well documented is the one step that will eat your afternoon if you’re not warned about it in advance: interactive login silently doesn’t work inside WSL. More on that below, first, the setup.
What we’re building
- A local dbt project (models, dbt_project.yml) that lives entirely on your machine.
- A connection profile pointing at your Fabric Warehouse/Lakehouse’s SQL endpoint.
- No copy of “the Fabric project” anywhere, there isn’t one. Fabric holds the data; your repo holds the transformation logic. Running dbt run is what connects the two.
This is deliberately the simplest way to get productive: no CI pipeline, no service principal, no Git-integrated workspace sync — just you, a terminal, and a warehouse you have read/write access to. Once this works, layering CI/CD or workspace-level Git sync on top is a separate, later concern — not a prerequisite.
Prerequisites
- Windows 11 with WSL2 (Ubuntu). Check with wsl -l -v; if nothing’s installed, wsl –install -d Ubuntu-24.04.
- VS Code on Windows, with the code CLI available.
- Access to a Fabric workspace with a Warehouse or Lakehouse, and your own Entra ID account with read/write rights on it.
One thing to check first: a Lakehouse’s SQL analytics endpoint is read-only. dbt can read from it fine as a source, but it cannot create views or tables there. If you only have a Lakehouse and no Warehouse, you can still follow this guide to read, but any model that materializes as view/table needs a Warehouse as its target.
Step 1 — Install the ODBC driver (inside WSL)
Fabric’s SQL endpoint speaks SQL Server’s wire protocol. dbt reaches it through ODBC, and the driver is a system package — pip install alone won’t pull it in.
curl -sSL -O https://packages.microsoft.com/config/ubuntu/24.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
sudo apt-get update -y
sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18 unixodbc-dev unixodbc
Step 2 — Python environment and dbt
cd /mnt/c/<path-to-your-project>
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install dbt-core dbt-fabric
dbt --version
Pin the versions in requirements.txt so teammates reproduce the same environment:
dbt-core==1.12.0
dbt-fabric==1.10.0
Step 3 — Project structure
At minimum, in the project root:
dbt_project.yml
models/
.gitignore (target/, dbt_packages/, logs/, .venv/, profiles.yml)
name: 'my_dbt_project'
version: '1.0.0'
config-version: 2
profile: 'my_dbt_project'
model-paths: ["models"]
clean-targets: ["target", "dbt_packages"]
models:
my_dbt_project:
+materialized: view
Step 4 — profiles.yml, and the gotcha
Create ~/.dbt/profiles.yml inside WSL (never commit this file — it lives outside the repo, per the .gitignore above):
my_dbt_project:
target: dev
outputs:
dev:
type: fabric
driver: 'ODBC Driver 18 for SQL Server'
server: '<workspace>.datawarehouse.fabric.microsoft.com'
port: 1433
database: '<warehouse-or-lakehouse-name>'
schema: dbo
authentication: cli
user: '<you>@yourcompany.com'
threads: 4
encrypt: true
trust_cert: false
login_timeout: 60
Every tutorial you’ll find defaults to authentication: ActiveDirectoryInteractive: log in with your own Entra ID account through a browser popup, no password in a config file. It’s the right idea, and it works fine — if VS Code and dbt are running natively on Windows or macOS.
Inside WSL, it usually doesn’t. The ODBC driver tries to launch a browser to complete the login. WSL2 has no default way to hand that off to your Windows desktop unless you’ve separately installed browser-launch interop (wslu/wslview, a BROWSER env var pointing somewhere useful). Without it, nothing visibly happens — no popup, no error — and after your configured login_timeout (or longer), dbt run simply fails with:
Database Error
('HYT00', '[HYT00] [Microsoft][ODBC Driver 18 for SQL Server]Login timeout expired (0) (SQLDriverConnect)')
There is no device-code equivalent baked into the driver either, passing something like ActiveDirectoryDeviceCode doesn’t fail informatively, it just throws:
('08001', "... Invalid value specified for connection string attribute 'Authentication' ...")
because that value doesn’t exist for this driver/adapter combination.
The fix that actually works in WSL: authenticate through the Azure CLI instead, and point dbt at that cached login via authentication: cli.
curl -sL https://aka.ms/InstallAzureCLIDeb -o /tmp/install_az.sh
sudo bash /tmp/install_az.sh
az login --use-device-code
az login –use-device-code prints a URL and a short code directly in your terminal — open the URL in any browser, on any device, type the code, sign in. No interop between WSL and Windows required, because nothing needs to pop up on your desktop at all. The dbt-fabric adapter recognizes authentication: cli and reuses whatever az already has cached, refreshing it transparently as needed.
If you’re running dbt natively on Windows/macOS (not WSL), ActiveDirectoryInteractive is simpler and works as documented — this whole detour is specific to the WSL layer.
Step 5 — Wire up VS Code
code --install-extension ms-vscode-remote.remote-wsl
code --install-extension ms-python.python
code --install-extension innoverio.vscode-dbt-power-user
Open the project from inside WSL, not from Windows Explorer:
cd /mnt/c/<path-to-your-project>
code .
VS Code should open in WSL mode (bottom-left status bar shows something like “WSL: Ubuntu-24.04”). Select ./.venv/bin/python as the interpreter, or set it once in .vscode/settings.json:
{
"python.defaultInterpreterPath": "./.venv/bin/python",
"python.terminal.activateEnvironment": true
}
Step 6 — Prove it end to end
From the VS Code integrated terminal (not a background task — az login needs a human):
source .venv/bin/activate
dbt debug
A successful run ends with All checks passed!. Then run a small, harmless model to confirm the round trip actually writes to Fabric — a one-liner is enough:
-- models/staging/stg_example.sql
select 1 as id, 'hello from dbt on fabric' as message
dbt run --select stg_example
If it succeeds, you’ll find a new view in your warehouse’s schema — visible in the Fabric portal, or previewable straight from VS Code via the dbt Power User extension.
Day-to-day
The dbt Power User extension adds CodeLens actions above every model:
- Compile — render the SQL (Jinja/ref() resolved) without touching Fabric
- Preview — run the query, see results inline
- Run — actually materialize the model in Fabric
Its sidebar shows a lineage graph as your model count grows, and autocompletes ref() / source() calls.
What this setup is (and isn’t)
This gets you a fast local dev loop against a real warehouse: no CI queue, no waiting. It deliberately does not cover automated deployment (a CI/CD pipeline building –target prod on merge, typically authenticating with a service principal instead of a personal login), and it does not cover syncing an entire Fabric workspace’s items (Lakehouses, pipelines, notebooks) through Git — that’s a different mechanism (Fabric’s workspace Git integration) solving a different problem. Both are worth their own write-up; this one is deliberately just about getting a single dbt project talking to a single warehouse from your own machine.
Conclusion
Getting dbt running locally against Microsoft Fabric isn’t hard, but the path is scattered across driver docs, adapter READMEs, and forum threads, and the one step that actually derails most first attempts (interactive Entra ID login silently failing inside WSL2) isn’t documented anywhere obvious. Once you know to authenticate through the Azure CLI (authentication: cli) instead of relying on a browser popup that WSL can’t reliably hand off to Windows, the rest of the setup is a fairly standard dbt-core project: install the ODBC driver, pin your Python environment, write a profiles.yml, and confirm the round trip with dbt debug and a one-line model.
What you end up with is a genuinely fast local development loop against a real warehouse — no CI queue, no waiting on a pipeline for every SQL change — while keeping production deployment (service-principal-authenticated CI/CD) and Fabric’s own workspace-level Git integration as clean, separate concerns to layer on later rather than prerequisites to get started. For a team standardizing on Fabric plus dbt, that’s the difference between a smooth onboarding and an afternoon lost chasing a login timeout with no useful error message.

Kenny Peeters
Consultant @ Cubis
