CI/CD, Drift Detection, and Policy Governance for Spatial Infrastructure as Code
Every spatial provisioning change — a new PostGIS read replica, a resized GeoServer fleet, a lifecycle rule on a raster bucket — should reach production only after passing through an automated gauntlet of plan, policy, and cost checks, which is why the governance and automation layer is the fourth load-bearing domain of Spatial Infrastructure as Code alongside architecture, provisioning, and security. This domain wraps every other workflow: it defines how a commit becomes a reviewed plan, how Pipeline Orchestration for Spatial Deployments sequences the stages that touch tile servers and geodatabases, and how Policy as Code for Spatial Resources rejects a non-compliant plan before a single bucket, role, or coverage endpoint is materialized. For DevOps and GIS platform teams, the governed deploy loop is what converts version-controlled infrastructure from an aspiration into an enforceable, auditable contract.
Architectural Context
Governance for spatial infrastructure is harder than governance for a stateless web tier because the resources under management are heavy, stateful, and expensive to recreate. A conventional application pipeline can afford to treat apply as low-risk: containers are immutable and roll forward cleanly. A spatial pipeline routinely mutates a PostGIS cluster holding terabytes of geometry, a GeoServer instance whose catalog is the product of weeks of layer configuration, and object stores containing pre-rendered tile pyramids that cost real money to regenerate. A careless apply that forces replacement of an RDS instance or empties a tile bucket is not an inconvenience — it is a multi-hour outage and a re-render bill. The automation layer therefore exists primarily to make destructive change visible and to force a human decision before it lands.
The second complication is scale of change surface. A single spatial estate spans PostGIS databases, GeoServer or MapServer fleets, vector and raster tile servers, COG raster stores, and the CDN and networking that fronts them. Each of these has its own compliance profile: the database must be encrypted and network-isolated, the tile CDN must honor CORS and TLS contracts, and the raster buckets must carry residency and lifecycle rules. Reviewing all of that by eye on every pull request does not scale, so the policy and cost checks that a human would otherwise perform are encoded as machine-enforced gates that run identically on every change.
Third, spatial platforms drift more than most. Tile-server fleets get resized by hand during traffic spikes, security groups get widened to debug a stuck WMS endpoint, and database parameters get tuned live during an incident. Each manual touch silently diverges the running estate from its committed definition. Left undetected, drift accumulates until the next apply produces a plan nobody understands and everybody is afraid to run. Scheduled drift detection — the closing arc of the loop shown above — is what keeps the committed code an honest description of reality. The backend that stores the authoritative record for that comparison is decided by State Backend Selection, and the identities the pipeline assumes to read and mutate it are governed by IAM Role Mapping for GIS.
Core IaC Patterns
The governed deploy loop is expressed in two complementary ways, and mature teams use both. Declarative pipeline definitions — a GitHub Actions or GitLab CI workflow file — describe the fixed sequence of stages that every change passes through: format check, validate, plan, policy evaluation, cost estimation, manual approval, apply, and post-apply verification. This declarative shape is auditable and diff-friendly; a reviewer can see exactly which gates guard main. Programmatic policy and cost logic — Rego for Open Policy Agent, Sentinel, or a typed Pulumi policy pack — expresses the rules those gates enforce, where loops and conditionals are genuinely needed to reason about a variable-length list of tile-server subnets or a map of per-environment budget ceilings.
The dividing line matters. Keep the pipeline topology declarative so the sequence of trust is legible, and reserve programmatic constructs for the rule bodies where real logic lives. Whichever engine drives the plan, three non-negotiables apply to the governance layer itself: the workflow must authenticate to the cloud with short-lived, federated credentials rather than long-lived keys; every provider and every action version must be pinned so a pipeline run today behaves identically six months from now; and the plan artifact that the gates evaluate must be the exact artifact that apply consumes, so no change can slip in between the check and the mutation. The pinned GitHub Actions workflow below encodes the governed deploy loop for a spatial estate, with OIDC federation, a saved plan, and both policy and cost gates fenced ahead of apply:
name: spatial-iac-governed-deploy
on:
pull_request:
paths: ["infra/**"]
permissions:
id-token: write # required for OIDC federation, no static AWS keys
contents: read
pull-requests: write
jobs:
plan-and-gate:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4 # pinned major
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/spatial-iac-ci
aws-region: us-east-1 # short-lived STS session
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.10.5 # pin the CLI too
- run: terraform init && terraform validate
- run: terraform plan -out=tfplan.bin # single saved artifact
- run: terraform show -json tfplan.bin > tfplan.json
# Policy gate: fails the job (exit 1) on any violation
- run: opa eval --fail-defined -i tfplan.json
-d policy/spatial/ 'data.spatial.deny[_]'
# Cost gate: block if the plan's monthly delta exceeds the ceiling
- run: |
infracost breakdown --path tfplan.json \
--format json --out-file cost.json
infracost comment github --path cost.json \
--behavior update --policy-path policy/cost.yml
Two properties make this workflow governed rather than merely automated. First, terraform plan -out=tfplan.bin produces a single binary artifact that both the policy gate and the cost gate inspect, and that the later apply job consumes verbatim — the reviewed plan is the applied plan. Second, opa eval --fail-defined returns a non-zero exit whenever any deny rule matches, which halts the pipeline before apply; the cost gate is fenced the same way against a ceiling defined in policy. Authentication is entirely keyless: id-token: write plus the configure-aws-credentials action mints a short-lived STS session from a GitHub OIDC token, so there are no long-lived secrets to rotate or leak. The mechanics of wiring that trust relationship correctly are covered in GitHub Actions OIDC for Terraform Spatial Deploys.
State, Security, and the Governance Dependency Graph
The governance layer does not own infrastructure directly; it owns the decision to change infrastructure, and every gate in that decision depends on artifacts produced by earlier stages. The plan depends on read access to remote state. The policy gate depends on the JSON rendering of that plan. The cost gate depends on the same plan plus a pricing model. The apply depends on all gates passing and on a write lock against the state backend so that two pipelines cannot converge the same estate simultaneously. This is a dependency graph of trust, and getting its ordering wrong is how ungoverned change reaches production — a cost gate that runs against a stale plan, or an apply that proceeds without a lock, is a gate that exists on paper only.
Secrets are the most sensitive edges in that graph. A spatial pipeline needs database master credentials to run migrations against PostGIS, API tokens to invalidate the tile CDN, and the cloud identity to mutate every resource. None of these belong in the repository, in environment variables committed to the workflow, or in the state file where they would be readable by anyone with state access. The governed pattern is to inject them at apply time from a dedicated secret store with short leases, and to rotate them without downtime — a discipline detailed in Secrets and Credential Management for Spatial Pipelines. The pipeline’s own identity should be a narrowly scoped role, assumed via OIDC, whose permissions are mapped exactly to the spatial actions it needs through IAM Role Mapping for GIS — never a broad administrative principal.
The diagram below maps the governance control points onto the environments they guard. Development flows with lighter gates to keep iteration fast, while staging and production tighten progressively: policy and cost checks become blocking, manual approval is required, and drift detection runs on a fixed cadence against the live estate.
Cost Governance and Drift Management
The cost gate exists because spatial infrastructure has a uniquely non-linear relationship between a small code change and a large bill. Widening a raster tile pyramid by a few zoom levels, switching a read replica to a GPU-capable instance family, or removing a lifecycle transition on a COG bucket can each multiply monthly spend while appearing as a one-line diff in review. A pre-apply cost gate renders that consequence in dollars on the pull request itself, and — as the workflow above shows — fails the pipeline when the estimated monthly delta breaches a policy-defined ceiling. This turns cost from a lagging dashboard metric into a leading, reviewable property of the plan. The methodology behind those estimates lives in Cost Estimation Frameworks, and the pipeline simply enforces the threshold they define.
Note: When a plan touches a PostGIS parameter group, express memory parameters in AWS integer units of 8 kB blocks, not percentage strings — for example
shared_buffers = 2097152allocates 16 GB, and the policy gate should assert the value is an integer within the instance’s supported range rather than a human-friendly string that the RDS API will silently reject.
Drift management is the other half of governance, and it is where the loop closes. Scheduled terraform plan and pulumi preview runs execute on a fixed cadence — nightly for production, weekly for lower tiers — comparing the committed definition against the live estate. A non-empty plan means someone changed something outside the pipeline: a hand-resized tile-server ASG, a manually widened security group, a console-edited CDN behavior. The governed response is not to blindly re-apply, which could revert a legitimate emergency fix, but to surface the drift as an alert and a proposed reconciliation commit that a human reviews. Running these scans across a large fleet without generating noise is its own discipline, covered in Scheduled Drift Detection for Tile-Server Fleets, and the broader remediation patterns — including when to auto-revert versus when to codify the change — are treated in Drift Detection and Remediation for Geospatial Estates. Together the cost gate and the drift scan enforce the same principle from two directions: every change to the estate must be intentional, attributed to a commit, and within budget.
Modularization and Portability
Governance rules are themselves infrastructure, and they deserve the same modular discipline as the resources they guard. A policy library should be a versioned, testable unit — a directory of Rego packages or a Pulumi policy pack — imported by every pipeline rather than copy-pasted into each repository. When the rule that mandates encryption on raster buckets lives in one place, tightening it propagates to every estate on the next run; when it is duplicated, enforcement rots unevenly. The same applies to the pipeline scaffolding itself: a reusable workflow that encapsulates plan, gate, and apply stages keeps every spatial repository governed identically, and the rule bodies it invokes are authored through Policy as Code for Spatial Resources.
Portability of the governance layer matters as much as portability of the data layer. A pipeline that assumes a single CI provider or a single cloud’s proprietary policy service becomes a lock-in point exactly where teams most need flexibility. Standardizing on open, engine-neutral formats keeps the gates portable: evaluate the vendor-neutral JSON plan that both Terraform and Pulumi can emit, express rules in a portable policy language such as Rego, and store cost models as declarative YAML rather than provider-specific scripts. Just as the data layer gains portability from open formats like GeoParquet and Cloud Optimized GeoTIFF served through OGC API standards, the governance layer gains portability from open plan and policy representations — so the same gates can guard an AWS estate today and a multi-cloud estate tomorrow. The engine-level decision underneath all of this, which shapes how cleanly plans and policies express, is examined in Terraform vs Pulumi for GIS.
Operational Maturity Checklist
- Keyless authentication: Pipelines assume short-lived, federated identities via OIDC — never long-lived cloud keys stored as CI secrets — scoped to the narrowest set of spatial resource actions.
- Single reviewed artifact: The plan that the policy and cost gates evaluate is the exact binary artifact that
applyconsumes, so no change can be introduced between review and mutation. - Blocking gates with pinned versions: Policy-as-code and cost gates fail the pipeline (non-zero exit) on violation, and every provider, CLI, and action version is pinned for reproducible runs.
- Progressive environment strictness: Development gates warn to keep iteration fast; staging and production add blocking gates, mandatory manual approval, and scheduled drift detection.
- Closed drift loop: Scheduled
plan/previewruns on a fixed cadence surface out-of-band change as reviewed reconciliation commits rather than silent divergence or blind re-apply. - Secrets injected, never stored: Database credentials and CDN tokens are injected at apply time from a short-lease secret store, rotated without downtime, and kept out of state and workflow files.
The governance and automation layer is what makes the rest of Spatial Infrastructure as Code trustworthy at production scale. Architecture defines the shape of the estate, provisioning creates it, and security constrains it — but the governed deploy loop is what guarantees that every subsequent change to that estate is planned, checked against policy and budget, applied deliberately, and continuously reconciled against reality. Without it, an IaC codebase is merely a snapshot; with it, the codebase becomes a durable, auditable contract for the entire spatial platform.
Related
- Pipeline Orchestration for Spatial Deployments — sequencing plan, gate, and apply stages for spatial estates.
- Policy as Code for Spatial Resources — authoring blocking OPA and Sentinel rules for GIS infrastructure.
- Drift Detection and Remediation for Geospatial Estates — scheduled scans and reconciliation patterns for live infrastructure.
- Secrets and Credential Management for Spatial Pipelines — short-lease injection and downtime-free rotation for pipeline secrets.
- Terraform vs Pulumi for GIS — the engine decision that shapes how plans and policies are expressed.