Skip to content

Getting Started

Requirements: Python 3.10 or higher

Install from PyPI

# MacOS / Linux
python -m venv .venv
source .venv/bin/activate
pip install snowcap

# Windows
python -m venv .venv
.\.venv\Scripts\activate
pip install snowcap

Quick Start: Create a Warehouse

The simplest way to get started—define a single resource and deploy it:

# snowcap.yml
warehouses:
  - name: analytics
    warehouse_size: xsmall
    auto_suspend: 60

Create a .env file for credentials (add .env to .gitignore!):

# .env
SNOWFLAKE_ACCOUNT=my-account
SNOWFLAKE_USER=my-user
SNOWFLAKE_PASSWORD=my-password
SNOWFLAKE_ROLE=SYSADMIN
All Environment Variables
Variable Description
SNOWFLAKE_ACCOUNT Your Snowflake account identifier
SNOWFLAKE_USER Username
SNOWFLAKE_PASSWORD Password (for password auth)
SNOWFLAKE_ROLE Role to use
SNOWFLAKE_WAREHOUSE Warehouse to use (optional)
SNOWFLAKE_DATABASE Default database (optional)
SNOWFLAKE_SCHEMA Default schema (optional)
SNOWFLAKE_AUTHENTICATOR Authentication method (see below)
SNOWFLAKE_MFA_PASSCODE TOTP passcode from authenticator app
SNOWFLAKE_PRIVATE_KEY_PATH Path to private key file (for key-pair auth)
PRIVATE_KEY_PASSPHRASE Passphrase for encrypted private key

Authenticator options:

Value Description
(default) Username and password
SNOWFLAKE_JWT Key-pair authentication
externalbrowser SSO via web browser
oauth OAuth with access token
username_password_mfa Password with MFA (push notification)
Key-Pair Authentication

For key-pair auth, use SNOWFLAKE_JWT instead of password:

# .env
SNOWFLAKE_ACCOUNT=my-account
SNOWFLAKE_USER=my-user
SNOWFLAKE_ROLE=SECURITYADMIN
SNOWFLAKE_PRIVATE_KEY_PATH=/path/to/private-key.pem
SNOWFLAKE_AUTHENTICATOR=SNOWFLAKE_JWT

If your private key is encrypted, also set:

PRIVATE_KEY_PASSPHRASE=your-passphrase

Run snowcap:

# Load environment variables
export $(cat .env | xargs)

# Preview changes
snowcap plan --config snowcap.yml

# Apply changes
snowcap apply --config snowcap.yml

Wrapper Scripts

For production use, consider a wrapper script that validates environment variables before running snowcap. See CLI Wrapper Scripts for an example.

That's it. Snowcap compares your config to Snowflake and generates the SQL to make them match.

Scaling Up: Directory Structure with Templates

As your infrastructure grows, organize configs into directories and use templates for scalability:

snowcap/
├── resources/
│   ├── databases.yml            # Database definitions
│   ├── schemas.yml              # Schema definitions
│   ├── warehouses.yml           # Warehouse definitions
│   ├── stages.yml               # Stage definitions
│   ├── users.yml                # User definitions
│   ├── roles__base.yml          # Atomic privilege roles
│   └── roles__functional.yml    # Functional roles + grants
└── object_templates/            # Auto-generate resources with for_each
    ├── database.yml
    ├── schema.yml
    └── warehouses.yml

databases.yml - Define your databases:

vars:
  - name: databases
    type: list
    default:
      - name: raw
        owner: loader
      - name: analytics
        owner: transformer
      - name: analytics_dev
        owner: transformer

object_templates/database.yml - Auto-generate databases, roles, and grants:

# Databases
databases:
  - for_each: var.databases
    name: "{{ each.value.name }}"
    owner: "{{ each.value.owner }}"

# Database roles
roles:
  - for_each: var.databases
    name: "z_db__{{ each.value.name }}"

# Database grants
grants:
  - for_each: var.databases
    priv: USAGE
    on: "database {{ each.value.name }}"
    to: "z_db__{{ each.value.name }}"

roles__functional.yml - Compose into functional roles:

roles:
  - name: analyst
  - name: loader
  - name: transformer

role_grants:
  - to_role: analyst
    roles:
      - z_db__analytics
      - z_schema__marts
      - z_wh__querying
      - z_tables_views__select

  - to_role: transformer
    roles:
      - z_db__raw
      - z_db__analytics
      - z_wh__transforming

Run snowcap:

# Load environment variables from .env
export $(cat .env | xargs)

# Preview all changes
snowcap plan --config ./snowcap/

# Apply all changes
snowcap apply --config ./snowcap/

Adding a new database? Just add one line to databases.yml—the template auto-creates the database, role, and grant.

CLI Commands

snowcap --help

# Commands:
#   apply    Apply a resource config to a Snowflake account
#   connect  Test the connection to Snowflake
#   export   Generate a resource config for existing Snowflake resources
#   plan     Compare a resource config to the current state of Snowflake

Optimizing Grant Fetching with ACCOUNT_USAGE

For large manifests with many roles, Snowcap can use Snowflake's ACCOUNT_USAGE views to fetch all grant information in a single bulk query instead of running individual SHOW GRANTS commands per role.

When to Enable ACCOUNT_USAGE

This optimization is disabled by default and is most beneficial when:

  • Your manifest manages 50+ roles with grants
  • You're seeing many SHOW GRANTS TO ROLE queries in the logs
  • The bulk query time (typically 30-60 seconds) is less than the cumulative time of individual queries

For smaller manifests, the default behavior (per-role SHOW GRANTS) is typically faster.

Enabling ACCOUNT_USAGE

Step 1: Grant access to ACCOUNT_USAGE views:

GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE TO ROLE <your_role>;

Replace <your_role> with the role you use for Snowcap (e.g., SYSADMIN or a custom deployment role).

Step 2: Enable the option:

CLI flag:

snowcap plan --config snowcap.yml --use-account-usage
snowcap apply --config snowcap.yml --use-account-usage

YAML config:

# snowcap.yml
use_account_usage: true

Python API:

bp = Blueprint(
    resources=[...],
    use_account_usage=True,
)

About ACCOUNT_USAGE Latency

ACCOUNT_USAGE views have up to 2 hours of latency—data may not reflect very recent changes. This is acceptable for grants because:

  • GRANT statements are idempotent: Re-granting an existing privilege succeeds without error
  • REVOKE has IF EXISTS semantics: Revoking a non-existent grant won't fail
  • Worst case: The plan shows a grant change that's already applied, and re-applies it harmlessly

If IMPORTED PRIVILEGES is not granted, Snowcap falls back automatically to SHOW GRANTS with a warning.

Next Steps