karankrishnani/snowflake-healthtech-warehouse

HIPAA-ready Snowflake data warehouse for healthcare analytics. Medallion architecture (Bronze/Silver/Gold) with Terraform IaC, dbt transformations, dynamic data masking, role-based access control, and Evidence dashboards. Uses Synthea synthetic patient data in FHIR format.

0

stars

27

commits

Python

primary language

Feb 10, 2026

updated

karankrishnani.com/blog/building-hipaa-ready-snowflake-data-warehouse

README

Snowflake Healthcare Data Warehouse

Terraform dbt Snowflake Evidence

A production-ready Snowflake data warehouse architecture for healthcare analytics, demonstrating enterprise patterns including RBAC, dynamic data masking, and HIPAA-compliant data handling.

Built entirely with infrastructure-as-code (Terraform), version-controlled transformations (dbt), and code-based BI (Evidence).

🎯 Overview

This project demonstrates:

  • Infrastructure as Code: All Snowflake resources provisioned via Terraform
  • Data Modeling Best Practices: dbt with staging β†’ intermediate β†’ marts pattern
  • Enterprise Security: Role-based access control (RBAC) and dynamic data masking
  • HIPAA Compliance Patterns: PII protection with role-specific data views
  • Code-Based BI: Evidence dashboards with version-controlled SQL

Target Audience

Healthcare technology companies needing scalable, secure analytics infrastructure.

Demo Purpose

Showcase Snowflake architecture skills for fractional consulting opportunities.

πŸ“Š Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        SNOWFLAKE DATA WAREHOUSE                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚   RAW_DB     β”‚    β”‚  STAGING_DB  β”‚    β”‚   MARTS_DB   β”‚    β”‚ANALYTICS_DBβ”‚ β”‚
β”‚  β”‚              β”‚    β”‚              β”‚    β”‚              β”‚    β”‚            β”‚ β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β”‚            β”‚ β”‚
β”‚  β”‚  β”‚SYNTHEA β”‚  │───▢│  β”‚HEALTH  β”‚  │───▢│  β”‚ CORE   β”‚  β”‚    β”‚  Secure    β”‚ β”‚
β”‚  β”‚  β”‚ Schema β”‚  β”‚    β”‚  β”‚ CARE   β”‚  β”‚    β”‚  β”‚ Schema β”‚  β”‚    β”‚  Shares    β”‚ β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚            β”‚ β”‚
β”‚  β”‚              β”‚    β”‚              β”‚    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β”‚            β”‚ β”‚
β”‚  β”‚  Raw CSVs    β”‚    β”‚  Cleaned &   β”‚    β”‚  β”‚ANALYT  β”‚  β”‚    β”‚            β”‚ β”‚
β”‚  β”‚  from        β”‚    β”‚  Standardizedβ”‚    β”‚  β”‚ ICS    β”‚  β”‚    β”‚            β”‚ β”‚
β”‚  β”‚  Synthea     β”‚    β”‚  Data        β”‚    β”‚  β”‚ Schema β”‚  β”‚    β”‚            β”‚ β”‚
β”‚  β”‚              β”‚    β”‚              β”‚    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚            β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚         β”‚                   β”‚                   β”‚                           β”‚
β”‚         β–Ό                   β–Ό                   β–Ό                           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚                        ROLE HIERARCHY                                    β”‚β”‚
β”‚  β”‚                                                                          β”‚β”‚
β”‚  β”‚                        ACCOUNTADMIN                                      β”‚β”‚
β”‚  β”‚                             β”‚                                            β”‚β”‚
β”‚  β”‚                         SYSADMIN                                         β”‚β”‚
β”‚  β”‚                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                      β”‚β”‚
β”‚  β”‚                    β–Ό        β–Ό        β–Ό            β–Ό                      β”‚β”‚
β”‚  β”‚               LOADER    TRANS-    ANALYST     READER                     β”‚β”‚
β”‚  β”‚               _ROLE    FORMER     _ROLE       _ROLE                      β”‚β”‚
β”‚  β”‚                        _ROLE                                             β”‚β”‚
β”‚  β”‚                                                                          β”‚β”‚
β”‚  β”‚   Write RAW    Read RAW      Read MARTS      Read via                    β”‚β”‚
β”‚  β”‚   Only         Write all     (masked)        Data Share                  β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚                                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚                      DYNAMIC DATA MASKING                                β”‚β”‚
β”‚  β”‚                                                                          β”‚β”‚
β”‚  β”‚   TRANSFORMER_ROLE sees:        ANALYST_ROLE sees:                       β”‚β”‚
β”‚  β”‚   SSN: 999-12-3456              SSN: ***-**-3456                         β”‚β”‚
β”‚  β”‚   Name: John Smith              Name: J*** S***                          β”‚β”‚
β”‚  β”‚   DOB: 1985-03-15               DOB: 1985-01-01 (year only)              β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ›  Technology Stack

ComponentTechnology
Cloud PlatformSnowflake (Trial Account)
Infrastructure as CodeTerraform with snowflake-labs/snowflake provider
AuthenticationRSA Key-Pair (no passwords)
Data Transformationdbt-core 1.7.x with dbt-snowflake
Synthetic DataSynthea (FHIR-compliant generator)
BI LayerEvidence (code-based dashboards)
CI/CDGitHub Actions

πŸ“‹ Prerequisites

Before you begin, ensure you have:

πŸš€ Quick Start

1. Clone the Repository

git clone https://github.com/yourusername/snowflake-healthtech-warehouse.git
cd snowflake-healthtech-warehouse

2. Generate RSA Key Pair for Snowflake Authentication

# Create directory for keys
mkdir -p ~/.snowflake

# Generate private key (PKCS8 format, no password)
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out ~/.snowflake/rsa_key.p8 -nocrypt

# Generate public key
openssl rsa -in ~/.snowflake/rsa_key.p8 -pubout -out ~/.snowflake/rsa_key.pub

# View public key (you'll need this for Snowflake)
cat ~/.snowflake/rsa_key.pub

3. Configure Snowflake User

Log into Snowflake and run:

-- Replace 'your_user' with your actual username
-- Copy the public key content (without the BEGIN/END lines)
ALTER USER your_user SET RSA_PUBLIC_KEY='MIIBIjANBgkq...';

4. Configure Environment

# Copy example environment file
cp .env.example .env

# Edit .env with your settings
nano .env  # or use your preferred editor

Required settings:

SNOWFLAKE_ACCOUNT=xy12345.us-east-1   # Your account identifier
SNOWFLAKE_USER=your_username           # Your username
SNOWFLAKE_PRIVATE_KEY_PATH=~/.snowflake/rsa_key.p8

5. Run Setup Script

./init.sh

This will:

  • Validate prerequisites
  • Load environment variables
  • Create Python virtual environment
  • Install dependencies
  • Initialize Terraform
  • Set up Evidence

6. Deploy Infrastructure

cd terraform
terraform plan     # Review changes
terraform apply    # Deploy resources

7. Generate and Load Data

# Generate synthetic patient data
./data/scripts/generate_synthea.sh

# Load data into Snowflake
python scripts/load_data.py

8. Run dbt Transformations

source venv/bin/activate
cd dbt
dbt deps    # Install packages
dbt run     # Run models
dbt test    # Validate data

9. Start Evidence Dashboards

cd evidence
npm run dev
# Open http://localhost:3000

10. Verify Security & Infrastructure (Optional)

Run the comprehensive verification scripts to ensure everything is working:

source venv/bin/activate

# Verify all infrastructure was provisioned correctly
python scripts/verify_infrastructure.py

# Test RBAC permissions and data masking
python scripts/test_security.py

# Or run all tests at once
python scripts/run_all_tests.py

The security test demonstrates:

  • RBAC: LOADER_ROLE can only access RAW_DB, ANALYST_ROLE can only access MARTS_DB
  • Masking: Same query returns different results based on role:
    • TRANSFORMER_ROLE sees: SSN 999-12-3456, Name John Smith, DOB 1985-03-15
    • ANALYST_ROLE sees: SSN ***-**-3456, Name J***, DOB 1985-01-01

πŸ“ Project Structure

snowflake-healthtech-warehouse/
β”œβ”€β”€ .env.example              # Environment template (COMMITTED)
β”œβ”€β”€ .env                      # Actual secrets (GITIGNORED)
β”œβ”€β”€ .gitignore                # Comprehensive ignore rules
β”œβ”€β”€ requirements.txt          # Python dependencies
β”œβ”€β”€ init.sh                   # Setup script
β”œβ”€β”€ README.md                 # This file
β”œβ”€β”€ AGENT_NOTES.md            # Notes for AI agents
β”‚
β”œβ”€β”€ terraform/                # Infrastructure as Code
β”‚   β”œβ”€β”€ main.tf               # Root module
β”‚   β”œβ”€β”€ variables.tf          # Input variables
β”‚   β”œβ”€β”€ outputs.tf            # Output values
β”‚   β”œβ”€β”€ providers.tf          # Snowflake provider
β”‚   β”œβ”€β”€ terraform.tfvars.example
β”‚   └── modules/
β”‚       β”œβ”€β”€ databases/        # RAW, STAGING, MARTS, ANALYTICS
β”‚       β”œβ”€β”€ warehouses/       # LOADING, TRANSFORM, ANALYTICS
β”‚       β”œβ”€β”€ roles/            # LOADER, TRANSFORMER, ANALYST, READER
β”‚       β”œβ”€β”€ schemas/          # SYNTHEA, HEALTHCARE, CORE, ANALYTICS
β”‚       β”œβ”€β”€ stages/           # Internal stages for loading
β”‚       └── security/         # Masking policies
β”‚
β”œβ”€β”€ dbt/                      # Data transformations
β”‚   β”œβ”€β”€ dbt_project.yml
β”‚   β”œβ”€β”€ profiles.yml.example
β”‚   β”œβ”€β”€ packages.yml
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   β”œβ”€β”€ sources.yml       # RAW_DB sources
β”‚   β”‚   β”œβ”€β”€ staging/          # 1:1 with source (views)
β”‚   β”‚   β”œβ”€β”€ intermediate/     # Business logic joins
β”‚   β”‚   └── marts/            # Dimensional models (tables)
β”‚   β”‚       β”œβ”€β”€ core/         # dim_patient, fact_encounters
β”‚   β”‚       └── analytics/    # Aggregated metrics
β”‚   β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ macros/
β”‚   └── seeds/
β”‚
β”œβ”€β”€ data/                     # Data generation
β”‚   β”œβ”€β”€ synthea/
β”‚   β”‚   └── csv/              # Generated patient data
β”‚   └── scripts/
β”‚       └── generate_synthea.sh
β”‚
β”œβ”€β”€ scripts/                  # Python utilities
β”‚   └── load_data.py          # CSV to Snowflake loader
β”‚
β”œβ”€β”€ evidence/                 # BI dashboards
β”‚   β”œβ”€β”€ package.json
β”‚   β”œβ”€β”€ evidence.config.yaml
β”‚   β”œβ”€β”€ sources/
β”‚   β”‚   └── snowflake.yaml.example
β”‚   └── pages/
β”‚       β”œβ”€β”€ index.md          # Executive dashboard
β”‚       β”œβ”€β”€ patients.md       # Demographics
β”‚       β”œβ”€β”€ encounters.md     # Visit analytics
β”‚       └── security-demo.md  # Masking demonstration
β”‚
β”œβ”€β”€ .github/workflows/        # CI/CD
β”‚   β”œβ”€β”€ terraform-plan.yml
β”‚   β”œβ”€β”€ terraform-apply.yml
β”‚   └── dbt-run.yml
β”‚
└── docs/                     # Documentation
    β”œβ”€β”€ architecture.md
    β”œβ”€β”€ security.md
    └── demo-script.md

πŸ” Security Features

Role-Based Access Control (RBAC)

RoleCan AccessCannot Access
LOADER_ROLEWrite to RAW_DBRead STAGING, MARTS
TRANSFORMER_ROLERead RAW, Write STAGING/MARTSN/A
ANALYST_ROLERead MARTS (masked)Read RAW, STAGING
READER_ROLERead via Data ShareDirect database access

Dynamic Data Masking

Same query, different results based on role:

SELECT patient_id, ssn, first_name, last_name, date_of_birth
FROM marts_db.core.dim_patient
LIMIT 3;

As TRANSFORMER_ROLE:

patient_idssnfirst_namelast_namedate_of_birth
1999-12-3456JohnSmith1985-03-15
2888-34-5678JaneDoe1990-07-22

As ANALYST_ROLE:

patient_idssnfirst_namelast_namedate_of_birth
1*--3456J***S****1985-01-01
2*--5678J***D**1990-01-01

πŸ“š Documentation

πŸ”„ CI/CD Setup

The project includes GitHub Actions workflows for automated deployment.

Required GitHub Secrets

Configure these secrets in your repository settings (Settings β†’ Secrets and variables β†’ Actions):

SecretDescriptionHow to Get
SNOWFLAKE_ACCOUNTSnowflake account identifierFrom your Snowflake URL (e.g., xy12345.us-east-1)
SNOWFLAKE_USERSnowflake usernameYour Snowflake login username
SNOWFLAKE_PRIVATE_KEYRSA private key (full content)Contents of ~/.snowflake/rsa_key.p8

Setting Up the Private Key Secret

# Copy the entire private key file content
cat ~/.snowflake/rsa_key.p8 | pbcopy  # macOS
# Or manually copy the contents including BEGIN/END lines

Then paste as the SNOWFLAKE_PRIVATE_KEY secret value in GitHub.

Workflow Overview

WorkflowTriggerPurpose
terraform-plan.ymlPull RequestPreview infrastructure changes
terraform-apply.ymlPush to mainApply infrastructure changes
dbt-run.ymlAfter terraform-applyRun data transformations

Environment Protection

For production safety, configure an "environment" in GitHub:

  1. Go to Settings β†’ Environments β†’ New environment
  2. Name it production
  3. Enable "Required reviewers" for apply workflows

🎬 Demo

A 5-7 minute video demonstration is available showing:

  1. Terraform infrastructure deployment
  2. Synthea data generation and loading
  3. dbt transformations and testing
  4. Dynamic data masking in action
  5. Evidence dashboards with live data

🀝 Contributing

This is a portfolio project, but suggestions and feedback are welcome!

πŸ“„ License

MIT License - See LICENSE for details.

πŸ‘€ Author

Built as a demonstration of Snowflake architecture patterns for healthcare analytics.


Note: This project uses synthetic data generated by Synthea. No real patient information is included.

Contributors

karankrishnani

27 commits

karankrishnani/snowflake-healthtech-warehouse

HIPAA-ready Snowflake data warehouse for healthcare analytics. Medallion architecture (Bronze/Silver/Gold) with Terraform IaC, dbt transformations, dynamic data masking, role-based access control, and Evidence dashboards. Uses Synthea synthetic patient data in FHIR format.

0

stars

27

commits

Python

primary language

Feb 10, 2026

updated

karankrishnani.com/blog/building-hipaa-ready-snowflake-data-warehouse

README

Snowflake Healthcare Data Warehouse

Terraform dbt Snowflake Evidence

A production-ready Snowflake data warehouse architecture for healthcare analytics, demonstrating enterprise patterns including RBAC, dynamic data masking, and HIPAA-compliant data handling.

Built entirely with infrastructure-as-code (Terraform), version-controlled transformations (dbt), and code-based BI (Evidence).

🎯 Overview

This project demonstrates:

  • Infrastructure as Code: All Snowflake resources provisioned via Terraform
  • Data Modeling Best Practices: dbt with staging β†’ intermediate β†’ marts pattern
  • Enterprise Security: Role-based access control (RBAC) and dynamic data masking
  • HIPAA Compliance Patterns: PII protection with role-specific data views
  • Code-Based BI: Evidence dashboards with version-controlled SQL

Target Audience

Healthcare technology companies needing scalable, secure analytics infrastructure.

Demo Purpose

Showcase Snowflake architecture skills for fractional consulting opportunities.

πŸ“Š Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        SNOWFLAKE DATA WAREHOUSE                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚   RAW_DB     β”‚    β”‚  STAGING_DB  β”‚    β”‚   MARTS_DB   β”‚    β”‚ANALYTICS_DBβ”‚ β”‚
β”‚  β”‚              β”‚    β”‚              β”‚    β”‚              β”‚    β”‚            β”‚ β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β”‚            β”‚ β”‚
β”‚  β”‚  β”‚SYNTHEA β”‚  │───▢│  β”‚HEALTH  β”‚  │───▢│  β”‚ CORE   β”‚  β”‚    β”‚  Secure    β”‚ β”‚
β”‚  β”‚  β”‚ Schema β”‚  β”‚    β”‚  β”‚ CARE   β”‚  β”‚    β”‚  β”‚ Schema β”‚  β”‚    β”‚  Shares    β”‚ β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚            β”‚ β”‚
β”‚  β”‚              β”‚    β”‚              β”‚    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β”‚            β”‚ β”‚
β”‚  β”‚  Raw CSVs    β”‚    β”‚  Cleaned &   β”‚    β”‚  β”‚ANALYT  β”‚  β”‚    β”‚            β”‚ β”‚
β”‚  β”‚  from        β”‚    β”‚  Standardizedβ”‚    β”‚  β”‚ ICS    β”‚  β”‚    β”‚            β”‚ β”‚
β”‚  β”‚  Synthea     β”‚    β”‚  Data        β”‚    β”‚  β”‚ Schema β”‚  β”‚    β”‚            β”‚ β”‚
β”‚  β”‚              β”‚    β”‚              β”‚    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚            β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚         β”‚                   β”‚                   β”‚                           β”‚
β”‚         β–Ό                   β–Ό                   β–Ό                           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚                        ROLE HIERARCHY                                    β”‚β”‚
β”‚  β”‚                                                                          β”‚β”‚
β”‚  β”‚                        ACCOUNTADMIN                                      β”‚β”‚
β”‚  β”‚                             β”‚                                            β”‚β”‚
β”‚  β”‚                         SYSADMIN                                         β”‚β”‚
β”‚  β”‚                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                      β”‚β”‚
β”‚  β”‚                    β–Ό        β–Ό        β–Ό            β–Ό                      β”‚β”‚
β”‚  β”‚               LOADER    TRANS-    ANALYST     READER                     β”‚β”‚
β”‚  β”‚               _ROLE    FORMER     _ROLE       _ROLE                      β”‚β”‚
β”‚  β”‚                        _ROLE                                             β”‚β”‚
β”‚  β”‚                                                                          β”‚β”‚
β”‚  β”‚   Write RAW    Read RAW      Read MARTS      Read via                    β”‚β”‚
β”‚  β”‚   Only         Write all     (masked)        Data Share                  β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚                                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚                      DYNAMIC DATA MASKING                                β”‚β”‚
β”‚  β”‚                                                                          β”‚β”‚
β”‚  β”‚   TRANSFORMER_ROLE sees:        ANALYST_ROLE sees:                       β”‚β”‚
β”‚  β”‚   SSN: 999-12-3456              SSN: ***-**-3456                         β”‚β”‚
β”‚  β”‚   Name: John Smith              Name: J*** S***                          β”‚β”‚
β”‚  β”‚   DOB: 1985-03-15               DOB: 1985-01-01 (year only)              β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ›  Technology Stack

ComponentTechnology
Cloud PlatformSnowflake (Trial Account)
Infrastructure as CodeTerraform with snowflake-labs/snowflake provider
AuthenticationRSA Key-Pair (no passwords)
Data Transformationdbt-core 1.7.x with dbt-snowflake
Synthetic DataSynthea (FHIR-compliant generator)
BI LayerEvidence (code-based dashboards)
CI/CDGitHub Actions

πŸ“‹ Prerequisites

Before you begin, ensure you have:

πŸš€ Quick Start

1. Clone the Repository

git clone https://github.com/yourusername/snowflake-healthtech-warehouse.git
cd snowflake-healthtech-warehouse

2. Generate RSA Key Pair for Snowflake Authentication

# Create directory for keys
mkdir -p ~/.snowflake

# Generate private key (PKCS8 format, no password)
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out ~/.snowflake/rsa_key.p8 -nocrypt

# Generate public key
openssl rsa -in ~/.snowflake/rsa_key.p8 -pubout -out ~/.snowflake/rsa_key.pub

# View public key (you'll need this for Snowflake)
cat ~/.snowflake/rsa_key.pub

3. Configure Snowflake User

Log into Snowflake and run:

-- Replace 'your_user' with your actual username
-- Copy the public key content (without the BEGIN/END lines)
ALTER USER your_user SET RSA_PUBLIC_KEY='MIIBIjANBgkq...';

4. Configure Environment

# Copy example environment file
cp .env.example .env

# Edit .env with your settings
nano .env  # or use your preferred editor

Required settings:

SNOWFLAKE_ACCOUNT=xy12345.us-east-1   # Your account identifier
SNOWFLAKE_USER=your_username           # Your username
SNOWFLAKE_PRIVATE_KEY_PATH=~/.snowflake/rsa_key.p8

5. Run Setup Script

./init.sh

This will:

  • Validate prerequisites
  • Load environment variables
  • Create Python virtual environment
  • Install dependencies
  • Initialize Terraform
  • Set up Evidence

6. Deploy Infrastructure

cd terraform
terraform plan     # Review changes
terraform apply    # Deploy resources

7. Generate and Load Data

# Generate synthetic patient data
./data/scripts/generate_synthea.sh

# Load data into Snowflake
python scripts/load_data.py

8. Run dbt Transformations

source venv/bin/activate
cd dbt
dbt deps    # Install packages
dbt run     # Run models
dbt test    # Validate data

9. Start Evidence Dashboards

cd evidence
npm run dev
# Open http://localhost:3000

10. Verify Security & Infrastructure (Optional)

Run the comprehensive verification scripts to ensure everything is working:

source venv/bin/activate

# Verify all infrastructure was provisioned correctly
python scripts/verify_infrastructure.py

# Test RBAC permissions and data masking
python scripts/test_security.py

# Or run all tests at once
python scripts/run_all_tests.py

The security test demonstrates:

  • RBAC: LOADER_ROLE can only access RAW_DB, ANALYST_ROLE can only access MARTS_DB
  • Masking: Same query returns different results based on role:
    • TRANSFORMER_ROLE sees: SSN 999-12-3456, Name John Smith, DOB 1985-03-15
    • ANALYST_ROLE sees: SSN ***-**-3456, Name J***, DOB 1985-01-01

πŸ“ Project Structure

snowflake-healthtech-warehouse/
β”œβ”€β”€ .env.example              # Environment template (COMMITTED)
β”œβ”€β”€ .env                      # Actual secrets (GITIGNORED)
β”œβ”€β”€ .gitignore                # Comprehensive ignore rules
β”œβ”€β”€ requirements.txt          # Python dependencies
β”œβ”€β”€ init.sh                   # Setup script
β”œβ”€β”€ README.md                 # This file
β”œβ”€β”€ AGENT_NOTES.md            # Notes for AI agents
β”‚
β”œβ”€β”€ terraform/                # Infrastructure as Code
β”‚   β”œβ”€β”€ main.tf               # Root module
β”‚   β”œβ”€β”€ variables.tf          # Input variables
β”‚   β”œβ”€β”€ outputs.tf            # Output values
β”‚   β”œβ”€β”€ providers.tf          # Snowflake provider
β”‚   β”œβ”€β”€ terraform.tfvars.example
β”‚   └── modules/
β”‚       β”œβ”€β”€ databases/        # RAW, STAGING, MARTS, ANALYTICS
β”‚       β”œβ”€β”€ warehouses/       # LOADING, TRANSFORM, ANALYTICS
β”‚       β”œβ”€β”€ roles/            # LOADER, TRANSFORMER, ANALYST, READER
β”‚       β”œβ”€β”€ schemas/          # SYNTHEA, HEALTHCARE, CORE, ANALYTICS
β”‚       β”œβ”€β”€ stages/           # Internal stages for loading
β”‚       └── security/         # Masking policies
β”‚
β”œβ”€β”€ dbt/                      # Data transformations
β”‚   β”œβ”€β”€ dbt_project.yml
β”‚   β”œβ”€β”€ profiles.yml.example
β”‚   β”œβ”€β”€ packages.yml
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   β”œβ”€β”€ sources.yml       # RAW_DB sources
β”‚   β”‚   β”œβ”€β”€ staging/          # 1:1 with source (views)
β”‚   β”‚   β”œβ”€β”€ intermediate/     # Business logic joins
β”‚   β”‚   └── marts/            # Dimensional models (tables)
β”‚   β”‚       β”œβ”€β”€ core/         # dim_patient, fact_encounters
β”‚   β”‚       └── analytics/    # Aggregated metrics
β”‚   β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ macros/
β”‚   └── seeds/
β”‚
β”œβ”€β”€ data/                     # Data generation
β”‚   β”œβ”€β”€ synthea/
β”‚   β”‚   └── csv/              # Generated patient data
β”‚   └── scripts/
β”‚       └── generate_synthea.sh
β”‚
β”œβ”€β”€ scripts/                  # Python utilities
β”‚   └── load_data.py          # CSV to Snowflake loader
β”‚
β”œβ”€β”€ evidence/                 # BI dashboards
β”‚   β”œβ”€β”€ package.json
β”‚   β”œβ”€β”€ evidence.config.yaml
β”‚   β”œβ”€β”€ sources/
β”‚   β”‚   └── snowflake.yaml.example
β”‚   └── pages/
β”‚       β”œβ”€β”€ index.md          # Executive dashboard
β”‚       β”œβ”€β”€ patients.md       # Demographics
β”‚       β”œβ”€β”€ encounters.md     # Visit analytics
β”‚       └── security-demo.md  # Masking demonstration
β”‚
β”œβ”€β”€ .github/workflows/        # CI/CD
β”‚   β”œβ”€β”€ terraform-plan.yml
β”‚   β”œβ”€β”€ terraform-apply.yml
β”‚   └── dbt-run.yml
β”‚
└── docs/                     # Documentation
    β”œβ”€β”€ architecture.md
    β”œβ”€β”€ security.md
    └── demo-script.md

πŸ” Security Features

Role-Based Access Control (RBAC)

RoleCan AccessCannot Access
LOADER_ROLEWrite to RAW_DBRead STAGING, MARTS
TRANSFORMER_ROLERead RAW, Write STAGING/MARTSN/A
ANALYST_ROLERead MARTS (masked)Read RAW, STAGING
READER_ROLERead via Data ShareDirect database access

Dynamic Data Masking

Same query, different results based on role:

SELECT patient_id, ssn, first_name, last_name, date_of_birth
FROM marts_db.core.dim_patient
LIMIT 3;

As TRANSFORMER_ROLE:

patient_idssnfirst_namelast_namedate_of_birth
1999-12-3456JohnSmith1985-03-15
2888-34-5678JaneDoe1990-07-22

As ANALYST_ROLE:

patient_idssnfirst_namelast_namedate_of_birth
1*--3456J***S****1985-01-01
2*--5678J***D**1990-01-01

πŸ“š Documentation

πŸ”„ CI/CD Setup

The project includes GitHub Actions workflows for automated deployment.

Required GitHub Secrets

Configure these secrets in your repository settings (Settings β†’ Secrets and variables β†’ Actions):

SecretDescriptionHow to Get
SNOWFLAKE_ACCOUNTSnowflake account identifierFrom your Snowflake URL (e.g., xy12345.us-east-1)
SNOWFLAKE_USERSnowflake usernameYour Snowflake login username
SNOWFLAKE_PRIVATE_KEYRSA private key (full content)Contents of ~/.snowflake/rsa_key.p8

Setting Up the Private Key Secret

# Copy the entire private key file content
cat ~/.snowflake/rsa_key.p8 | pbcopy  # macOS
# Or manually copy the contents including BEGIN/END lines

Then paste as the SNOWFLAKE_PRIVATE_KEY secret value in GitHub.

Workflow Overview

WorkflowTriggerPurpose
terraform-plan.ymlPull RequestPreview infrastructure changes
terraform-apply.ymlPush to mainApply infrastructure changes
dbt-run.ymlAfter terraform-applyRun data transformations

Environment Protection

For production safety, configure an "environment" in GitHub:

  1. Go to Settings β†’ Environments β†’ New environment
  2. Name it production
  3. Enable "Required reviewers" for apply workflows

🎬 Demo

A 5-7 minute video demonstration is available showing:

  1. Terraform infrastructure deployment
  2. Synthea data generation and loading
  3. dbt transformations and testing
  4. Dynamic data masking in action
  5. Evidence dashboards with live data

🀝 Contributing

This is a portfolio project, but suggestions and feedback are welcome!

πŸ“„ License

MIT License - See LICENSE for details.

πŸ‘€ Author

Built as a demonstration of Snowflake architecture patterns for healthcare analytics.


Note: This project uses synthetic data generated by Synthea. No real patient information is included.

Contributors

karankrishnani

27 commits

Languages

Python

48.0%

HCL

40.5%

Shell

11.4%