
August 29, 2026
12 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Your team provisions workloads on AWS for production, Azure for analytics, and maybe GCP for ML experiments—and within weeks you have public S3 buckets, untagged resources, and IAM roles that violate your own standards. Multi-Cloud Governance and Policy as Code is how you stop that drift before it reaches a bill or an audit. Instead of PDF runbooks and quarterly checklist reviews, you encode rules as versioned policy files, run them in CI against Terraform and Kubernetes manifests, and block non-compliant changes automatically. If you are already thinking about multi-cloud architecture patterns, governance is the layer that keeps that architecture safe as headcount and cloud accounts grow.
What is Multi-Cloud Governance and Policy as Code?
Multi-cloud governance is the set of processes, roles, and guardrails that keep resources consistent, auditable, and within budget when you operate across more than one public cloud. Policy as Code (PaC) is the mechanism: policies are written in a declarative language, stored in Git, reviewed in pull requests, and evaluated by automated engines—not left to memory or Slack reminders.
In practice, governance spans four domains that overlap on every cloud:
- Security: encryption at rest, no public endpoints, least-privilege IAM, approved regions.
- Cost: mandatory cost-centre tags, instance-size caps, idle-resource rules.
- Compliance: CIS benchmarks, SOC 2 controls, data-residency constraints.
- Operations: naming conventions, backup retention, approved AMIs or machine images.
Native cloud policy engines—AWS Organizations SCPs, Azure Policy, GCP Organization Policies—enforce rules inside each provider. Policy as Code tools sit one layer above: they validate the intent in your Terraform, Pulumi, or Kubernetes YAML before anything is applied, giving you one pipeline for three clouds instead of three separate manual review processes.
How policy as code differs from documentation
A written standard says "all S3 buckets must block public access." Policy as code encodes that rule so a pull request adding acl = "public-read" fails the build. The policy file, the Terraform module, and the CI log become audit evidence. That shift matters when a Nepali fintech startup chases SOC 2 or when a SaaS product stores PII subject to Nepal's data-privacy expectations—you need repeatable proof, not screenshots of console settings.
Why do teams need policy as code in multi-cloud environments?
Single-cloud teams can sometimes rely on console guardrails and one platform team's tribal knowledge. Multi-cloud breaks that model fast. Each provider names resources differently, exposes different defaults, and bills in different currencies. A developer who learned AWS tagging conventions will mis-tag Azure resource groups on day one unless automation catches it.
The pain points I see repeatedly on client projects and in production deployments:
- Configuration drift: Emergency hotfixes bypass standards; six months later nobody remembers which bucket is exposed.
- Inconsistent tagging: FinOps reports are useless when
cost-centreexists on AWS butCostCenteron Azure. - Shadow IT: A second AWS account or Azure subscription spun up for a demo never gets decommissioned.
- Audit scramble: Compliance asks for encryption proof across 200 resources; you grep CloudTrail instead of pointing at Git history.
Policy as code addresses each item at the point of change. Combined with centralised Terraform state for multi-cloud, you get a single approval path for infrastructure intent regardless of target cloud.
Governance that lives only in Confluence is governance that fails the first busy release week.
Which policy-as-code tools work best across AWS, Azure, and GCP?
No single tool covers every layer. Mature teams stack complementary engines: a universal IaC scanner, an optional admission controller for Kubernetes, and native org-level policies as the last line of defence.
| Tool | Best for | Cloud coverage | Language / format | Typical gate |
|---|---|---|---|---|
| Checkov | Terraform, CloudFormation, K8s YAML scanning | AWS, Azure, GCP (+100 frameworks) | Python policies + YAML | CI on terraform plan JSON |
| OPA + Conftest | Custom cross-cloud rules | Any JSON/YAML input | Rego | CI + admission webhook |
| HashiCorp Sentinel | Terraform Cloud/Enterprise policy sets | All Terraform providers | Sentinel DSL | Remote plan/apply in TFC |
| tfsec / Trivy | Fast Terraform security scans | Major providers | Go rulesets | CI pre-merge |
| Azure Policy / AWS SCPs | Runtime enforcement at org level | Single cloud each | JSON / HCL | API deny on non-compliance |
For teams already standardising on Terraform or OpenTofu, Checkov plus OPA covers most cross-cloud needs without vendor lock-in to a single cloud's policy language. Sentinel shines when you pay for Terraform Cloud and want policy sets attached to workspaces—see the dedicated walkthrough on Sentinel policy as code for Terraform for that path.
How do you implement Multi-Cloud Governance and Policy as Code step by step?
Start narrow. Pick five rules that already caused incidents or invoice surprises—not fifty controls copied from a CIS PDF. Ship those in CI, measure false-positive rate for two sprints, then expand.
Step 1: Define a cross-cloud policy baseline
Write a short internal standard covering mandatory tags, allowed regions, encryption defaults, and forbidden resource patterns. Map each rule to a cloud-agnostic identifier:
# policies/baseline.yaml — team convention (documentation)
rules:
- id: TAG-001
description: Every billable resource must have cost_centre, environment, owner
- id: SEC-001
description: Block public object storage and open security groups on 0.0.0.0/0:22
- id: REG-001
description: Production only in ap-south-1, eastus, asia-south1 Align tag keys across clouds before you automate. Use lowercase snake_case everywhere—cost_centre, not three variants—so multi-cloud cost reporting actually joins in your FinOps dashboard.
Step 2: Add Checkov to CI against Terraform plan output
Generate a plan JSON file and scan it. This catches issues in modules before apply:
terraform init -backend=false
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json
pip install checkov
checkov -f tfplan.json \
--framework terraform_plan \
--check CKV_AWS_19,CKV_AWS_21,CKV_AZURE_33 \
--soft-fail-on CHECKOV_CUSTOM_* \
--compact Pin Checkov in CI (for example checkov==3.2.x) so rule behaviour does not shift mid-pipeline. Pair this with dedicated IaC scanning guidance from tfsec, Checkov, and Terrascan compared if you want parallel engines for defence in depth.
Step 3: Write custom OPA Rego for rules scanners miss
Generic scanners cannot encode "staging Azure subscriptions may not peer into production AWS VPCs." Rego fills that gap:
# policies/rego/tags.rego
package terraform.tags
deny[msg] {
resource := input.resource_changes[_]
resource.change.actions[_] == "create"
not required_tags(resource)
msg := sprintf("TAG-001: %s missing required tags", [resource.address])
}
required_tags(resource) {
tags := resource.change.after.tags
tags.cost_centre
tags.environment
tags.owner
} Run Conftest against plan JSON or against rendered manifests:
conftest test tfplan.json -p policies/rego/ --all-namespaces Step 4: Wire the policy gate into GitLab CI or GitHub Actions
A pattern I've used on GitLab CI pipelines mirrors application test gates—policy stage fails the pipeline, merge is blocked:
policy:terraform:
stage: test
image: bridgecrew/checkov:latest
script:
- terraform init -backend=false
- terraform plan -out=plan.bin
- terraform show -json plan.bin > plan.json
- checkov -f plan.json --framework terraform_plan --quiet
- conftest test plan.json -p policies/rego/ --output json
artifacts:
paths: [plan.json]
reports:
junit: checkov-junit.xml Store policies in the same repository as modules—or a dedicated cloud-governance repo consumed as a submodule—so policy changes get the same review culture as application code.
Step 5: Layer native org policies as runtime backstop
CI can be bypassed by a local terraform apply with stolen credentials. Organisation-level denies close that hole:
- AWS: Service Control Policies denying
s3:PutBucketPublicAccessBlockmodifications outside a break-glass role. - Azure: Policy initiative requiring
Microsoft.Storage/storageAccountsto deny public network access. - GCP: Organization Policy constraint
constraints/storage.publicAccessPreventionset to enforced.
Native policies are cloud-specific; that is acceptable—they are the seatbelt when PaC in CI is the driver training.
How do you govern identity, secrets, and Kubernetes across clouds?
Policy as code is not only Terraform. Three adjacent areas cause multi-cloud incidents as often as misconfigured storage.
Workload identity instead of long-lived keys
CI pipelines that embed AWS access keys and Azure client secrets in GitLab variables violate the same least-privilege principles your IAM policies preach. Federated OIDC from GitHub Actions or GitLab to each cloud provider lets apply jobs assume short-lived roles. That pattern is documented in depth under workload identity federation without long-lived keys; treat absence of static cloud keys as policy IAM-001 and scan for them with Gitleaks in the same pipeline.
Secrets governance
Centralise secrets in HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault—and write PaC rules that forbid password = "..." literals in Terraform. On a production Laravel deployment, I've seen database URLs committed in tfvars; a Conftest rule matching password patterns in plan JSON would have blocked it before merge.
Kubernetes admission policies
If you run GKE, AKS, or EKS, OPA Gatekeeper or Kyverno enforce pod security at admission: no privileged containers, required labels, approved registries only. The same Rego skills transfer from Terraform plans to Kubernetes AdmissionReview payloads—one language, two enforcement points.
What are common mistakes when rolling out multi-cloud governance?
Teams fail less on tooling choice and more on organisational fit. Avoid these patterns:
- Policy big-bang: Enabling every Checkov rule on day one floods developers with hundreds of failures. Roll out by severity: critical security first, tagging second, optimisation third.
- No exception workflow: Every org needs time-bound waivers with ticket IDs embedded in policy annotations. Permanent exceptions become undeclared debt.
- Policies without module fixes: Blocking public S3 buckets while your shared Terraform module still defaults to public-read just trains people to click "retry with override."
- Ignoring plan-vs-apply gap: Scanning only static
.tffiles misses computed values. Always scan plan JSON afterterraform plan. - Separate repos, separate owners: When the platform team owns policies but product teams own modules, PRs stall. Co-locate policy tests with the modules they protect.
- Cost policies without visibility: Tag enforcement without a FinOps dashboard developers can see breeds resentment. Pair TAG-001 with a weekly cost report filtered by
owner.
For smaller Nepali teams running a primary VPS plus one cloud account for backups or CDN, full enterprise governance is overkill—but the same PaC habits pay off when you add a second region or DR environment. Start with secrets scanning and tag rules; expand when monthly cloud spend crosses a threshold you would notice on an NPR credit-card statement (roughly Rs 50,000/month, ~USD 370, is a sensible trigger for formal FinOps gates).
Measuring success
Track metrics that executives and engineers both understand:
- Policy violation rate per 100 PRs — should fall after module fixes, not because checks are disabled.
- Mean time to remediate a failed gate — if it exceeds a day, policies may be too opaque.
- Percentage of resources with mandatory tags — reported from cloud asset inventory APIs.
- Count of open policy exceptions — more than a handful means standards need revision.
Reference frameworks like the AWS Well-Architected Framework and CIS benchmarks when prioritising which Checkov rules to enable first—they give auditors familiar language even when you run Azure and GCP alongside AWS.
Put Multi-Cloud Governance and Policy as Code into production
Multi-Cloud Governance and Policy as Code is not a one-time compliance project. It is a feedback loop: encode a rule, fail a PR, fix a module, watch violation counts drop, add the next rule. Start with five high-impact policies—public access blocks, mandatory tags, encryption defaults, region allowlists, and no static credentials in IaC—wire them into the same GitLab CI or GitHub Actions pipeline you already use for application tests, and layer native org policies so runtime catches whatever CI misses.
If you are designing a multi-cloud footprint for a SaaS product, legal-tech portal, or eCommerce platform and want governance baked in from the first Terraform module—not bolted on after an audit—get in touch to review your architecture, CI pipeline, and policy baseline before the second cloud account opens.

