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.
The working directory follows a predictable path: ~/.snapcd/runner/<stack>/<namespace>/<module>. If the Module specifies a subdirectory within the repo, the Runner navigates into it before running any commands.
2. Provide Inputs through standard mechanisms
Snap CD writes the Inputs your Module needs into a .snapcd subdirectory using formats that Terraform already understands:
-
inputs.tfvars— Terraform variables (values wired from other Modules' outputs or from static configuration). -
snapcd.env— environment variables your providers or scripts might need. -
Shell scripts (
init.sh,plan.sh,apply.sh, etc.) — wrap the Terraform commands with the correct flags and environment.
There's nothing proprietary about these files. The .tfvars file is a standard Terraform variable 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/runner/<stack>/<namespace>/<module>
# Look at what Snap CD prepared
ls -la
# main.tf
# variables.tf
# outputs.tf
# .snapcd/
# inputs.tfvars ← Inputs from Snap CD
# snapcd.env ← environment variables
# init.sh ← the init command Snap CD ran
# plan.sh ← the plan command Snap CD ran
# apply.sh ← the apply command Snap CD ran
# output.sh ← the output command Snap CD ran
# Run a plan yourself
terraform init
terraform plan -var-file=.snapcd/inputs.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. The .snapcd directory contains a .gitignore that excludes all its contents, so none of these generated files pollute your repository.
Your code doesn't know about Snap CD
A Terraform module managed by Snap CD is identical to one that isn't. There's no snapcd {} block, no special provider, no required metadata annotation. You won't find a single line in your .tf files that reveals which tool deploys them. If you searched a managed module for the string "snapcd", you'd get zero results.
All of the orchestration configuration — which Runner deploys the Module, which Inputs to provide, which Outputs to wire to downstream consumers — lives in Snap CD itself, typically managed via the Terraform Provider for Snap CD. Your infrastructure code stays portable: it works with Snap CD, without it, or with something else entirely.
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, environment variables, 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.
See also
- Introducing Snap CD — full architecture overview including Runner execution model
- 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
Top comments (0)