Tutorial: Set up CI/CD integrations on dbt Projects on Snowflake¶
Introduction¶
This tutorial is a continuation of the Getting started with dbt Projects on Snowflake tutorial. It assumes you’ve already completed that tutorial and have a working Snowflake environment with your database, schemas, warehouse, and source data set up.
This tutorial guides you through building a secure CI/CD pipeline for dbt Projects on Snowflake using OIDC authentication, Snowflake CLI, and dbt project objects to automate testing, deployment, and orchestration with minimal overhead.
The tutorial supports three CI/CD platforms: GitHub Actions, GitLab CI/CD, and Azure DevOps. Choose your platform in the tabbed sections below and follow along.
For more information, see Understand CI/CD for dbt Projects on Snowflake.
Overview¶
This tutorial walks you through the following steps:
-
Setting up your Snowflake environment:
- You choose one of three ways to prepare dev and prod targets (full database clone, partial clone, or brand-new databases).
- Your dbt project must include a
profiles.ymlthat refers to these dev and prod targets.
-
Setting up an OIDC service user for secure authentication: Instead of passwords or long-lived tokens, you create a Snowflake service user that trusts your CI/CD platform through OpenID Connect. This enables secure, short-lived, per-run authentication.
-
Setting up network policies: (Optional) If your Snowflake account restricts inbound IPs, you can add your CI/CD platform’s runner IPs to your service user’s network policy (using Snowflake-managed network rules where available). Otherwise, you can skip this step.
-
Storing secrets and variables in your CI/CD platform to configure Snowflake CLI in your workflows:
- Your Snowflake account identifier
- (Optionally) the Snowflake username
- The target database and schema where dbt project objects will be deployed
-
Creating CI/CD pipeline workflows:
- CI workflow that triggers on pull requests, deploys a tester dbt project object, and runs
dbt buildto build models and test them in DAG order. If anything breaks, the pull request fails.- CD workflow that triggers on merges to main, deploys the production dbt project object, and optionally applies scheduling.
At the end of the tutorial, you will have:
- A fully automated, CI/CD-driven dbt workflow
- Secure OIDC authentication
- Consistent, tested deployments into Snowflake
- Version-controlled orchestration (optional)
- A repeatable template for scaling dbt workflows across teams
Prerequisites¶
-
CI/CD platform (one of the following):
- GitHub: A repository with GitHub Actions enabled.
- GitLab: A project with CI/CD pipelines enabled.
- Azure DevOps: A project with Azure Pipelines enabled.
-
Snowflake
- Completion of the
Getting started with dbt Projects on Snowflake tutorial,
which sets up the
tasty_bytes_dbt_dbdatabase,dev/prodschemas,TASTY_BYTES_DBT_WHwarehouse, and source data. - Basic understanding of dbt Projects on Snowflake. For more information, see dbt Projects on Snowflake.
- A Snowflake account and user with privileges as described in Access control for dbt projects on Snowflake.
- Privileges or administrator assistance to create and edit the following:
- CI/CD platform secrets or variables to specify the Snowflake account and (optional) username
- A Snowflake service user
- Network policy
- Completion of the
Getting started with dbt Projects on Snowflake tutorial,
which sets up the
Platform-specific reference guides
For detailed configuration options beyond what this tutorial covers, see the dedicated Snowflake CLI CI/CD guides:
Set up your environment¶
Set up where your dbt project will read and write in Snowflake, then update your profiles.yml file.
Create dev and prod databases and schemas¶
Note
If you’ve already completed the
Getting started with dbt Projects on Snowflake tutorial
and run the tasty_bytes_setup.sql file, your database (tasty_bytes_dbt_db) and schemas (dev, prod) already exist. You can
skip this step. For details, see
Run the SQL commands in tasty_bytes_setup.sql to set up source data.
If you didn’t complete the getting-started tutorial, run the following SQL commands to create the database and schemas this tutorial uses:
Tip
If you already have a production database that you want to test against, you can use zero-copy cloning to create a high-fidelity dev database without duplicating storage costs. Zero-copy cloning is cost-effective because you only pay for storage on rows that change during dbt runs.
-
Clone the full database — gives you a complete replica of production:
-
Clone only specific schemas — useful when you only need certain schemas for testing:
For either option, replace <your_production_db> with the name of your existing production database.
Update your profiles.yml file¶
To manage CI/CD for a dbt project object, you must include a profiles.yml file inside your dbt project folder (for
example, my_dbt_project/profiles.yml). This file defines your dev and prod targets and uses placeholder values that your CI/CD platform’s secrets or variables will later replace.
Edit this file to reference the dev and prod databases and schemas you created, as shown below:
Key points from the example:
target: devsets the default target of the dbt project. This value can be overridden by Snowflake CLI or a dbt project object.devandprodboth usetype: snowflake.- Database and schema point to the databases and schemas you created in the previous step (or already set up from the getting-started tutorial).
- Warehouse is the warehouse created in the getting-started tutorial (
TASTY_BYTES_DBT_WH). - Account and user are set to dummy values like ‘_’ because they’ll be replaced by your CI/CD platform’s secrets or variables later.
threads: 8sets the number of concurrent threads dbt uses. Snowflake recommends 8 threads.
Create a CI/CD service user in Snowflake (recommended)¶
CI/CD pipelines run using the Snowflake user specified in your Snowflake CLI commands. To keep things clean and secure, create a dedicated Snowflake user for all CI/CD workflows and grant it the required privileges.
Recommended: OIDC-based service user¶
This approach uses OpenID Connect (OIDC) rather than long-lived credentials. The service user trusts your CI/CD platform as an identity provider, allowing pipelines to request short-lived tokens for each run. For more information, see Workload identity federation.
Create an OIDC-based service user for your platform:
Each OIDC service user must have a unique subject. Use a repo path and an environment name, for example
repo:<org>/<repo>:environment:<environment_name>. The environment name must match exactly in your GitHub Action YAML file.
The subject uses GitLab’s project path format. Restrict it to a specific branch for tighter security.
For more information, see GitLab CI/CD component.
The subject uses an Azure DevOps service connection identifier. Replace <azure-ad-tenant-id> with your Microsoft Entra tenant ID.
For more information, see Azure DevOps extension.
After you create your user, explicitly grant the default role for the service user to assume that role. The DEFAULT_ROLE parameter only sets the user’s default role and doesn’t grant it. Then set a default warehouse.
Alternative: PAT-based authentication (less secure)¶
If you prefer to use one Snowflake user across multiple repositories, or cannot use OIDC, you can create the user with a personal access token (PAT) instead.
This method is easier to reuse across repositories but less secure because it relies on long-lived credentials and requires manual rotation.
Note
This example uses GitHub-oriented names. If you’re on GitLab or Azure DevOps, substitute a service user name that matches your platform (for
example, gitlab_cicd_service_user or ado_cicd_service_user).
(Optional) Set up a network policy for your CI/CD platform¶
Now that you’ve created the service user that Snowflake CLI will use, configure this user to connect to your Snowflake account from within your CI/CD platform.
Note
Creating or modifying network policies requires ACCOUNTADMIN or an equivalent role.
Determine whether you need a network policy¶
- If your account restricts inbound access, you must create or update a network policy to add your CI/CD platform’s runner IPs to your allowlist. Snowflake simplifies this with Snowflake-managed network rules for some platforms. For more information, see Network rules.
- If your account does not restrict inbound access, no network policy changes are required.
If you’re unsure, skip this step for now and return only if you see an error like: Incoming request with IP/Token <IP> is not allowed to access Snowflake.
To create and apply a network policy to a user, choose one of the following options:
- Create a new network policy and assign it to the service user, or
- Add a network rule to an existing network policy that the user already uses.
Note
Before doing this, consult your Snowflake account admin. They must ensure the policy includes not only the CI/CD platform network rule but also any other IP ranges your organization requires.
Once a network policy is applied, Snowflake restricts user access based on its allowed and blocked IP ranges. Your account admin might need to adjust the policy or apply it account wide to avoid unintentionally blocking essential access.
Option 1: Create a new network policy and apply it to the user¶
A Snowflake user can have only one network policy at a time. If the user doesn’t have one or you want to replace the existing policy, complete the following steps:
GitLab doesn’t have a Snowflake-managed network rule. GitLab.com hosted (shared) runners don’t have static, allowlistable IP addresses: they run as ephemeral VMs drawn from large Google Cloud and AWS ranges, and GitLab recommends against allowlisting those ranges. If your Snowflake account restricts inbound IPs, run your pipelines on a self-hosted GitLab Runner (or route runner egress through a proxy with a fixed IP) and add that static IP to a network rule.
Azure DevOps doesn’t have a Snowflake-managed network rule. If you use Microsoft-hosted agents, allow the Azure DevOps service IP ranges. If you use self-hosted agents, add your agent’s static IP.
Option 2: Add a network rule to an existing network policy¶
If the user already has a network policy, you can add the appropriate rule to it. First, check the user’s current network policy:
Note
If the network policy is applied at the account level or shared by many users, updating it will affect everyone.
The user inherits the update automatically since they’re already assigned to this policy.
Configure CI/CD secrets and variables¶
Your CI/CD pipelines use Snowflake CLI to connect to your Snowflake account, so you must configure platform secrets and variables first. This is how the CI/CD integration passes Snowflake account info into Snowflake CLI inside pipeline workflows.
Configure secrets¶
Add secrets to securely store the information Snowflake CLI needs to identify your Snowflake account and, if required, the user it should authenticate as.
-
In your GitHub repository, go to Settings.
-
From the left-hand side navigation, select Secrets and variables » Actions.
-
Under Secrets, select New repository secret.
-
Add a secret to connect your Snowflake account:
- Name:
SNOWFLAKE_ACCOUNT - Value: Your Snowflake account identifier (for example,
org_name-account_name). This value tells Snowflake CLI which account you want to connect to.
- Name:
-
Select Add secret.
-
(Optional) If you aren’t using OIDC, select New repository secret to specify the Snowflake username the CLI should use when connecting. It specifies which user credentials to run commands under.
- Name:
SNOWFLAKE_USER - Value: Optional if you’re using OIDC or credential-less authentication.
- With OIDC, Snowflake CLI automatically matches the GitHub Action’s subject to the OIDC service user (created in Step 3), so this is not required.
- Without OIDC, you must specify a user (and supply password or key credentials). As a recommended best practice, you should create a personal access token in Snowsight. For more information, see Generating a programmatic access token.
- Name:
-
Select Add secret.
-
(Optional) If you aren’t using OIDC, select New repository secret to specify the service user’s personal access token that the CLI should use when connecting.
- Name:
SNOWFLAKE_PAT - Value: Optional if you’re using OIDC or credential-less authentication.
- Name:
-
In your GitLab project, go to Settings » CI/CD.
-
Expand the Variables section.
-
Select Add variable.
-
Add a variable for your Snowflake account:
- Key:
SNOWFLAKE_ACCOUNT - Value: Your Snowflake account identifier (for example,
org_name-account_name). - Enable Mask variable to keep it hidden in job logs.
- Key:
-
Select Add variable.
-
Add a variable for your Snowflake user:
- Key:
SNOWFLAKE_USER - Value: The service user you created (for example,
gitlab_cicd_service_user). - Enable Mask variable.
- Key:
-
Select Add variable.
-
(Optional) If you aren’t using OIDC, add a variable for the PAT:
- Key:
SNOWFLAKE_PASSWORD - Value: The personal access token.
- Enable Mask variable and Protect variable.
- Key:
-
In your Azure DevOps project, go to Pipelines » Library.
-
Select + Variable group or open an existing variable group.
-
Add a variable for your Snowflake account:
- Name:
SNOWFLAKE_ACCOUNT - Value: Your Snowflake account identifier (for example,
org_name-account_name). - Select the lock icon to mark it as secret.
- Name:
-
Add a variable for your Snowflake user:
- Name:
SNOWFLAKE_USER - Value: The service user you created (for example,
ado_cicd_service_user). - Select the lock icon to mark it as secret.
- Name:
-
(Optional) If you aren’t using OIDC, add a variable for the PAT:
- Name:
SNOWFLAKE_PASSWORD - Value: The personal access token.
- Select the lock icon to mark it as secret.
- Name:
-
Select Save.
-
Link this variable group to your pipeline in the pipeline YAML or pipeline settings.
Configure variables¶
These help Snowflake CLI connect to the right database and schema.
-
In your GitHub repository, go to Settings.
-
From the left-hand side navigation, select Secrets and variables » Actions.
-
Under Variables, select New repository variable.
-
Add a database variable:
- Name:
SNOWFLAKE_DATABASE - Value: Enter an existing database where the dbt project object will be created.
- Name:
-
Select Add variable.
-
Add a schema variable:
- Name:
SNOWFLAKE_SCHEMA - Value: Enter an existing schema where the dbt project object will be created.
- Name:
-
Select Add variable.
-
In your GitLab project, go to Settings » CI/CD.
-
Expand the Variables section.
-
Add a variable for your database:
- Key:
SNOWFLAKE_DATABASE - Value: Enter an existing database where the dbt project object will be created.
- Key:
-
Add a variable for your schema:
- Key:
SNOWFLAKE_SCHEMA - Value: Enter an existing schema where the dbt project object will be created.
- Key:
Add these to the same variable group you created for secrets, or define them as pipeline variables:
-
Add a database variable:
- Name:
SNOWFLAKE_DATABASE - Value: Enter an existing database where the dbt project object will be created.
- Name:
-
Add a schema variable:
- Name:
SNOWFLAKE_SCHEMA - Value: Enter an existing schema where the dbt project object will be created.
- Name:
-
Select Save.
Create your Continuous Integration (CI) pipeline¶
This step is where automation starts. This CI workflow runs whenever a pull request (or merge request) targets main. It:
- Creates a tester dbt project object in Snowflake
- Runs
dbt buildagainst your dev target, which builds all models and runs tests in DAG order, failing early if any test fails - Fails the pull request (or merge request) if the dbt execution fails
Create your CI workflow file¶
Warning
Don’t use --force in these deploy steps unless you intentionally want to recreate the dbt project object. In snow dbt deploy, --force runs CREATE OR REPLACE DBT PROJECT, which removes all existing versions and run history.
-
In your GitHub repository, go to Actions.
-
From the left-hand side navigation, select New workflow.
-
Select set up a workflow yourself to create an empty workflow.
-
Name the file
incoming_pr.yml. -
Copy and paste the following into the file:
-
Select Commit changes.
-
Select Create a new branch for this commit and start a pull request.
-
Select Propose changes.
-
After you finish submitting the pull request, you should see your
incoming_pr.ymlaction start to run. -
After it’s merged, the file will be saved to
.github/workflows/incoming_pr.yml.
Create a .gitlab-ci.yml file at the root of your repository (or add the following to your existing one):
Commit this file to a branch and open a merge request to see the pipeline run.
Create a file named azure-pipelines-ci.yml at the root of your repository:
In Azure DevOps, go to Pipelines » New pipeline, select your repository, and point it to this YAML file.
Key pieces from the workflow file¶
Regardless of which platform you chose, the pipeline follows the same pattern:
-
Trigger: Runs on incoming pull requests (or merge requests) targeting
main. -
OIDC authentication: Each platform requests a short-lived token that Snowflake validates against your service user’s WORKLOAD_IDENTITY configuration.
-
Snowflake CLI with
-xflag: The-xflag (--temporary-connection) tells Snowflake CLI to build the connection from environment variables (such asSNOWFLAKE_ACCOUNTandSNOWFLAKE_DATABASE) rather than reading a named profile fromconfig.toml, which isn’t available in a CI/CD runner. -
Steps in the job:
-
Check out repository code.
-
Install Snowflake CLI with OIDC enabled.
-
Verify the connection with
snow connection test -x. -
Deploy a tester dbt project object using
snow dbt deploy ... -x(with--sourceif the dbt project is in a subfolder). -
Build and test the dbt project in DAG order:
snow dbt execute -x tester_tasty_bytes_dbt_project_object build --target devUsing
buildinstead of separaterunandtestcommands ensures that tests execute immediately after each model is built, in dependency order. If an upstream model’s test fails, downstream models aren’t built, providing faster feedback and preventing invalid data from propagating.
-
-
Once you commit this new workflow on a branch and open a pull request (or merge request), the pipeline will run. If the dbt project object fails to build a model or any test fails, the CI check fails and the change can’t be merged.
Create your Continuous Deployment (CD) pipeline¶
The CD workflow runs after code is merged to main (or any direct push to main), ensuring the dbt project object in Snowflake reflects the latest code.
Create your CD workflow file¶
Warning
Don’t use --force in these deploy steps unless you intentionally want to recreate the dbt project object. In snow dbt deploy, --force runs CREATE OR REPLACE DBT PROJECT, which removes all existing versions and run history.
-
In your GitHub repository, go to Actions.
-
From the left-hand side navigation, select New workflow.
-
Select set up a workflow yourself to create an empty workflow.
-
Name the file
pr_merged.yml. -
Copy and paste the following into the file:
-
Select Commit changes to save the file to
.github/workflows/pr_merged.yml. -
Navigate to the Actions tab of your repository to see your
pr_merged.ymlaction start to run.
Add the following deploy job to your .gitlab-ci.yml file (after the CI test job you created earlier):
Commit and push to main to trigger the deployment.
Create a file named azure-pipelines-cd.yml at the root of your repository:
Create a second pipeline in Azure DevOps pointing to this YAML file.
Key pieces from the workflow file¶
-
Trigger: Runs on pushes to
main(after a merge). -
Same OIDC authentication and
-xflag as the CI pipeline. -
Steps:
- Check out the repository code.
- Install Snowflake CLI with OIDC.
- Verify the connection with
snow connection test -x. - Deploy/update the production dbt project object with
snow dbt deploy ... --default-target prod -x. - (Optional) Run a
schedules.sqlfile to manage tasks (see next section).
-
Once this workflow is in place, every successful merge to main updates the dbt project object in Snowflake.
(Optional) Add orchestration with Snowflake tasks¶
Orchestrate executions of your dbt project object using a schedules.sql file and Snowflake tasks (triggered from the CD workflow):
-
In your repository, navigate to your dbt project (for example,
tasty_bytes/). -
Create a file named
schedules.sqland copy and paste the following into the file.This file:
-
Suspends any existing tasks
-
Creates or alters tasks to:
- Run a subset of the DAG on a schedule, failing early if any test fails
- Run the full project, failing early if any test fails
-
Resumes tasks in the correct order (child → root)
-
-
Commit the file to your repository.
-
In the CD workflow file you created earlier, uncomment the step that runs
schedules.sql(see the commentedschedules.sqlline in your CD pipeline). -
Commit the change.
Next steps¶
Next steps to improve your workflow:
-
Use zero-copy cloning in CI:
Test against fresh production data by adding a step in your CI pipeline before deploying your tester dbt object:
-
Add alerting:
Configure Slack or email notifications in your CI/CD platform, or use Snowflake task error notifications.
For more information, see Configure a task to send error notifications.
-
Explore the platform-specific Snowflake CLI CI/CD reference guides for advanced configuration:
-
Explore Managing dbt Projects on Snowflake using Snowflake CLI.