TL;DR
- If governance checks run after infrastructure reaches production, they only document violations after the damage or cost has already occurred. Enforcement needs to sit between Terraform plans and apply so critical violations stop deployment.
- Restricting console access works only when developers have a governed path for the infrastructure they actually need. If the catalog doesn’t cover that work, they’ll find another path that bypasses policy checks and audit records.
- Governance cannot enforce rules against resources it doesn’t know exist. Automatic inventory through the Terraform backend removes the manual registration step that otherwise leaves orphaned resources outside the governance model.
- Cost controls become actionable when teams see the financial effect before deployment rather than at month-end. Pre-deployment estimates expose expensive changes while they’re still easy to reject or adjust.
- Developer velocity suffers when governance becomes a separate approval queue instead of part of provisioning. Restricting self-service options to policy-approved configurations prevents invalid deployments without forcing developers through manual review.
- Scaling governance requires one control point that propagates changes instead of separate policies across every account and provider. RBAC, deployment validation, inventory, and self-service therefore have to operate as a connected chain rather than independent controls.
When Cloud Governance Breaks, and Why It Breaks the Same Way
Governance policies fail because the policy document and the infrastructure provisioning workflow live in completely different systems, connected only by human memory. Cloud governance is the set of policies, automated controls, and access models that determine how infrastructure is provisioned, changed, and decommissioned. It grew from a compliance checkbox in regulated industries into an operational discipline that any team running infrastructure at scale has to address. The critical distinction is that governance means enforcement, not documentation. A security baseline that exists as a wiki page but isn’t validated at deployment time isn’t governance. It’s a record of what the team intended to do before the incident happened.
The structural failure mode looks like this: a platform team writes tagging requirements, encryption standards, and instance sizing rules. Engineers provision infrastructure through cloud provider consoles. Nothing connects the two. When an audit arrives, someone manually cross-references deployed resources against the policy document. Multi-cloud environments compound this because each provider’s native tooling only sees its own cloud. AWS Config rules don’t apply to Azure resources, and Azure Policy can’t see GCP projects. Engineers working across three providers end up maintaining three partially overlapping compliance models, each requiring separate configuration.
A thread in r/devops captured the operational reality: infrastructure audits “feel like archaeology” because cloud changes made through native consoles bypass formal change management, leaving teams spending weeks reconstructing documentation for actions already running in production.

This article covers four layers of a governance framework that enforces itself: policy-as-code for deployment-time validation, RBAC design that doesn’t require console access to function, asset inventory as the visibility layer, and governed self-service that keeps developers out of uncontrolled provisioning paths.
The Policy Layer: From Documents to Deployment-Time Enforcement
A policy document and a policy enforcement mechanism are not the same thing, but most governance implementations treat them as equivalent until the first audit finding.
The policy-as-code model closes this gap by expressing governance rules as machine-readable definitions that execute against infrastructure changes before they ship. Instead of a wiki page stating “all production RDS instances must have backup enabled,” the rule exists in code that reads the Terraform plan, checks the configuration, and either blocks the deployment or sends a notification, depending on severity level, without a human reviewer in the loop.
Why Written Rules Don’t Translate to Running Infrastructure
Two provisioning paths coexist in most cloud environments: the formal IaC path, where infrastructure is defined in Terraform or OpenTofu and reviewed before deployment, and the console path, where engineers with direct cloud access make changes that never touch a pipeline. The console path is where governance breaks and security groups get modified to unblock a developer. An RDS instance gets provisioned during an incident without the tagging that enables cost attribution, and nobody decommissions it afterward. According to Flexera’s 2026 State of the Cloud report, only 28% of organizations report automated cost optimization and governance controls, which means 72% still rely on processes that depend on people following procedures they already know how to skip.
The enforcement gap isn’t a tooling deficiency in most cases; rather it’s that the governance check runs after the change, not before it. Post-deployment compliance reports catch violations that have already been running for days or weeks, at which point remediation costs more than prevention would have.
OPA and Rego: What Policy-as-Code Actually Looks Like
Cycloid’s InfraPolicy runs on the Open Policy Agent engine, evaluating Terraform plan JSON against rules written in Rego before any change is applied. Three severity levels determine the enforcement response: Critical blocks the change entirely, Warning blocks it but allows a manual override, and Advisory lets the change proceed while triggering a notification.
The following rule from the InfraPolicy documentation enforces required tags on all resources. Without it, any resource provisioned without a tag key passes validation silently:
package example
deny[reason] {
resource := input.tfplan.planned_values.root_module.resources[_]
not resource.values.tags
reason = sprintf("tags required for the resource %q", [resource.address])
}
When this rule runs at Critical severity, an untagged resource fails the pipeline job before any cloud API call is made. The plan output surfaces the specific resource address that triggered the denial, so the engineer knows exactly what to fix. Contrast this with a tagging policy enforced through a monthly compliance report: the untagged resource runs for weeks before anyone notices, cost attribution is impossible during that window, and remediation requires a manual change rather than a corrected deployment.
For environments where instance sizing is a cost governance concern, an allow-list rule restricts the instance types a Stack can provision without requiring platform team review of every deployment:
package example
allowed_instance_types = {
"t2.medium",
"t2.large",
"t2.xlarge"
}
deny[reason] {
itype := input.tfplan.resource_changes[_].change.after["instance_type"]
not allowed_instance_types[itype]
reason = sprintf("instance_type %q is not accepted, use one of the allowed: %v", [itype, allowed_instance_types])
}
Wiring Policy Checks into the Deployment Path
A policy rule sitting in a repository does nothing until it’s wired into the pipeline between the Terraform plan step and the apply step. InfraPolicy integrates directly into Cycloid’s CI/CD pipeline as a validation resource. The pipeline job fails on a Critical violation, surfaces the denial reasons as job metadata, and blocks apply until the plan is corrected.
Locally, a platform engineer can test policies against a plan before committing them using the Cycloid CLI:
$ terraform plan -out=./plan; terraform show -json ./plan > plan.json
$ cy infrapolicy validate --plan-path ./plan.json
ADVISORIES CRITICALS WARNINGS
1 0 0
The failure mode to avoid: integrating policy checks as optional steps rather than required gates. A check that can be skipped under time pressure will be skipped. Governance value holds only when the policy validation step is a hard dependency where the apply job doesn’t run if validation fails.
RBAC and Access Control Without Console-Shaped Holes
RBAC fails not when it’s designed too loosely, but when it’s designed correctly at initial setup and then never updated as team membership and project scope evolve.
Role definitions that made sense at a team of 15 become security liabilities at 150, because inherited permissions accumulate faster than anyone audits them. Permission drift, where IAM roles expand beyond their original scope over time, was a root cause in the UnitedHealth / Change Healthcare breach that exposed 192.7 million patient records in 2025. In multi-cloud environments, drift happens in parallel: AWS IAM, Azure RBAC, and GCP IAM each require separate configuration, and a change in one doesn’t propagate to the others without manual duplication.
Role Hierarchy That Maps to How Work Gets Done
The practical structure for a platform team is four distinct access tiers.
- Platform engineers need write access to shared Stack definitions and InfraPolicy configurations.
- Application team leads need provisioning access within approved boundaries, where they can deploy from the service catalog but can’t modify the catalog itself.
- Developers access the StackForms interface to configure and trigger deployments without touching the underlying IaC.
- Cost owners and compliance reviewers get read-only access to dashboards, inventory, and audit logs.

Cycloid’s organization hierarchy enforces this through child organizations that inherit policies defined at the root level. A policy change at root propagates to every child environment at the next update cycle, with no per-account patches and no manual synchronization across dozens of cloud accounts. The alternative, updating policies account by account at scale, guarantees drift: the human effort required to push changes consistently across hundreds of accounts exceeds what a platform team can reliably deliver without dedicated automation.
The tradeoff is explicit; a tighter role hierarchy means fewer engineers with console access, which reduces the drift risk but requires that the self-service provisioning interface covers the provisioning paths developers need. If the service catalog doesn’t cover a use case, the developer will find another way, and that way will almost certainly bypass governance controls.
Console Access as a Governance Risk
Direct cloud console access is where governance models develop the holes that auditors find. An engineer with console access can modify a security group, resize an instance, or delete a resource without triggering any pipeline step, InfraPolicy check, or inventory update. The change is real; the governance record of it is absent.
Reducing console access isn’t primarily a security decision. It’s a governance architecture decision. When provisioning runs through Cycloid’s pipeline and StackForms interface, every change is version-controlled, policy-validated, and recorded in the Asset Inventory. The engineer who needed to modify a security group during an incident can do so through a Stack update that goes through InfraPolicy validation, producing an audit trail that a manual console change never would.

Cycloid’s observability dashboard showing RBAC roles, project environments, and governance controls in a unified view. The platform team configured access tiers are visible in the role assignment panel, showing the four-tier model across platform engineers, developers, and cost owners.
Access in the panel is scoped by organizational role rather than by which cloud console the engineer happens to have credentials for, which is what makes the model auditable.
Asset Inventory: Governance Requires Knowing What Exists
A governance framework can’t enforce policy on resources it doesn’t know about. Orphaned resources, EBS volumes without an attached instance, elastic IPs no longer routed anywhere, RDS instances provisioned during an incident that nobody decommissioned, don’t violate any active policy because they’re invisible to the policy engine.
Flexera’s 2026 data puts estimated cloud waste at 29% of total spend, with the majority attributable to resources with no owner tag and no decommission schedule.
Inventory-Driven Visibility Across Providers
Cycloid’s Asset Inventory populates automatically through the Terraform HTTP backend: every resource provisioned through a Cycloid Stack gets recorded in the inventory with its owning project, environment, and Stack version.

Engineers see what exists across all environments without requiring direct cloud console access, which is how the console-access drift risk gets resolved at the architecture level. When the inventory is the authoritative source, console access becomes an emergency measure rather than a routine workflow.
The two methods for adding resources to inventory differ in governance implications. The Terraform HTTP backend path is automatic: Cycloid reads state from the remote backend and creates the inventory record without manual steps. The API path for customer resources is manual, which means it requires the same kind of discipline as the wiki-based governance model. Someone has to remember to register the resource. The Terraform path is the one to enforce as policy because the manual path can’t be trusted at provisioning velocity.
Cost and Carbon at the Resource Level
The governance failure that post-deployment cost reporting creates is a timing problem: the team sees what they spent at month-end but the over-provisioned instance ran for 30 days before anyone noticed. Cycloid’s Cloud Cost Management surfaces spend data per provider, per project, and per environment in a continuous dashboard rather than a monthly bill.

The Cloud Carbon Footprint module integrates directly alongside cost data, so the same resource view shows both financial and emissions impact without switching tools.
The pre-deployment layer addresses a different governance intervention point. Using the TerraCost engine, cost estimation runs before a Stack deploys:
$ terraform plan -out=./plan; terraform show -json ./plan > plan.json
$ cy terracost estimate --plan-path plan.json
PLANNEDCOST PRIORCOST RESOURCEESTIMATES
71.71 53.24 3
The output shows the engineer a projected cost delta before any cloud API call is made. An InfraPolicy rule set at Warning severity can flag deployments that exceed a cost threshold: the change isn’t blocked, but the platform team sees the advisory before the environment goes live.

Cycloid’s FinOps and GreenOps dashboard showing cloud costs and carbon footprint side-by-side across multiple providers. Cost bars are broken down by project and environment, with carbon footprint trend lines alongside.
The per-environment breakdown makes cost attribution possible at the project level rather than at the cloud account level, which is the granularity that makes chargebacks and governance conversations accurate.
Self-Service That Doesn’t Create Governance Gaps
The governance frameworks that generate the most shadow IT aren’t the ones with weak policies. They’re the ones with policies so restrictive that developers route around them.
An 86% majority of IT professionals in a Stacklet survey reported that enforcement of cost, compliance, and security is challenging, and cited governance friction as an inhibitor to cloud adoption. The platform team becomes the cloud police. Developers with console access use it. The governance model breaks not because it was poorly designed but because it was designed to block rather than to channel.
StackForms as Governed Self-Service
Cycloid’s StackForms resolve this by embedding governance into the provisioning interface itself. Platform teams define Stack configurations that constrain the choices available to developers through dropdown menus, auto-complete lists, and range sliders, all of which translate directly to Terraform variable values. A developer selecting an EC2 instance type from a StackForms dropdown can only select instance types in the allowed_instance_types set defined in the InfraPolicy. The governance check doesn’t happen after the form is submitted. It’s built into the form structure before any developer ever sees it.
The StackForms configuration that enforces instance type selection, taken from the Cycloid documentation, looks like this:
vars:
- name: Front type
description: Type of Aws EC2 frontend servers.
key: front_type
widget: auto_complete
type: string
values: [t3.micro, t3.small, t3.medium, t3.large]
default: t3.small
Based on the context above, here is the .forms.yml file you can create in the stack git repository, next to the .cycloid.yml file.

The values list is the governance boundary. A developer sees four instance type options. The InfraPolicy allow-list covers exactly those four types. No policy violation is possible from the StackForms interface because the form was designed against the policy from the start.
Keeping the Governance Model Current
A governance model that can’t be updated without a platform team release cycle accumulates technical debt at the same rate as the infrastructure it governs. Stacks defined at the root organization level in Cycloid propagate to child organizations automatically: a policy change in a shared Stack applies to every environment using that Stack at the next update cycle, without per-account patches. One change point, automatic distribution, and no manual synchronization.

Cycloid’s service catalog showing a stack deployment view with StackForms interface, displaying governed instance type options and pre-deployment cost estimate.
The contrast with a per-account policy update model matters at any organization running more than 50 cloud accounts: pushing a policy change account by account at that scale requires either dedicated automation or dedicated headcount, and neither solves the underlying problem. Both approaches just move the governance burden from enforcement to distribution.
Building a Cloud Governance Framework That Doesn’t Require Headcount to Enforce
The organizations reporting faster audit completion and fewer compliance violations have one structural characteristic in common: their governance runs as code rather than as a review process. The OPA-embedded fintech case that IT Convergence documents, where embedding Open Policy Agent policies directly into Terraform reduced compliance violations by 80% and cut audit preparation from weeks to one day, reflects the pattern: enforcement at deployment time outperforms enforcement through periodic review at every scale.
The four layers in this article depend on each other in sequence. InfraPolicy validation only covers changes that go through the pipeline; RBAC design that eliminates console access is what ensures the pipeline is the only provisioning path. Asset Inventory visibility only reflects what it knows about; the Terraform HTTP backend integration is what ensures everything gets recorded. StackForms governance only works when the form options are designed against the InfraPolicy rules, because a form that allows an instance type the policy blocks produces a confusing developer experience rather than a governed one.
The choice isn’t between governance and developer velocity. Platform teams that defer governance enforcement in favor of faster provisioning eventually spend more time on compliance remediation than the upfront enforcement work would have cost. The question is whether the enforcement mechanism is a human looking at a compliance dashboard after the fact, or code running at the moment a change is proposed.
FAQs
1. What’s the difference between a cloud governance framework and a cloud compliance framework?
Compliance frameworks (SOC 2, HIPAA, PCI DSS) define the requirements an organization must meet. A cloud governance framework defines how those requirements are enforced operationally: which controls exist, who owns them, and how violations are detected and remediated. Governance is the operating model; compliance is the target it aims at.
2. How do you enforce cloud governance without slowing developer velocity?
Governance-wrapped self-service is the pattern that resolves this: developers get a provisioning interface with pre-approved options built against the policy rules, so enforcement runs at form-design time rather than as a gate after submission. When the service catalog covers the provisioning paths developers actually need, the approval is embedded in the form rather than added as a ticket queue.
3. What is policy-as-code and how does it work in cloud governance?
Policy-as-code expresses governance rules as machine-readable definitions, using tools like Open Policy Agent, that execute against infrastructure changes before deployment. The rule reads the Terraform plan, checks the proposed configuration against defined conditions, and returns either a pass or a denial with a specific reason. Enforcement runs in the CI/CD pipeline rather than in a post-deployment review.
4. How do you build a cloud governance framework for a multi-cloud environment where each provider has different IAM and policy models?
The approach that works at scale is defining policy and access control at a layer above the individual cloud providers rather than natively within each one. A platform-level tool that applies InfraPolicy rules to Terraform plans enforces the same governance logic regardless of whether the plan targets AWS, Azure, or GCP resources. RBAC defined at the organization level and propagated to child organizations avoids the per-provider synchronization problem that makes native IAM models diverge over time.
Check our Comparisons: Cycloid vs Backstage / Cycloid vs Port / Cycloid vs Humanitec


