This document provides security guidance for configuring schemachange, with a focus on protecting sensitive credentials and following industry best practices.
β DON'T DO THIS:
# BAD - Never store passwords in YAML!
config-version: 2
snowflake:
account: myaccount.us-east-1
user: my_user
password: "my_secret_password" # β INSECURE!Why? YAML configuration files are often:
- Committed to version control (Git, SVN, etc.)
- Shared across teams
- Backed up to multiple locations
- Visible in CI/CD logs
β DON'T DO THIS:
# BAD - CLI arguments are visible in process list and shell history!
schemachange deploy --snowflake-password "my_secret_password" # β INSECURE!Why?
- Visible in
psoutput to all users - Stored in shell history (
.bash_history,.zsh_history) - Logged by monitoring tools
- Visible to system administrators
Note: schemachange intentionally blocks --snowflake-private-key-passphrase via CLI for this reason.
β DO THIS:
# Set restrictive permissions on connections.toml
chmod 600 ~/.snowflake/connections.toml
# Verify permissions (should show -rw-------)
ls -l ~/.snowflake/connections.tomlWhat schemachange checks:
- Warns if file is readable by group or others
- Warns if file is writable by group or others
- Provides actionable remediation commands
- Service users: Password authentication is NOT SUPPORTED. Must use PAT, Key Pair (JWT), OAuth, or WIF.
- Human users (CLI/CI/CD): PREFERRED to use PAT, Key Pair (JWT), or OAuth. Password+MFA is allowed but not recommended for automation.
- Human users (Interactive): Password+MFA is acceptable for interactive sessions but use PAT/Key Pair for automation.
-
β BEST: JWT/Private Key Authentication (Service Accounts & Automation)
- REQUIRED for service accounts (password not supported)
- PREFERRED for all automation (human or service accounts)
- Most secure - no password exposure
- Key-based authentication
export SNOWFLAKE_ACCOUNT="myaccount.us-east-1" export SNOWFLAKE_USER="service_account" export SNOWFLAKE_AUTHENTICATOR="snowflake_jwt" export SNOWFLAKE_PRIVATE_KEY_FILE="~/.ssh/snowflake_key.p8" export SNOWFLAKE_PRIVATE_KEY_FILE_PWD="key_passphrase" # Only if key is encrypted export SNOWFLAKE_ROLE="DEPLOYMENT_ROLE" export SNOWFLAKE_WAREHOUSE="DEPLOYMENT_WH" schemachange deploy
-
β PREFERRED for Automation: Programmatic Access Tokens (PATs)
- PREFERRED for human users in CLI/CI/CD scenarios
- Supported for service users (alternative to JWT)
- Token rotation support
- Bypasses MFA prompts for automation
export SNOWFLAKE_ACCOUNT="myaccount.us-east-1" export SNOWFLAKE_USER="human_user" export SNOWFLAKE_PASSWORD="<your_pat_token>" # PAT token, NOT your login password export SNOWFLAKE_ROLE="DEPLOYMENT_ROLE" export SNOWFLAKE_WAREHOUSE="DEPLOYMENT_WH" schemachange deploy
-
β GOOD: connections.toml (With Proper Permissions)
- Centralized credential management
- Multiple profile support
- Must have restrictive file permissions (0600)
- Use PAT tokens for human users, JWT for service accounts
# ~/.snowflake/connections.toml (chmod 600) [production] account = "myaccount.us-east-1" user = "service_account" authenticator = "snowflake_jwt" private_key_file = "~/.ssh/snowflake_key.p8" # private_key_file_pwd = "passphrase" # Only if key is encrypted role = "DEPLOYMENT_ROLE" warehouse = "DEPLOYMENT_WH"
-
β ACCEPTABLE: OAuth with Token File
- For SSO integration
- Token file should have restrictive permissions
export SNOWFLAKE_AUTHENTICATOR="oauth" export SNOWFLAKE_TOKEN_FILE_PATH="~/.snowflake/oauth_token.txt" chmod 600 ~/.snowflake/oauth_token.txt schemachange deploy
-
β οΈ NOT RECOMMENDED for Automation: Password + MFA- NOT SUPPORTED for service accounts
- Allowed for human users in interactive sessions
- NOT RECOMMENDED for CLI/CI/CD automation (requires manual MFA input)
- Use PAT or Key Pair instead for automation
# β οΈ Works for human users but requires MFA prompts (not suitable for automation) export SNOWFLAKE_PASSWORD="my_login_password" schemachange deploy # Will prompt for MFA (blocks automation)
-
β DEPRECATED: Password-Only (No MFA)
- NOT SUPPORTED - Snowflake requires MFA for password authentication
- Use PAT, Key Pair, or OAuth instead
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Which configuration source should I use? β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββ
β Is it a β
β SECRET? β
β (password, β
β token, etc.) β
βββββββββ¬ββββββββ
β
βββββββββββββ΄ββββββββββββ
β β
YESβ βNO
β β
βΌ βΌ
βββββββββββββββββββββββββ βββββββββββββββββββββββ
β Use ENVIRONMENT β β What's the use β
β VARIABLE or β β case? β
β connections.toml β ββββββββββββ¬βββββββββββ
β (with chmod 600) β β
β β β
β β
SNOWFLAKE_PASSWORD β βββββββββββ΄ββββββββββ
β β
connections.toml β β β
β β Same for Different per
β β NEVER CLI β all environments environment
β β NEVER YAML β β β
βββββββββββββββββββββββββ βΌ βΌ
ββββββββββββββββ ββββββββββββββββ
β Use YAML β β Use CLI args β
β Config File β β or ENV vars β
β β β β
β Examples: β β Examples: β
β β’ root-folderβ β CLI: β
β β’ log-level β β -d DATABASE β
β β’ vars β β β
β β β ENV: β
β Priority: β β SNOWFLAKE_ β
β CLI > ENV > β β DATABASE β
β YAML β β β
ββββββββββββββββ ββββββββββββββββ
Legend:
β
= Recommended
β οΈ = Use with caution
β = Never use
Schemachange uses a layered configuration approach:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. CLI Arguments (Highest Priority) β
β --snowflake-account myaccount β
β Wins in conflicts β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β overrides
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. Environment Variables β
β SNOWFLAKE_ACCOUNT=myaccount β
β β
Best for secrets β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β overrides
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. YAML Configuration File β
β snowflake.account: myaccount β
β β
Best for non-secret settings β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β overrides
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 4. connections.toml (Lowest Priority) β
β account = "myaccount" β
β β
Good for secrets with proper permissions β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Recommended Approach - Human User with MFA:
# Use connections.toml with PAT token
cat > ~/.snowflake/connections.toml << EOF
[dev]
account = "dev-account.us-east-1"
user = "dev_user"
password = "<your_pat_token>" # PAT token, NOT your login password
role = "DEVELOPER"
warehouse = "DEV_WH"
EOF
chmod 600 ~/.snowflake/connections.toml
# Deploy using connection profile
schemachange deploy -C devWhy?
- β Convenient for local development
- β Credentials don't leak to version control
- β No MFA prompts during deployment (unlike password+MFA)
How to get a PAT:
- Log into Snowflake UI
- Go to user preferences
- Generate new Programmatic Access Token
- Copy and use in place of password
Alternative - Password+MFA (Not Recommended for Automation):
# β οΈ Acceptable for interactive sessions but will prompt for MFA
export SNOWFLAKE_PASSWORD="my_login_password"
schemachange deploy -C dev # Will prompt for MFA code each timeβ BEST: JWT with Service Account (Recommended):
# .github/workflows/deploy.yml
- name: Deploy with Schemachange
env:
SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_SERVICE_ACCOUNT }} # Service account
SNOWFLAKE_AUTHENTICATOR: "snowflake_jwt"
SNOWFLAKE_PRIVATE_KEY_FILE: ${{ secrets.SNOWFLAKE_PRIVATE_KEY_FILE }}
SNOWFLAKE_PRIVATE_KEY_FILE_PWD: ${{ secrets.SNOWFLAKE_KEY_PASSPHRASE }}
SNOWFLAKE_ROLE: DEPLOYMENT_ROLE
SNOWFLAKE_WAREHOUSE: DEPLOYMENT_WH
SNOWFLAKE_DATABASE: ${{ matrix.database }}
run: |
# Write private key to temp file
echo "${{ secrets.SNOWFLAKE_PRIVATE_KEY }}" > /tmp/snowflake_key.p8
chmod 600 /tmp/snowflake_key.p8
schemachange deploy --config-folder ./migrations
# Clean up
rm -f /tmp/snowflake_key.p8Alternative - PAT with Human Account (Less Preferred):
- name: Deploy with Schemachange
env:
SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }}
SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PAT }} # PAT token
SNOWFLAKE_ROLE: DEPLOYMENT_ROLE
SNOWFLAKE_WAREHOUSE: DEPLOYMENT_WH
run: |
schemachange deploy --config-folder ./migrationsWhy JWT or PAT for automation:
- β Service accounts cannot use passwords - JWT or PAT required
- β Human accounts (automation) - JWT or PAT preferred (no MFA prompts)
- β More secure - no password exposure, better audit trail
- β Key rotation without Snowflake user changes (JWT) or token rotation (PAT)
β Recommended Approach - JWT with Service Account:
# 1. Generate key pair (one-time setup)
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out snowflake_key.p8 -nocrypt
# 2. Configure Snowflake user with public key
# (Upload public key to Snowflake user)
# 3. Store private key securely
chmod 600 snowflake_key.p8
# 4. Deploy
export SNOWFLAKE_ACCOUNT="prod-account.us-east-1"
export SNOWFLAKE_USER="deployment_service_account"
export SNOWFLAKE_AUTHENTICATOR="snowflake_jwt"
export SNOWFLAKE_PRIVATE_KEY_FILE="./snowflake_key.p8"
schemachange deployWhy?
- β No password needed
- β Key rotation without Snowflake user changes
- β Better audit trail
β Recommended Approach - YAML + ENV Override:
Base Configuration (YAML - Checked into Git):
# config/base-config.yml
config-version: 2
schemachange:
root-folder: ./migrations
log-level: INFO
create-change-history-table: true
snowflake:
# Non-sensitive defaults
role: DEPLOYMENT_ROLE
warehouse: DEPLOYMENT_WH
# DO NOT include:
# - account (varies by environment)
# - user (varies by environment)
# - password (NEVER in YAML!)Environment-Specific Configuration (Environment Variables):
Option 1 - JWT with Service Account (Recommended):
# Production
export SNOWFLAKE_ACCOUNT="prod-account.us-east-1"
export SNOWFLAKE_USER="prod_service_account"
export SNOWFLAKE_AUTHENTICATOR="snowflake_jwt"
export SNOWFLAKE_PRIVATE_KEY_FILE="~/.ssh/snowflake_prod.p8"
export SNOWFLAKE_DATABASE="PRODUCTION_DB"
schemachange deploy --config-folder ./config
# Staging
export SNOWFLAKE_ACCOUNT="staging-account.us-east-1"
export SNOWFLAKE_USER="staging_service_account"
export SNOWFLAKE_AUTHENTICATOR="snowflake_jwt"
export SNOWFLAKE_PRIVATE_KEY_FILE="~/.ssh/snowflake_staging.p8"
export SNOWFLAKE_DATABASE="STAGING_DB"
schemachange deploy --config-folder ./configOption 2 - PAT with Human Account:
# Production
export SNOWFLAKE_ACCOUNT="prod-account.us-east-1"
export SNOWFLAKE_USER="prod_deployment"
export SNOWFLAKE_PASSWORD="<prod_pat_token>" # PAT, not password
export SNOWFLAKE_DATABASE="PRODUCTION_DB"
schemachange deploy --config-folder ./config- No passwords/secrets in YAML files - Check with
grep -r password *.yml - No passwords/secrets in version control - Use
.gitignorefor sensitive files - connections.toml has 600 permissions -
ls -l ~/.snowflake/connections.toml - Private keys have 600 permissions -
ls -l ~/.ssh/snowflake_key.p8 - Using JWT or PAT for service accounts - Password auth is not supported
- Using JWT or PAT for automation - Preferred over password+MFA (no interactive prompts)
- Test with
schemachange verify- Before running deploy
- Secrets in secret manager - Not in pipeline YAML
- Using JWT or PAT - Preferred (passwords not supported for service accounts, not recommended for human users)
- Minimal permissions - Role has only required privileges
- Service account preferred - Or use PAT/JWT with human account (avoid password+MFA for automation)
- Audit logging enabled - Track all deployments
- Separate environments - Dev/Staging/Prod isolation
- Rotate credentials quarterly - PATs, passwords, keys
- Review access logs - Check for unauthorized access
- Update dependencies - Keep schemachange updated
- Audit connections.toml - Remove unused profiles
- Immediately rotate the credentials in Snowflake
- Remove from Git history using
git filter-branchor BFG Repo-Cleaner - Force push after cleaning (coordinate with team)
- Notify security team if in production
- Review access logs for unauthorized usage
# Remove sensitive file from Git history
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch config-with-secrets.yml" \
--prune-empty --tag-name-filter cat -- --all
# Force push (after team coordination!)
git push origin --force --all- Rotate credentials immediately
- Clear shell history:
history -c && history -w - Clear application logs containing the credentials
- Review who had access to the system
- Implement prevention measures (use environment variables)
# Test your configuration and connectivity
schemachange verify
# What it shows:
# β Configuration sources used
# β Masked sensitive parameters (password, tokens)
# β Connection test results
# β Session details after successful connection================================================================================
Schemachange Configuration Verification
================================================================================
Snowflake Connection Configuration:
Account: myaccount.us-east-1
User: deployment_user
Role: DEPLOYMENT_ROLE
Warehouse: DEPLOYMENT_WH
Password: ****** (set)
Testing Snowflake Connectivity...
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Connection Successful!
Connection Details:
Session ID: 123456789
Snowflake Version: 8.25.0
- Snowflake Key Pair Authentication
- Snowflake OAuth
- Programmatic Access Tokens (PATs)
- connections.toml Documentation
| Use Case | Recommended Method | Why |
|---|---|---|
| Service Accounts | JWT (private key) or PAT | β REQUIRED - passwords not supported |
| CI/CD Automation | JWT (private key) or PAT | β PREFERRED - no interactive MFA prompts |
| Human Users (Automation) | PAT or JWT | β PREFERRED - bypasses MFA prompts |
| Human Users (Interactive) | Password+MFA or PAT | |
| Local Development | PAT via connections.toml | β Convenient + no MFA prompts |
| Legacy (Unsupported) | Password-only (no MFA) | β BLOCKED by Snowflake |
| Credential Type | CLI | ENV | YAML | connections.toml | Recommended |
|---|---|---|---|---|---|
| PAT Token | β | β | β | β (chmod 600) | ENV or connections.toml |
Private Key File (private_key_file) |
β | β | β | β (chmod 600) | ENV or connections.toml |
Private Key Password (private_key_file_pwd) |
β | β | β | β (chmod 600) | ENV or connections.toml |
| OAuth Token | β | β | β | Use token-file-path | Token file |
| Account | β | β | β | β | YAML or ENV |
| User | β | β | β | β | YAML or ENV |
| Role | β | β | β | β | YAML |
| Warehouse | β | β | β | β | YAML |
| Database | β | β | β | β | ENV or CLI |
Remember: Security is not a feature, it's a requirement! π