Chapter 4 · Foundations

Setting Up Snowflake

Lab:Snowflake · AWS us-west-2+Snowsight+snowflake/00_setup_account.sqlRun once, as a user with ACCOUNTADMIN. Everything after this runs as the service user.

Everything the platform needs in Snowflake is created by one script that you run once in a Snowsight worksheet. It separates compute from storage, gives each job its own role, creates a service user that logs in with a key instead of a password, and caps spending. Your own administrator login is used only for this one run.

 Snowsight · Worksheet
Snowsight worksheet with the setup script after Run All; the last query returns the account identifier (blurred) and the region AWS_US_WEST_2, and the query history shows every statement succeeded.
The setup script after Run All. The last query returns the account identifier (blurred here) and the region.

Compute: one warehouse per job

In Snowflake, compute (a virtual warehouse) is separate from storage, and you pay per second while a warehouse runs. Three extra-small warehouses keep the jobs apart, so a heavy report can never slow the nightly load, and each one suspends itself after 60 idle seconds:

USE ROLE SYSADMIN;
CREATE WAREHOUSE IF NOT EXISTS WH_LOAD      WAREHOUSE_SIZE = XSMALL AUTO_SUSPEND = 60 AUTO_RESUME = TRUE INITIALLY_SUSPENDED = TRUE;
CREATE WAREHOUSE IF NOT EXISTS WH_TRANSFORM WAREHOUSE_SIZE = XSMALL AUTO_SUSPEND = 60 AUTO_RESUME = TRUE INITIALLY_SUSPENDED = TRUE;
CREATE WAREHOUSE IF NOT EXISTS WH_REPORTING WAREHOUSE_SIZE = XSMALL AUTO_SUSPEND = 60 AUTO_RESUME = TRUE INITIALLY_SUSPENDED = TRUE;
 Snowsight · Admin → Warehouses
Snowsight warehouses page listing WH_LOAD, WH_TRANSFORM and WH_REPORTING, each X-Small.
The three X-Small warehouses.

Storage: raw data apart from modelled data

CREATE DATABASE IF NOT EXISTS WWI_RAW;            CREATE SCHEMA IF NOT EXISTS WWI_RAW.BRONZE;
CREATE DATABASE IF NOT EXISTS WWI_ANALYTICS;      CREATE SCHEMA IF NOT EXISTS WWI_ANALYTICS.SILVER;
CREATE SCHEMA IF NOT EXISTS WWI_ANALYTICS.GOLD;   CREATE SCHEMA IF NOT EXISTS WWI_ANALYTICS.MART_SALES;

-- gzipped, pipe-delimited CSV with every value quoted and \N for NULL
CREATE FILE FORMAT IF NOT EXISTS WWI_RAW.BRONZE.CSV_GZIP
  TYPE = CSV COMPRESSION = GZIP FIELD_DELIMITER = '|' SKIP_HEADER = 1
  FIELD_OPTIONALLY_ENCLOSED_BY = '"' NULL_IF = ('\\N') EMPTY_FIELD_AS_NULL = FALSE;
CREATE STAGE IF NOT EXISTS WWI_RAW.BRONZE.LANDING FILE_FORMAT = WWI_RAW.BRONZE.CSV_GZIP;

Keeping raw data in its own database means the people and tools that model data can read Bronze but never change it. The internal stage is Snowflake-managed storage for the files on their way in, so there is no S3 bucket or IAM role to set up.

 Snowsight · Data → Databases
Snowsight database explorer showing WWI_RAW with the BRONZE schema and WWI_ANALYTICS with SILVER, GOLD and MART_SALES.
The two databases and their schemas.

Access: three roles, each with one job

SVC_AIRFLOW LOADER TRANSFORMER write stage · create + loadWWI_RAW.BRONZE read Bronze · ownSILVER · GOLD · MART_SALES REPORTER READS GOLD + MART
Figure 4.1 — Least privilege: the service user can load and transform, but only REPORTER is for reading reports.
USE ROLE USERADMIN;
CREATE ROLE IF NOT EXISTS LOADER;  CREATE ROLE IF NOT EXISTS TRANSFORMER;  CREATE ROLE IF NOT EXISTS REPORTER;
USE ROLE SECURITYADMIN;
GRANT USAGE, CREATE TABLE ON SCHEMA WWI_RAW.BRONZE TO ROLE LOADER;
GRANT READ, WRITE ON STAGE WWI_RAW.BRONZE.LANDING  TO ROLE LOADER;
GRANT SELECT ON FUTURE TABLES IN SCHEMA WWI_RAW.BRONZE TO ROLE TRANSFORMER;
GRANT OWNERSHIP ON SCHEMA WWI_ANALYTICS.GOLD TO ROLE TRANSFORMER COPY CURRENT GRANTS;
GRANT SELECT ON FUTURE TABLES IN SCHEMA WWI_ANALYTICS.MART_SALES TO ROLE REPORTER;

Note the future grants: tables that don't exist yet, because the extract or dbt will create them later, are readable by the right role the moment they appear.

The service user: no password at all

CREATE USER IF NOT EXISTS SVC_AIRFLOW TYPE = SERVICE DEFAULT_ROLE = TRANSFORMER DEFAULT_WAREHOUSE = WH_TRANSFORM;
ALTER USER SVC_AIRFLOW SET RSA_PUBLIC_KEY = '<your rsa_key.pub body, without the BEGIN/END lines>';   -- from Chapter 2
GRANT ROLE LOADER TO USER SVC_AIRFLOW;
GRANT ROLE TRANSFORMER TO USER SVC_AIRFLOW;

TYPE = SERVICE tells Snowflake this user is a program: it can't have a password or MFA and can't use the web interface. It can only log in with its key. Check that the key is the right one with DESC USER SVC_AIRFLOW: its RSA_PUBLIC_KEY_FP must equal the fingerprint you compute on the VM.

 Snowsight · Admin → Users & Roles → SVC_AIRFLOW
Snowsight user page for SVC_AIRFLOW: user type Service, default role TRANSFORMER, default warehouse WH_TRANSFORM, MFA No, and one active key pair.
The service user: type Service, default role TRANSFORMER, one active key pair.

A cap on spending

USE ROLE ACCOUNTADMIN;
CREATE RESOURCE MONITOR IF NOT EXISTS WWI_MONITOR WITH CREDIT_QUOTA = 20 FREQUENCY = MONTHLY
  START_TIMESTAMP = IMMEDIATELY TRIGGERS ON 80 PERCENT DO NOTIFY ON 100 PERCENT DO SUSPEND;
ALTER WAREHOUSE WH_LOAD SET RESOURCE_MONITOR = WWI_MONITOR;      -- and the other two

Test the login from the VM

cd /opt/airflow/wwi_snowflake/dbt && dbt debug --profiles-dir . --target prod
#   user: SVC_AIRFLOW · role: TRANSFORMER · warehouse: WH_TRANSFORM
#   Connection test: [OK connection ok]

Key takeaways

  • One script creates everything; it is safe to re-run.
  • Separate warehouses per job, all X-Small and auto-suspending.
  • Three roles with future grants; the service user has a key pair and no password.
  • A resource monitor stops runaway spending.