Why Snap CD: Non-invasive Orchestration
Most infrastructure CD tools ask you to change the way you write Terraform. Some require a proprietary wrapper CLI. Others impose a specific directory layout, inject custom backends, or parse plans through a format only they understand. The trade-off is always the same: you get orchestration, but your code now only works inside that tool's ecosystem.
Snap CD takes a different approach. It orchestrates deployments without modifying how Terraform runs. Your code stays portable, your commands stay standard, and nothing is hidden behind an abstraction you can't inspect.
The lock-in pattern
Infrastructure CD tools typically insert themselves between you and Terraform in one or more of these ways:
Wrapper CLIs. Instead of terraform plan, you run toolname plan or toolname run -- terraform plan. The wrapper intercepts the command, adds flags, manages state configuration, and sometimes alters the output. Your CI pipeline, your local workflow, and your debugging sessions all depend on the wrapper being present.
Proprietary plan formats. Some tools parse Terraform's plan output into their own internal representation for policy checks or approval workflows. When Terraform changes its plan format — which it does across major versions — you're waiting on the tool vendor to update their parser before you can upgrade.
Opinionated directory structures. A tool might require your repo to follow a specific layout: one directory per environment, a configuration file at the root describing which directories map to which workspaces, naming conventions that the tool uses to infer relationships. Reorganise your repo and the tool breaks.
Custom state backends. Some tools manage Terraform state themselves, replacing the S3/GCS/Azure backend you'd normally configure. This can simplify initial setup, but it means your state is locked inside the tool. Migrating away requires state surgery.
DSL layers. A few tools go further: you write configuration in a tool-specific language or templating system that generates Terraform code. At that point, you're not really writing Terraform anymore — you're writing input to a code generator.
Each of these creates a dependency. The more a tool wraps Terraform, the harder it is to leave, and the more your team needs to learn beyond Terraform itself.
What non-invasive means in practice
When Snap CD deploys a Module, here's what actually happens on the Runner:
1. Clone the source
The Runner clones your Git repository (or downloads from a Terraform registry) into a local working directory. This is the same code you'd check out on your laptop.
2. Provide inputs through standard mechanisms
Snap CD writes the inputs your Module needs into files that Terraform already understands:
.tfvarsfiles for Terraform variables (values wired from other Modules' outputs or from static configuration)..envfiles for environment variables your providers or scripts might need.- Shell scripts (
plan.sh,apply.sh) that wrap the Terraform commands with the correct flags and environment.
There's nothing proprietary about these files. A .tfvars file is a .tfvars file. You can open it, read it, and pass it to terraform apply -var-file= yourself.
3. Run standard Terraform commands
The Runner executes terraform init, then terraform plan, then (after approval) terraform apply. These are the real Terraform binaries — not a wrapper, not a fork, not a shim. The Runner captures stdout and stderr and streams them back to the Snap CD Server for logging, but it doesn't intercept or alter the commands.
4. Collect outputs
After a successful apply, the Runner runs terraform output -json and reports the results back to the Server. These outputs become available as inputs to dependent Modules. Standard Terraform, standard JSON.
You can always drop to the shell
Because the Runner operates on a plain directory with real Terraform files, you can inspect and interact with it directly:
# SSH into the runner host
ssh runner-prod
# Navigate to the module's working directory
cd ~/.snapcd/runnerwd/<module-id>
# Look at what Snap CD prepared
ls -la
# main.tf
# variables.tf
# outputs.tf
# terraform.tfvars ← inputs from Snap CD
# plan.sh ← the plan command Snap CD would run
# apply.sh ← the apply command Snap CD would run
# Run a plan yourself
terraform init
terraform plan -var-file=terraform.tfvars
This is useful for debugging ("why is this plan showing a diff?"), for one-off operations (terraform import, terraform state mv), and for building confidence that nothing magical is happening behind the scenes.
Your code doesn't know about Snap CD
A Terraform module managed by Snap CD looks identical to one that isn't. Compare these two modules — one deployed by Snap CD, one deployed manually:
# modules/networking/main.tf — deployed by Snap CD
variable "environment" {
type = string
}
variable "cidr_block" {
type = string
}
resource "aws_vpc" "main" {
cidr_block = var.cidr_block
tags = {
Environment = var.environment
}
}
output "vpc_id" {
value = aws_vpc.main.id
}
# modules/networking/main.tf — deployed manually
variable "environment" {
type = string
}
variable "cidr_block" {
type = string
}
resource "aws_vpc" "main" {
cidr_block = var.cidr_block
tags = {
Environment = var.environment
}
}
output "vpc_id" {
value = aws_vpc.main.id
}
They're the same file. There's no snapcd {} block, no special provider, no metadata annotation. The Snap CD configuration — which runner to use, which inputs to provide, which outputs to wire — lives in Snap CD itself (typically managed via the Terraform provider for Snap CD), not in your infrastructure code.
Contrast with the alternatives
| Concern | Typical CD tool | Snap CD |
|---|---|---|
| How plans run | toolname plan or tool-managed wrapper |
terraform plan (standard binary) |
| Input delivery | Tool-specific config files or API injection | .tfvars, .env, shell scripts |
| State management | Often tool-managed custom backend | Your existing backend (S3, GCS, Azure, etc.) |
| Directory structure | Must follow tool's conventions | Any structure — Snap CD points at your repo |
| Debugging | Through the tool's UI/logs only | SSH to Runner, inspect files, run commands |
| Leaving the tool | State migration, code restructuring | Change nothing — your code already works standalone |
| Plan format dependency | Tool must parse each TF version's plan format | No plan parsing — Snap CD reads outputs, not plans |
When this matters
The value of non-invasiveness shows up in specific moments:
- Upgrading Terraform. You upgrade from 1.5 to 1.9. With Snap CD, you update the binary on your Runner and you're done. There's no intermediary that needs to understand the new plan format.
- Debugging a failed apply. Instead of reading logs through a web UI and guessing, you SSH into the Runner, look at the actual files, and run the command yourself to reproduce the error.
- Onboarding a new team member. They already know Terraform. They don't need to learn a wrapper CLI, a directory convention, or a configuration DSL. The Snap CD concepts (Modules, Stacks, Namespaces) are the orchestration layer — they don't change how Terraform itself works.
- Evaluating alternatives. If you decide Snap CD isn't the right fit, your Terraform code doesn't need to change. Your state files are where they've always been. You take your code and go.
Tips
- Don't fight the shell. If you need to run a one-off
terraform importorterraform state rm, do it directly on the Runner. Snap CD will pick up the state changes on the next plan cycle. - Use custom scripts when needed. If your Module needs a pre-step (downloading a dependency, running a linter, generating a file), put it in a script and reference it from your Module's configuration. Snap CD executes it as part of the standard flow.
- Keep your backends. Snap CD doesn't replace your state backend. If you're using S3 with DynamoDB locking, keep using it. If you're using Terraform Cloud for state only, that works too.
- Test locally first. Because your code has no Snap CD dependencies, you can always
terraform planon your laptop with a local.tfvarsfile before pushing. The same code, the same commands, the same result.
See also
- Modular Deployments — how the Module, Namespace, and Stack hierarchy works in detail
- Self-Hosted Terraform Runners with Credential Isolation — how Runners execute standard Terraform without wrappers
- Event-driven Continuous Deployment — how source watching and dependency cascading trigger deployments
- An Extensive Supporting Toolset — the Terraform provider and broader tooling ecosystem