DEV Community

Cover image for Why Snap CD: Non-invasive Orchestration
Karl Schriek
Karl Schriek

Posted on • Originally published at snapcd.io

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.

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
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)