Policy-as-Code in Snowflake: Automating Governance at Scale
Aravindan Selvakumar

1. Executive Summary
Problem: At enterprise scale, manually managing Snowflake governance, including RBAC assignments, masking policies, row access rules, and object tags, becomes inconsistent, audit-deficient, and operationally fragile.
Recommended approach: Treat governance controls as versioned code artifacts. Define policies in declarative configuration files, store them in source control, and deploy them through automated pipelines, using the same discipline applied to application code.
Where it fits: This approach is useful for any organization running Snowflake across multiple environments, such as dev, UAT, and prod, with more than a handful of data domains, compliance obligations, or regulatory scrutiny.
Key outcomes: Consistent policy application across all environments, a full change history for audit purposes, reduced human error in access provisioning, and the ability to detect and remediate policy drift before it becomes a compliance incident.
What the reader can implement: A governance pipeline that takes policy definitions from a Git repository, validates them, and deploys to Snowflake without requiring a dedicated governance platform product.
2. Background
Snowflake provides a rich set of native governance capabilities: role-based access control, dynamic data masking, row access policies, object tagging, and a comprehensive audit trail through the ACCESS_HISTORY and POLICY_REFERENCES system views. These are well-designed features. The problem is not capability; it is operationalization.
In most enterprise Snowflake deployments, governance controls are applied manually by the data platform team in response to access requests, compliance reviews, or audit findings. Individual engineers run ALTER statements, grant privileges, and create masking policies against a live environment. Those changes are rarely tracked in source control, almost never peer-reviewed, and frequently inconsistent between environments.
For example, the production account may have a masking policy on a sensitive column that was never deployed to UAT, leaving test pipelines with unmasked access to production-grade data.
Policy-as-Code addresses this by applying the same engineering rigor to governance that modern teams apply to application code and infrastructure. It is not a single product or framework. It is a discipline: governance controls defined declaratively, stored in version control, validated automatically, and deployed through a pipeline.
3. Problem
3.1 Symptoms
Access privileges accumulate over time without a systematic review process. Users change roles, leave the organization, or complete a project, but their Snowflake grants remain. Over months, the effective access landscape diverges significantly from the intended model.
Masking policies and row access rules applied to production are not consistently replicated to lower environments. Data engineers working in UAT operate without the same controls, inadvertently validating pipelines against data visibility that does not reflect production behavior.
When an auditor asks which users had access to a given table six months ago, the answer requires piecing together emails, Jira tickets, and manual change logs because there is no single authoritative record of when a privilege was granted or by whom.
A privilege or masking policy change applied urgently in production by a senior engineer is never documented, never reviewed, and never reflected in the stated governance baseline. Six weeks later, no one is certain whether it is still needed.
As data domain count grows from five to twenty, the manual overhead of maintaining RBAC consistency scales proportionally, but the data platform team does not.
3.2 Impact
The business consequence of governance drift is not theoretical. Regulatory audits that surface unexplained access grants carry remediation costs and, in sectors such as financial services and healthcare, potential enforcement action.
Operationally, the data platform team absorbs a growing share of its capacity in reactive governance work: revoking stale access, investigating anomalous query patterns, and manually reconciling policy states across environments. This is time that does not go towards building data products that create business value.
4. Requirements & Assumptions
4.1 Data & SLA
Environments: At minimum, development, UAT, and production Snowflake accounts or databases. Policy-as-Code is most valuable when environments are meaningfully distinct and controlled separately.
Change frequency: Governance policy changes are expected to be infrequent relative to data pipeline changes, typically a handful of policy updates per sprint rather than dozens. The pipeline should be fast enough that governance changes are not a deployment bottleneck.
Scope: RBAC, dynamic masking policies, row access policies, object-level tags, and network policy configurations are within scope. Warehouse sizing and query performance tuning are out of scope for the governance pipeline.
4.2 Security & Compliance
Data sensitivity: Assumes the presence of PII, commercially sensitive data, or regulated information that requires controlled access and a demonstrable audit trail.
Compliance posture: The model is designed to support internal audit, GDPR or equivalent data protection obligations, and sector-specific regulation. It does not replace a compliance programme; it provides the infrastructure that makes compliance defensible.
Access to deploy: The pipeline service account must hold sufficient privileges to apply governance objects, such as SECURITYADMIN-equivalent permissions for role management and SYSADMIN-delegated privileges for object tagging. This account's own credentials and activity should be subject to the highest level of audit scrutiny.
4.3 Tooling & Constraints
Source control: A Git-based repository, such as GitHub, GitLab, Azure DevOps, or equivalent, is assumed. All policy definitions live here; the repository is the source of truth.
CI/CD platform: Any standard pipeline platform, including GitHub Actions, GitLab CI, Jenkins, or Azure Pipelines, can host the governance deployment workflow. No specialist governance tooling is required.
Policy language: Policies can be expressed as YAML, JSON, or HCL. The choice depends on existing tooling and team familiarity. The pattern described here is tool-agnostic at a conceptual level.
Key constraint: Policy-as-Code reduces human error in deployment but does not eliminate the need for human judgment in policy design. Someone must still decide what a masking policy should do and who a role should cover. Automation enforces those decisions consistently.
5. Recommended Architecture
5.1 High-Level Flow
The architecture integrates a governance pipeline alongside, not replacing, the data pipeline. Policy changes follow the same lifecycle as code changes: authored, reviewed, tested, and deployed. The data pipeline remains responsible for moving and transforming data; the governance pipeline is responsible for ensuring that access controls on that data are correct and consistent.
The critical architectural principle is that no governance object in Snowflake should exist unless it has a corresponding definition in the policy repository. Objects created manually outside the pipeline create drift. Drift detection, a scheduled comparison between the live Snowflake state and the repository state, is a first-class component of this architecture.
| Layer | Data Flow | Policy-as-Code Activity | Owner |
|---|---|---|---|
| Source Systems | ERP, CRM, SaaS apps, and operational databases emit raw data | Policy definitions stored in Git; no enforcement yet | Data & Platform Engineering |
| Ingestion Layer | Fivetran, Airbyte, or custom pipelines land data into Snowflake raw schemas | CI/CD pipeline lints and validates policy files on commit | Data Engineering |
| Snowflake Raw / Landing | Immutable source-aligned data; streams capture change events | Policy deployment pipeline applies RBAC roles and object tags to schemas | Data Engineering |
| Policy Enforcement Layer | Dynamic masking, row access, and object-level privileges enforced on governed schemas | Automated compliance checks run post-deployment; drift detection active | Governance & Platform Ops |
| Governed Data Products | Curated domain schemas with certified data contracts; accessible only through governed roles | Audit logs collected from ACCESS_HISTORY and POLICY_REFERENCES views | Domain Stewards + Platform Ops |
| Consumption Layer | BI tools, notebooks, and data apps query governed objects; external shares served via Secure Data Sharing | Policy-as-Code repository is the single source of truth for all active controls | Business Consumers |
5.2 Architecture Diagram
5.3 Options
Option A - Terraform-based deployment: Terraform with the Snowflake provider manages Snowflake governance objects as infrastructure resources. State is stored remotely, such as S3 or Azure Blob. This is best for teams already using Terraform for infrastructure and provides mature state management with a plan/apply workflow.
Option B - Custom YAML pipeline: Policy definitions stored as YAML are parsed and applied by a Python or Go utility in the CI pipeline. This is simpler to start with and does not require Terraform expertise. It is best for teams prioritizing a lightweight initial implementation.
Option C - dbt-native governance: For teams using dbt, governance metadata such as tags and data contracts can be embedded in dbt model YAML and applied through dbt macros or post-hooks. This is best for analytics engineering teams who want governance co-located with transformation logic.
Selection Guide
These options are not mutually exclusive. Many enterprises use Terraform for RBAC and network policies, which behave like infrastructure governance objects, and dbt for data classification tags, which behave like data governance objects. Start with the approach that aligns with the team already responsible for governance. Do not introduce a new toolchain just for governance.
6. Implementation
6.1 Setup
Snowflake Objects
- A dedicated service account role for the governance pipeline, with SECURITYADMIN permissions for role and grant management and SYSADMIN-delegated privileges for object tagging.
- A dedicated GOVERNANCE_ADMIN role distinct from SYSADMIN. This role is used exclusively by the deployment pipeline and carries no ability to query data directly.
- A POLICY_AUDIT schema within a governance database, used to log deployment events, drift findings, and policy change summaries in a format queryable by the data team and compliance stakeholders.
External Objects
- A Git repository with a structured directory layout: /roles, /grants, /masking-policies, /row-access-policies, /tags, and /network-policies.
- CI/CD pipeline configuration files that trigger validation on pull request and deployment on merge to main.
- A remote state store if using Terraform. Without remote state, concurrent pipeline runs can corrupt the governance state. A locking mechanism is required in production.
6.2 Core Build Steps
- Define the policy taxonomy: Agree on the classification scheme that policies will enforce, such as sensitivity tiers for masking and role hierarchy for RBAC.
- Author policy definitions: Write governance objects as declarative configuration. Masking policies should specify the sensitivity tag, role-tier behavior, and target columns.
- Implement CI validation: Run validation on every pull request, including schema validation, naming convention linting, and a plan stage that shows what Snowflake objects will change.
- Implement deployment pipeline: On merge to main, apply validated changes to the target environment and log each event with timestamp, author, and Git commit SHA.
- Implement drift detection: Run a scheduled job that compares live Snowflake policy state against repository definitions and flags unmanaged objects, configuration mismatches, or missing objects.
- Integrate with governance council process: Use the pipeline to enforce agreed decisions, not to replace the decision-making process. Major policy changes should be reviewed before pull requests are raised.
6.3 Configuration Defaults
- Branch strategy: A main branch represents the current deployed state per environment. Direct commits to main are blocked, and pull requests require review.
- Policy versioning: Each deployed policy carries the Git commit SHA as a version identifier, stored as an object comment in Snowflake and logged to the POLICY_AUDIT schema.
- Environment promotion: Policy changes deploy to DEV first, then UAT, then PROD. Production promotion requires explicit approval in the CI pipeline.
- Rollback: Terraform implementations can roll back through a revert commit and plan/apply cycle. Custom pipelines should have rollback procedures documented and tested before incidents occur.
7. Validation & Testing
7.1 Data Validation
Policy application verification: After each deployment, the pipeline should query Snowflake system views such as POLICY_REFERENCES and GRANTS_TO_ROLES to confirm that every object defined in the repository has a corresponding live entry with the expected configuration.
Masking policy behavior testing: For each masking policy, a test suite should verify that the correct role sees the correct output, such as masked values for restricted roles and full values for privileged roles.
Row access policy testing: Verify that a user assigned a given row-filter role sees only the rows expected for their filter context.
Privilege escalation testing: Verify that the role hierarchy does not introduce unintended privilege inheritance.
7.2 Reconciliation
The drift detection job described in Section 6.2 is also the primary reconciliation mechanism. Its output should be reviewed as part of a regular governance operations cadence, weekly at minimum.
Drift findings fall into three categories:
- Unmanaged objects: Objects created outside the pipeline that require a decision to either add them to the repository or remove them from Snowflake.
- Configuration drift: An object exists in both places but with different settings, typically from a manual override.
- Missing objects: An object is defined in the repository but absent from Snowflake, indicating a failed deployment.
8. Security & Access
Pipeline service account security: The GOVERNANCE_ADMIN service account used by the CI pipeline must use key-pair authentication, not password. The private key should be stored in the CI platform's secrets store, rotated on a defined schedule, and never committed to the repository.
Separation of duties: The engineer who authors a policy change should not be the sole approver of that change. SECURITYADMIN-equivalent privileges in Snowflake should not be held by developers who also hold data access.
Repository access controls: The policy repository should have branch protection on main, requiring pull request review. Direct pushes to main bypass validation and must be blocked.
Audit trail integration: The deployment pipeline logs every change with the Git commit SHA, committing author, target environment, and timestamp. This log, combined with Snowflake ACCESS_HISTORY, provides a two-layer audit trail: what the policy was configured to do and who actually accessed data under that policy.
9. Performance & Cost
9.1 Performance Considerations
Dynamic masking policies and row access policies add a policy evaluation step to each query execution. For most analytical workloads, this overhead is negligible. The performance risk arises when row access policies use subqueries that execute at query time; poorly written filter conditions can add measurable latency to large queries.
The drift detection job runs reconciliation queries against Snowflake system views. This is a metadata-intensive operation, not a data-intensive one, and should run on a small, auto-suspending warehouse. Scheduling it during off-peak hours avoids contention with analytical workloads.
Policy deployment itself executes DDL statements, which are lightweight operations in Snowflake. A full governance deployment across a large environment typically completes in minutes, not hours.
9.2 Cost Drivers
- CI/CD compute: Governance pipeline runs consume CI/CD platform compute minutes.
- Drift detection warehouse: A dedicated XS warehouse with aggressive auto-suspend is appropriate for scheduled reconciliation.
- Snowflake DDL operations: DDL statements such as CREATE MASKING POLICY and GRANT do not consume credits. Snowflake cost impact is limited to the drift detection warehouse and validation query runs.
9.3 Cost Controls
- Auto-suspend the drift detection warehouse at 60 seconds.
- Batch all governance deployment DDL into a single pipeline run per environment instead of opening a Snowflake connection per policy object.
- Set resource monitors on the governance pipeline's Snowflake role. Anomalous credit consumption from this role is a signal that something is misbehaving in the pipeline.
10. Operations & Monitoring
10.1 What to Monitor
| Signal | What It Indicates | Detection Method | Owner |
|---|---|---|---|
| Drift count (unmanaged objects) | Policy objects exist in Snowflake without a repository definition | Drift detection job; alerts on count > 0 | Platform Ops |
| Drift count (config mismatch) | A deployed policy has been modified after deployment | Drift detection job; alerts on any mismatch | Platform Ops + Governance Lead |
| Failed deployment pipeline runs | A policy change failed to apply to one or more environments | CI/CD pipeline failure notification | Data Engineering |
| Unreviewed pull requests > 48hrs | Policy change backlog building; governance bottleneck forming | Repository PR age report | Governance Lead |
| POLICY_REFERENCES coverage drop | A governance object was detached from a table | Scheduled query against POLICY_REFERENCES | Platform Ops |
| Privileged role membership change | SECURITYADMIN or GOVERNANCE_ADMIN membership changed outside the pipeline | ACCESS_HISTORY query; alert on SECURITYADMIN grant events | Security / Compliance |
10.2 Alerting
Drift alerts should fire immediately and route to the platform operations channel. A drift count above zero means governance controls do not match their specification. This is a compliance exposure, not a cosmetic issue.
Failed pipeline alerts should include the environment affected, the policy file that failed, and the error message from Snowflake. The on-call engineer should be able to diagnose the failure without logging into the CI platform.
POLICY_REFERENCES coverage drops, indicating a masking or row access policy was detached from a table, should alert the security or compliance function directly, not only the data team.
All alerts should include a link to the runbook entry for that alert type. An alert that requires the recipient to know what to do from memory will be handled inconsistently.
10.3 Runbook - Top Issues
| Issue | Likely Cause | First Response |
|---|---|---|
| Drift detected: unmanaged RBAC object | A privilege was granted manually in Snowflake outside the pipeline | Confirm whether the grant is legitimate; if yes, add it to the repository and redeploy; if no, revoke it and log the incident |
| Deployment pipeline failed mid-run | Snowflake DDL error, naming conflict, insufficient privilege, or transient connectivity | Check pipeline logs for the failed DDL statement, confirm privileges, and re-run after fixing the issue |
| Masking policy returns unexpected values in UAT | Policy file was updated but UAT deployment was not completed or was partially applied | Run drift detection against UAT, confirm POLICY_REFERENCES output, and re-run the UAT deployment job |
| POLICY_REFERENCES shows a table with no masking policy attached | A table-altering DDL detached the policy association | Reapply the masking policy from the repository definition and investigate whether the DDL was pipeline-managed or manual |
| Terraform state lock not released after failed apply | A pipeline run was interrupted without completing the unlock step | Identify the lock owner, confirm the run is not still in progress, release the lock manually, and verify state before re-running |
11. Common Pitfalls
Starting with tooling before defining policies: Teams often spend weeks evaluating Terraform versus custom scripts before deciding what a masking policy should actually contain. The policy taxonomy must be agreed before the pipeline is built. Automating undefined policies does not reduce governance risk; it scales it.
Treating Policy-as-Code as a one-time migration: Moving existing governance objects into the repository is the starting point, not the objective. The ongoing discipline is ensuring that every subsequent policy change goes through the pipeline.
Ignoring the pipeline service account as a governance risk: The account that deploys governance objects is the most privileged account in the Snowflake environment. If its credentials are compromised or its activity is not monitored, an attacker can modify governance controls without triggering a data-level alert.
Conflating policy deployment success with policy correctness: A pipeline that exits with status 0 means the DDL was accepted by Snowflake. It does not prove that the policy does what it is intended to do. Behavioral validation is still required.
12. Variations / Use Cases
| Variation / Use Case | How Policy-as-Code Adapts |
|---|---|
| Multi-account Snowflake deployment | The policy repository defines environment-specific variable overrides. The CI pipeline parameterizes deployments per target account so a single policy definition drives consistent controls across accounts. |
| Regulated industry requiring change evidence for audit | Every pull request and deployment event is retained as an immutable audit artifact. Commit history provides change evidence; POLICY_AUDIT provides deployment confirmation. |
| Federated governance with multiple domain teams authoring policies | A CODEOWNERS file routes domain-specific policy changes to the correct domain steward while the central governance council owns the role hierarchy and sensitivity taxonomy. |
| Snowflake on Azure with Azure DevOps and Key Vault | The pipeline runs in Azure Pipelines, the GOVERNANCE_ADMIN private key is retrieved from Azure Key Vault at runtime, and Azure DevOps branch policies enforce peer review. |
13. Next Steps
If this pattern aligns with challenges your organization is facing, a practical starting point is a governance posture assessment: an inventory of what governance objects currently exist in your Snowflake environment, how many were created manually, and how far the current state diverges from any stated policy baseline.
- Inventory your current governance state: Query POLICY_REFERENCES, GRANTS_TO_ROLES, and TAG_REFERENCES in your Snowflake account.
- Define your policy taxonomy before touching tooling: Agree on sensitivity classification tiers, role naming conventions, and the hierarchy model.
- Start with RBAC, not masking: Role and grant management is the highest-impact, most operationally tractable governance object type to bring under code.
- Run a Policy-as-Code readiness workshop with your SI: Boolean offers a structured half-day engagement to assess current governance tooling and produce a phased rollout plan.
- Review Boolean's related content: See the companion blog on Snowflake RBAC design patterns and the guide to audit-ready data platform operations.

Aravindan Selvakumar
Data Engineer
Boolean Data Systems

Aravindan S is a Data Engineer at Boolean Data Systems, specializing in building scalable data pipelines and modern cloud data platforms. He focuses on data transformation and modeling, with expertise in Snowflake and dbt for developing efficient ELT pipelines. He has hands-on experience in implementing Snowpipe, Tasks, and dbt-based workflows to enable reliable and maintainable data processing.
About Boolean Data
Systems
Boolean Data Systems is a Snowflake Premier Partner that implements solutions on cloud platforms. We help enterprises make better business decisions with data and solve real-world business analytics and data challenges.
Services and
Offerings
Follow us on
Solutions &
Accelerators
Global
Head Quarters
USA - Atlanta
3970 Old Milton Parkway,
Suite #200, Alpharetta, GA 30005
Ph. : 770-410-7770
Fax : 855-414-2865