env_var() is convenient, but misuse can leave passwords in compiled SQL or logs. For both the exam and real work, it is essential to understand where it is evaluated and where the values end up.
This article walks through safe usage and anti-patterns based on the official dbt-core behavior, how to combine it with CI/CD and Vault, and how to think about audit.
env_var(name, default=None) reads OS environment variables at the moment Jinja is evaluated. In dbt, the main evaluation points for Jinja are when models and macros are compiled, and when dbt_project.yml and profiles.yml are loaded.
Crucially, if you use env_var() inside a SQL file, the value can end up baked into the compiled SQL under target/compiled. The rule is: never bake secrets into SQL — reference them only from connection info (profiles.yml).
profiles.yml is not included in run artifacts (manifest.json, run_results.json, catalog.json). On the other hand, values supplied via dbt_project.yml vars or model config can show up in artifacts, so avoid placing secrets there.
Safe reference example in profiles.yml (Snowflake)
snowflake:
target: prod
outputs:
prod:
type: snowflake
account: "{{ env_var('SNOWFLAKE_ACCOUNT') }}"
user: "{{ env_var('SNOWFLAKE_USER') }}"
password: "{{ env_var('SNOWFLAKE_PASSWORD') }}"
role: "{{ env_var('SNOWFLAKE_ROLE') }}" # Even non-secret values: be cautious with default
warehouse: "{{ env_var('SNOWFLAKE_WAREHOUSE') }}"
database: ANALYTICS
schema: CORE
The places where secrets most often leak are: compiled SQL, run logs, dbt artifacts (such as manifest.json), and CI/CD job logs or environment dumps. env_var() itself simply returns a value, so where you reference it is what matters most.
dbt does mask some sensitive data in places, but not everything is guaranteed. On the operations side, enforce a strict don't-emit, don't-retain design.
Dangerous example (printing or embedding in SQL inside a model)
-- model.sql (do NOT do this)
{{ log(env_var('SNOWFLAKE_PASSWORD'), info=True) }} -- ends up in logs
select '{{ env_var('SNOWFLAKE_PASSWORD') }}' as leaked; -- left in compiled SQL
The principle: store secrets in an external secret manager, inject them temporarily as environment variables during CI/CD execution, and let dbt reference them only via env_var() in profiles.yml. This keeps secrets out of SQL and artifacts and makes rotation easy.
If you use dbt Cloud, prefer provider integrations and managed connection settings, and avoid handling secrets via arbitrary env_var values. For OSS execution, combine env_var with your CI platform's masking features (GitHub Actions secrets, GitLab CI variables, etc.).
Temporary injection example with GitHub Actions (OSS execution)
jobs:
run-dbt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install dbt-snowflake
- name: Run dbt
env:
SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }}
SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }}
SNOWFLAKE_WAREHOUSE: ${{ secrets.SNOWFLAKE_WAREHOUSE }}
run: |
dbt deps
dbt build --target prod
Patterns such as putting secrets in vars, printing them via log() inside models or macros, or interpolating them into SQL via Jinja all increase long-term leak risk. vars in particular ends up in artifacts, so it is forbidden.
Silently passing through with a default when a variable is unset is another common source of incidents. Provide a helper macro to check env_var() presence and fail explicitly when missing.
Required env var validation macro (fails on missing)
{% macro required_env_var(name) -%}
{% set val = env_var(name) %}
{% if not val %}
{{ exceptions.raise_compiler_error('Missing required env var: ' ~ name) }}
{% endif %}
{{ return(val) }}
{%- endmacro %}
# Usage in profiles.yml (Jinja allowed)
password: "{{ required_env_var('SNOWFLAKE_PASSWORD') }}"
The correct answer for both real work and the exam is the same configuration: store externally, inject as environment variables at runtime, and reference only from profiles.yml via env_var(). The table below compares the typical options.
| Method | Exposure risk (SQL / logs / artifacts) | Ease of rotation | Exam recommendation |
|---|---|---|---|
| Store in dbt_project.yml vars | High (easily ends up in artifacts) | Low (requires code changes) | Not allowed |
| Hardcode plaintext in profiles.yml | Medium (not in artifacts, but high impact if leaked) | Low (manual distribution and replacement) | Not allowed |
| External secret -> env var -> env_var() in profiles.yml | Low (can be designed to leave nothing in SQL or artifacts) | High (rotate centrally externally) | Recommended (the standard answer) |
| Managed connections in dbt Cloud | Low (managed by the platform) | High (update via UI/API) | Recommended (when using Cloud) |
| Dynamic auth via Vault + CI injection + env_var() | Low (can be designed with short-lived, least-privilege credentials) | High (automatic rotation) | Recommended (for mature operations) |
Databricks (token) example: profiles.yml
databricks:
target: prod
outputs:
prod:
type: databricks
host: "{{ env_var('DATABRICKS_HOST') }}"
http_path: "{{ env_var('DATABRICKS_HTTP_PATH') }}"
token: "{{ required_env_var('DATABRICKS_TOKEN') }}"
catalog: main
schema: core
From an audit perspective, the essentials are: automated scans of artifacts, compiled outputs, and logs for secrets; CI masking configuration and privilege separation; and periodic checks of token expiry and rotation. On the dbt side, stick to two rules: reference secrets only via env_var() in profiles.yml, and never let them flow into vars or SQL.
For exam prep, lock in these points: when env_var() is evaluated, the fact that secrets placed in vars end up in manifest.json, the danger of silently passing through with a default when unset, and the preference for platform management in dbt Cloud.
Safe secret flow (conceptual diagram)
[Secret Manager]
|
v (masked, short-lived)
[CI/CD Runner] --(env export)--> [Process Env]
|
v
[dbt env_var()]
|
v
[Adapter/Driver]
|
v
[Warehouse/BI]
Guide comment example for not using env_var() inside models or macros
{#
Notes:
- Do NOT pass env_var() into model SQL or log().
- Reference secrets only from profiles.yml.
- When unset, fail immediately with required_env_var().
#}
Analytics Engineer
問題 1
You are running OSS dbt on GitHub Actions. Which is the most appropriate way to handle the Snowflake password safely?
正解: A
Storing secrets externally, injecting them as environment variables at runtime, and referencing them only via env_var() in profiles.yml is the safe and standard approach. Putting them in vars, model SQL, or log output carries high leak risk and is inappropriate.
Does dbt automatically load .env files?
No. dbt-core itself does not auto-load .env files. Either export the variables in your shell, or inject them as environment variables through your CI/CD secrets feature. If you do use an external dotenv tool, be careful about how you handle plaintext files.
Do values from env_var() end up in dbt artifacts?
When referenced from profiles.yml as connection info, the values do not show up in artifacts like manifest.json. However, if you use env_var() inside dbt_project.yml vars or inside models/macros, the resolved values can leak into compiled SQL and artifacts.
Is it okay to pass a default to env_var()?
As a rule, do not pass a default for secrets. If the value is unset and you silently fall through to a default, you risk connecting to the wrong target and delaying detection. Use required_env_var() to fail explicitly when the variable is missing. Reserve default values for non-sensitive, well-understood items such as role names.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
dbt Models: SQL-Defined Transformation Units (2026)
Model fundamentals — SELECT-based definitions, naming, refs,...
dbt Analytics Engineering Exam: Complete Guide (2026)
Pass the AE Certification — scope, weighting, sample questio...
dbt Cloud vs dbt Core: Feature & Cost Comparison (2026)
Honest comparison of dbt Cloud vs. dbt Core — IDE, scheduler...
dbt Project Structure: models/seeds/macros Layout (2026)
Recommended dbt project layout — models, seeds, macros, snap...
dbt_project.yml Explained: Every Config (2026)
Every dbt_project.yml setting that matters — paths, vars, ma...