Snowflake Infrastructure
On this page, you will:
- Create the
SVC_DBTservice account in Terraform - Store dbt credentials in AWS Secrets Manager
- Understand the existing access grants that cover dbt-created schemas
Overview
dbt needs a Snowflake service account to run transformations in production and CI/CD. The required database, warehouse, and role hierarchy are already in place from the Data Warehouse section — this page adds only the service account.
Existing Snowflake Infrastructure (already built)
├── TRANSFORMING warehouse ← dbt will use this
├── ANALYTICS database ← dbt writes here (production)
│ ├── REPORTING schema ← Terraform-managed; BI tool access
│ └── (staging, intermediate, marts created by dbt)
├── ANALYTICS_DEV database ← dbt writes here (development)
├── ANALYTICS_TRANSFORMER role ← has write access to ANALYTICS
└── ANALYTICS_DEVELOPER role ← has read access to ANALYTICS
New (this page)
└── SVC_DBT service account
└── SVC_DBT role (dedicated, auto-created)
└── Granted ANALYTICS_TRANSFORMER
How Existing Grants Cover dbt Schemas
dbt creates schemas dynamically when it runs (STAGING, INTERMEDIATE, MARTS). These schemas do not need to be pre-created in Terraform.
The ANALYTICS database module already configures two grant types that cover dbt-created schemas:
-
ANALYTICS_TRANSFORMERhas write access at the database level via future grants — when dbt creates a new schema and writes tables to it, the transformer role has access automatically -
ANALYTICS_DEVELOPERhas read access at the database level — when dbt creates tables inSTAGING,INTERMEDIATE, orMARTS, developer role members can query them immediately
The REPORTING schema is the exception — it was created explicitly in Terraform with schema-level reader grants. dbt writes to it using the transformer role, and ANALYTICS_REPORTER reads from it via the Terraform-configured schema reader role.
No new Terraform schema resources are needed. No dbt grants configuration is needed either.
Create the Service Account
Open the terraform/snowflake/ repository and add SVC_DBT to users.tf.
Following the same pattern as SVC_DLT and SVC_AIRBYTE, use user_create_dedicated_role = true:
# users.tf — add to data_loaders or create a data_transformers section
# =============================================================================
# Data Transformers
# =============================================================================
module "user_svc_dbt" {
source = "./modules/snowflake_user"
providers = {
snowflake.sys_admin = snowflake.sys_admin
snowflake.security_admin = snowflake.security_admin
snowflake.user_admin = snowflake.user_admin
}
user_name = "SVC_DBT"
user_comment = "Service account for dbt transformation runs."
default_warehouse = module.warehouse_transforming.warehouse_name
default_role = "SVC_DBT" # set after role is created by module
# Creates a SVC_DBT role and grants it to this user
user_create_dedicated_role = true
# Key-pair authentication — set manually after Terraform apply
lifecycle {
ignore_changes = [rsa_public_key, rsa_public_key_2]
}
}
Grant ANALYTICS_TRANSFORMER to SVC_DBT
The dedicated SVC_DBT role needs the ANALYTICS_TRANSFORMER functional role so it inherits all transformation permissions:
# users.tf — below the user module
resource "snowflake_grant_account_role" "svc_dbt_gets_analytics_transformer" {
provider = snowflake.security_admin
role_name = module.role_analytics_transformer.role_name
user_name = module.user_svc_dbt.user_name
}
Why grant to the user, not the role?
The SVC_DBT dedicated role is used as the default_role so dbt connects with a predictable role. Granting ANALYTICS_TRANSFORMER directly to the SVC_DBT user means the user inherits those permissions regardless of which role is active. Either approach works — granting to the role is equally valid if you prefer the role inheritance model.
Deploy the Changes
cd terraform/snowflake
git checkout -b add-svc-dbt-user
git add users.tf
git commit -m "Add SVC_DBT service account for dbt transformations"
git push
Open a pull request, review the plan, and merge to trigger the CI/CD pipeline.
After the pipeline completes, verify the user was created:
SHOW USERS LIKE 'SVC_DBT';
DESCRIBE USER SVC_DBT;
-- default_warehouse should be TRANSFORMING
-- default_role should be SVC_DBT
Set Up Key-Pair Authentication
dbt uses key-pair authentication for SVC_DBT. Follow the same process as for other service accounts:
Generate the Key Pair
# Generate private key
openssl genrsa -out svc_dbt_rsa_key.pem 2048
# Generate public key
openssl rsa -in svc_dbt_rsa_key.pem -pubout -out svc_dbt_rsa_key.pub
# Display public key (without header/footer lines)
grep -v "PUBLIC KEY" svc_dbt_rsa_key.pub | tr -d '\n'
Assign the Public Key in Snowflake
USE ROLE USERADMIN;
ALTER USER SVC_DBT SET RSA_PUBLIC_KEY='<paste-public-key-here>';
Verify:
DESCRIBE USER SVC_DBT;
-- RSA_PUBLIC_KEY_FP should now be populated
Store the Private Key in 1Password
Store the private key file in the Service Accounts vault in 1Password:
- Title:
SVC_DBT Snowflake Key Pair - Username:
SVC_DBT - Private key: contents of
svc_dbt_rsa_key.pem
Delete the local key files:
rm svc_dbt_rsa_key.pem svc_dbt_rsa_key.pub
Store Credentials in AWS Secrets Manager
dbt CI/CD retrieves credentials from AWS Secrets Manager at runtime. Add the dbt credentials to Terraform:
# terraform/aws/secrets.tf — add alongside existing secrets
resource "aws_secretsmanager_secret" "dbt_snowflake_credentials" {
name = "dbt/snowflake-credentials"
description = "Snowflake credentials for SVC_DBT (dbt transformation service account)"
tags = {
Project = "data-platform"
ManagedBy = "terraform"
ServiceName = "dbt"
}
}
Secret value set manually
The secret resource is created by Terraform but the value is set manually (following the same pattern as other service account credentials). This avoids storing the private key in Terraform state.
After terraform apply, set the secret value in the AWS console or CLI:
aws secretsmanager put-secret-value \
--secret-id "dbt/snowflake-credentials" \
--secret-string '{
"account": "your-account.snowflakecomputing.com",
"user": "SVC_DBT",
"private_key": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
"role": "SVC_DBT",
"warehouse": "TRANSFORMING",
"database": "ANALYTICS",
"schema": "ANALYTICS"
}' \
--profile infrastructure-admin
Retrieve the private key from 1Password when running this command, then clear your terminal history.
Update profiles.yml for Local Development
With the service account created, update the prod target in your local profiles.yml (not committed):
# ~/.dbt/profiles.yml
dbt_transform:
target: dev
outputs:
dev:
type: snowflake
account: "{{ env_var('SNOWFLAKE_ACCOUNT') }}"
user: YOUR_PERSONAL_USERNAME
authenticator: externalbrowser
role: ANALYTICS_DEVELOPER
warehouse: DEVELOPER
database: ANALYTICS_DEV
schema: ANALYTICS_DEV
threads: 4
prod:
type: snowflake
account: "{{ env_var('SNOWFLAKE_ACCOUNT') }}"
user: SVC_DBT
private_key_path: "{{ env_var('SNOWFLAKE_PRIVATE_KEY_PATH') }}"
private_key_passphrase: "{{ env_var('SNOWFLAKE_PRIVATE_KEY_PASSPHRASE', '') }}"
role: SVC_DBT
warehouse: TRANSFORMING
database: ANALYTICS
schema: ANALYTICS
threads: 8
Test the production profile locally (optional — only needed for debugging):
export SNOWFLAKE_ACCOUNT="your-account.snowflakecomputing.com"
export SNOWFLAKE_PRIVATE_KEY_PATH="/path/to/svc_dbt_rsa_key.pem"
dbt debug --target prod
Verify Access
Confirm SVC_DBT has the correct permissions:
-- Connect as SVC_DBT
USE ROLE SVC_DBT;
-- Can use the TRANSFORMING warehouse
USE WAREHOUSE TRANSFORMING;
-- Can create schemas in ANALYTICS (needed for dbt to create STAGING etc.)
USE DATABASE ANALYTICS;
CREATE SCHEMA IF NOT EXISTS STAGING; -- should succeed
DROP SCHEMA STAGING;
-- Can write to the REPORTING schema (Terraform-managed)
USE SCHEMA REPORTING;
CREATE TABLE IF NOT EXISTS test_table (id int); -- should succeed
DROP TABLE test_table;
-- Can read from raw source databases
USE DATABASE DLT;
SHOW SCHEMAS; -- should show OPEN_EXCHANGE_RATES
USE DATABASE AIRBYTE;
SHOW SCHEMAS; -- should show HUBSPOT
Summary
You've set up the Snowflake infrastructure for dbt:
- Created
SVC_DBTservice account withuser_create_dedicated_role = true - Granted
ANALYTICS_TRANSFORMERtoSVC_DBTfor write access to ANALYTICS - Configured key-pair authentication
- Stored credentials in AWS Secrets Manager
- Confirmed existing database-level grants cover dbt-created schemas
What's Next
Define your raw data sources and build the first staging models.
Continue to Sources and Staging →