I Used an AI Agent to Build My Snowflake Governance Framework. Here's What That Actually Looks Like.
There's a lot of hype about AI-assisted code generation: write a function, fix a bug, generate a test. Useful, but not that interesting (at least for me).
What's more interesting is using AI to solve the part of the problem that was always expensive: the thinking before the code.
As such, I built a Snowflake governance framework with an AI-native workflow. (Unsure if "AI-native workflow" is the right name but "I asked Claude to interview me about my own data stack" feels like it undersells it.) An AI agent handles the intake interview to capture all details custom to your data stack, the human reviews the output aka config file, and generated Terraform code handles the rest.
The Problem I Was Solving
I spent years managing a Snowflake environment for a B2B SaaS company, from original inception through 2026. Over time I watched the same tech debt accumulate: shared service accounts, ACCOUNTADMIN used as a debugging shortcut, no cost attribution, role structures that nobody could fully explain.
I suspect these aren't unusual problems. They're the predictable result of organic growth outpacing governance (we'll circle back to it eventually, right? Right?).
When I started Flynn Data Services, I wanted to build a reusable framework that other teams could use to establish rigorous guardrails without starting from scratch. The challenge: how do you generalize effectively? Your connector topology is different from mine. Your cost attribution structure depends on your team or business unit organization. Your RBAC hierarchy depends on which tools you actually use.
A static template forces you to adapt it. A config-driven framework generates the right structure for your specific environment.
My ah-ha moment: an AI-native intake can be the bridge between the two.
How the Workflow Works
The workflow has four stages — intake, config, codegen, and apply:
The intake is the part most frameworks skip entirely. They give you a template and tell you to fill it in. The problem: if you don't already understand Snowflake governance deeply, you don't know what questions to ask yourself.
The intake interview does the asking. It's a structured conversation that surfaces your specific connector topology, your warehouse workload patterns, your team structure, and your tag taxonomy. At the end of the interview, you have a connectors.yaml (one entry per integration), a team.yaml (one entry per human persona), a tags.yaml, and a decisions.md that documents every design choice and why it was made. (Working with an AI agent means you could, in theory, adapt it further if an unexpected use case or edge case surfaces as well.)
Two paths:
- Greenfield: The interview is forward-looking. What are your ingestion sources? What tools are you planning? How many analysts? The interview surfaces decisions you haven't made yet and gives you opinionated defaults where you don't have a preference.
- Brownfield: The interview starts with an environment survey. Run the audit queries against your existing account, review the output together, then do the targeted interview to surface intent and constraints behind what you find. What was this legacy role created for? Which service accounts are still active? Where does the current setup cause the most friction?
What the Config Drives
Once you have connectors.yaml, Terraform does the rest. No hardcoded role names, no hand-editing required. The full role hierarchy, grants, warehouse configuration, and resource monitors are generated from the config using native for_each patterns.
# connectors.yaml — generated during intake
connectors:
- name: FIVETRAN
type: etl
target_db: RAW_FIVETRAN
privileges: [INSERT, CREATE TABLE]
warehouse: INGEST
reason: "Fivetran managed ETL — isolated to RAW_FIVETRAN db"
vendor_managed: true
- name: AIRFLOW
type: orchestrator
target_db: RAW_AIRFLOW
privileges: [INSERT, CREATE TABLE, SELECT]
warehouse: INGEST
reason: "Airflow custom pipelines — SELECT needed for incremental logic"
- name: DBT_PROD
type: transformer
source_dbs: ["RAW_FIVETRAN", "RAW_AIRFLOW", "EVENTS"]
target_db: ANALYTICS
privileges: [SELECT, INSERT, CREATE TABLE, CREATE SCHEMA]
warehouse: TRANSFORM
reason: "dbt production runs — reads all RAW, writes to ANALYTICS" Each connector entry produces:
- A
CONN_{name}account role - A scoped object role (
OBJ_{DB}_WRITERorOBJ_{DB}_READER) - The appropriate privilege grants
- Assignment to the correct warehouse
Adding a new integration is a YAML entry and a terraform apply. No module changes required.
The Architecture Decision This Unlocks
The connector role pattern — one role per integration, scoped to that tool's specific database — is well-established community practice, and for good reason.1 What this framework adds is generating that hierarchy automatically from connectors.yaml rather than hand-rolling it per environment.
The intake interview ensures it reflects your actual topology rather than a generic template. And the config-driven approach makes this pattern maintainable (and enforceable via Terraform CI/CD in theory).
What's Built, What's Honest
52 unit tests passing Terraform validated end-to-end Live Snowflake account
Core is built, tested, and validated against a live Snowflake trial account:
- Config-driven RBAC hierarchy via
connectors.yaml - Workload-separated warehouses with resource monitors
- Intake CLI (greenfield path) — generates connectors.yaml, team.yaml, tags.yaml, and decisions.md
- 52 unit tests passing
- Terraform validate, fmt, and plan against a real account end-to-end
Running against a live environment surfaced five issues before anything shipped. The first three were visible failures: a privilege grant being applied to a schema when it needed to target future tables within it (Snowflake rejects INSERT at the schema level — it's a table-level privilege), a provider auth variable that the Snowflake Terraform provider deprecated and split into two in a recent version — setting the old variable doesn't produce an error, the provider just silently fails to connect and surfaces a generic "account is empty" message that gives you nothing to go on (the fix is a two-line change to provider.tf; finding it is the hard part), and a codegen output path that Terraform silently ignored because it only auto-loads *.auto.tfvars.json from the root module directory, not subdirectories (the kind of failure that looks like success until you notice nothing actually changed in your account).
The last two were design gaps — things that looked correct but produced environments that were subtly wrong in ways the existing tooling couldn't see. The first: team.yaml entries with named schemas (e.g. ["MARTS", "REPORTS"]) fail silently at apply time in a greenfield account because those schemas don't exist yet. Terraform applies the role but the grants never land. The fix is a scope_to field that captures the operator's intent while keeping schemas: ["*"] for the initial apply, with explicit codegen reminders so the narrowing doesn't get forgotten. The second was a gap in the brownfield audit: the existing survey checked for direct object grants to users but had no query covering which roles humans actually hold. It's possible to have zero direct grants and still have analysts assigned to OBJ_ roles — bypassing the functional role layer entirely without triggering any existing check. That query is now part of the audit, and the gap report flags it as a standard finding.
The visible failures are caught by running the framework against a real account before it reaches a client. The design gaps are the harder category — they require knowing what a correct environment looks like in order to know what to check for.
One detail worth calling out explicitly: FIREFIGHTER is live in Core. It's a break-glass role for incident response — dormant by design, no users assigned, no privilege grants on objects. It sits in the role hierarchy under SYSADMIN, which is how the role tree is structured, but it can't do anything until someone explicitly assigns a user to it. Most generic RBAC templates don't model the incident response case at all. Having a named, auditable role that is explicitly empty is a different posture than hoping nobody escalated privileges during last night's incident.
Core is the complete governance foundation: correct RBAC hierarchy, one connector role per integration, no direct object grants to users, ACCOUNTADMIN at zero active assignments, all warehouses with resource monitors, and a decisions.md that documents why the environment is structured the way it is.
Two expansion packs are on the roadmap for environments that need more:
- Observability — a tag eval suite, cost attribution via warehouse tags, and an Evidence dashboard that tracks compliance trends week-over-week. Designed for when a second team starts creating objects and you want visibility before committing to enforcement.
- Enforcement — Snowflake-native tag policies that block untagged object creation at the engine level, a FIREFIGHTER activation alert on a 60-second schedule, and CI/CD gates that block RBAC changes without a passing policy check. Designed for larger teams, regulated industries, or when Observability has surfaced persistent drift that manual remediation isn't resolving.
The right level of governance depends on your context. The expansions exist because some environments will need them — not because every environment should eventually have them. The right answer depends on your team size, your regulatory context, and how much drift you're actually seeing.
The full philosophy behind every design decision lives in PHILOSOPHY.md in the repo. Everything is traceable to a principle, not a preference.
The Part That Changed How I Think About This Work
Building this with an AI-native workflow forced something I didn't expect: it made me ask the questions that governance templates assume you already know the answers to. What's your blast radius if CONN_FIVETRAN is compromised? Why does this legacy service account exist? What's the acceptable monthly credit budget per warehouse?
Most engineers skip these not because they don't matter but because there's no structured moment in the workflow to surface them. The intake creates that moment. Agent interviews, human decides, deterministic code executes. I'm still figuring out where it breaks down.
What's Next
The repo is public on GitHub. Core is validated against a real Snowflake environment and ready to fork.
I'm looking for Snowflake shops to run the intake process against their actual setup — greenfield or brownfield. In exchange for feedback on what broke or didn't fit, you get a documented governance configuration specific to your environment. If your team needs Observability or Enforcement on top of that, we can scope it from there.
Run the intake against your environment
Clone the repo and run the intake CLI, or reach out if you'd rather walk through it together.
Greenfield or brownfield — in exchange for feedback on what broke or didn't fit, you get a documented governance configuration specific to your stack.