# From Click-Ops to Code: A Practical Path to Terraform Without Stopping Delivery

Canonical: https://kaansystems.com/library/click-ops-to-terraform
Topic: Terraform · Published: 2026-10-16 · Updated: 2026-10-16
Summary: Most teams don't choose click-ops; it accumulates. This is a five-step path out (inventory, foundation, import in slices, gate every change, watch for drift) that keeps feature work shipping the whole time.

## Key takeaways

- Inventory before you import, and rank the import list by blast radius: networking and identity first, then data stores, then everything else.
- Make Terraform itself safe before importing: remote state with locking, pinned provider and module versions, and no secrets in state or code.
- Import one component at a time with import blocks, and treat a zero-change plan as proof the code matches reality.
- Plan-on-PR with applies from the pipeline is the step that turns Terraform from documentation into control.
- A scheduled drift check closes the loop, so console changes surface in days, not at the next outage.

**How do you move an existing cloud environment into Terraform without stopping delivery?** Inventory what exists, make Terraform itself safe, import one component at a time until each plan shows zero changes, then gate every change behind plan-on-PR and watch for drift. No freeze, no rewrite.

Most software teams don't choose click-ops. It happens. Someone needed a queue on a Friday, an incident needed a firewall rule at 2 a.m., and the first engineer built the database by hand because shipping mattered more. Two years later, part of the platform is in Terraform and part of it only exists in the console.

This post is a practical path out, written for teams with no dedicated platform lead.

## Why it matters now

Click-ops costs show up at the worst moments: when the person who built it leaves, when you need a second environment fast, or when an enterprise customer's security review asks how infrastructure changes are approved. Code answers all three. A screenshot answers none.

There's also a quieter cost. When nobody is sure what's running, every change feels risky, so changes slow down. Engineers stop touching the network because "it works." Upgrades get postponed. The platform starts setting the pace of the product instead of the other way around.

The good news: you don't need a quarter-long migration project to fix it. You need a sequence that lets you make progress in small, safe steps while the rest of the team keeps shipping.

## Step 1: Inventory before you import

List what exists by account or subscription, region and environment. Tag what is already managed by Terraform. Everything else goes on the import list, ranked by blast radius: networking and identity first, then data stores, then everything else.

A few practical notes:

- **Use the cloud's own inventory.** AWS Config or Resource Explorer, Azure Resource Graph and GCP Cloud Asset Inventory will each give you a list faster than clicking through consoles.
- **Compare against state.** `terraform state list` across your existing workspaces tells you what's already managed. The difference between the two lists is your click-ops footprint.
- **Ignore what the cloud manages for you.** Service-linked roles, default resources and auto-generated items usually don't belong in your code. Note them and move on.

If you run more than one AWS account, this is a good moment to look at whether your account structure itself needs work; our guide to [building a multi-account AWS landing zone](/library/multi-account-aws-landing-zone) covers that, and [do you actually need an AWS landing zone?](/library/when-do-you-need-aws-landing-zone) helps you decide whether now is the time.

## Step 2: Fix the foundation first

Before importing anything, make Terraform itself safe:

- **Remote state with locking**, one state per environment and component. One giant state file means every plan touches everything and every lock blocks everyone.
- **Pinned provider and module versions.** An unpinned provider turns a routine apply into a surprise upgrade.
- **No secrets in state or code.** Use your cloud's secret store and reference secrets at runtime. Assume state will be read by more people than you expect.
- **Workload identity for CI.** Your pipeline should authenticate to the cloud with short-lived, federated credentials, not a long-lived access key stored in a CI variable. We cover the pattern in [killing long-lived credentials](/library/killing-long-lived-credentials).

This step is unglamorous and it's where most "partial Terraform" setups fall down. Skip it and every import that follows inherits the problems.

## Step 3: Import in small slices

Use `import` blocks to bring existing resources under management one component at a time. After each import, run a plan and aim for zero changes. A clean plan is your proof the code matches reality.

How to keep slices small:

- **One component per pull request.** "The production VPC and its subnets" is a good slice. "Production" is not.
- **Generate, then clean up.** Recent Terraform versions can generate starter configuration for imported resources. Treat that output as a first draft: rename things, remove defaults you don't need, and move repeated patterns into modules later.
- **Don't refactor while importing.** Get the resource under management exactly as it is. Improve it in a separate change, where the plan shows precisely what will move.
- **Stop on any unexpected diff.** If the plan wants to change or replace something right after import, the code is wrong, not the cloud. Fix the code until the plan is clean.

Delivery keeps going throughout. Each slice is a small, reviewable change that doesn't touch running traffic.

[Book a call →](https://cal.com/benrubert/30min?utm_source=library&utm_medium=article&utm_campaign=click-ops-to-terraform&utm_content=middle)

## Step 4: Gate every change

Add plan-on-PR in CI so every change is reviewed as a diff before it applies. Apply from the pipeline, not from laptops. This is the step that turns Terraform from documentation into control, and it's where change records start creating themselves.

What a good gate looks like:

- **Every pull request posts its plan** as a comment or check, so reviewers see what will actually change, not just what the code says.
- **Applies run from the pipeline** after approval and merge, using the federated identity from Step 2.
- **Console write access shrinks.** Once a component is in code, humans keep read access and a documented break-glass path for emergencies. Anything done through break-glass goes back into code afterward.
- **Policy checks run alongside the plan.** Tools such as Checkov or tfsec catch common misconfigurations before they merge.

Each merged pull request now records who proposed a change, who approved it, what the plan was and when it applied. That's the raw material for answering "how are infrastructure changes approved?" without a single screenshot. If you're curious how far that goes, [evidence pipelines 101](/library/evidence-pipelines-101) walks through it.

## Step 5: Watch for drift

Schedule a drift check (a plan against each environment) and send the result somewhere people read. Drift will happen; the goal is to see it within days, not at the next outage.

The rule for every finding is simple: codify it or revert it. If the console change was right, bring it into code. If it wasn't, let the next apply put things back. Not every difference deserves a page, though. Our guide to [detecting infrastructure drift without drowning in alerts](/library/infrastructure-drift-detection) shows how to tier drift so the dangerous changes get attention and the noise doesn't.

## How long does this take?

It depends on how much lives outside code, how many environments you run, and how much engineering time you can give it each week. A team with one cloud account and a handful of services is in a very different place from a team with several accounts, a Kubernetes cluster and years of manual changes. The inventory in Step 1 is what turns "it depends" into a real plan.

## What you get at the end

New environments in hours rather than weeks, reviewable changes, and a repo that answers "how is this configured and who approved it?" That last one is why we say: audit evidence included.

You also get a platform that more than one person can safely change. That matters most for teams without a senior platform lead, because it means the knowledge lives in the repo instead of in one engineer's memory.

## Where to start

If you're not sure how much of your platform lives outside code, our **Terraform & Platform Assessment** maps it in 7 business days with read-only access: Terraform state and modules, drift, CI/CD, Kubernetes, IAM and network baseline, and cost quick wins, plus a one-page Evidence Map and a 90-day roadmap. Credited 100% toward a retainer signed within 60 days. No Terraform yet? The Assessment becomes a click-ops-to-code review on the same 7-day, read-only scope. [Book the Terraform & Platform Assessment](https://cal.com/benrubert/30min?utm_source=library&utm_medium=article&utm_campaign=click-ops-to-terraform&utm_content=close).

**Related reading:** [Detecting infrastructure drift](/library/infrastructure-drift-detection) · [Building a multi-account AWS landing zone](/library/multi-account-aws-landing-zone) · [Killing long-lived credentials](/library/killing-long-lived-credentials) · [Evidence pipelines 101](/library/evidence-pipelines-101)

## Frequently asked questions

**Q: Do we have to freeze feature work to move to Terraform?**

No. Import one component at a time, aim for a zero-change plan after each import, and leave everything else alone. Delivery keeps going while the managed share of the platform grows.

**Q: Should we rewrite our infrastructure from scratch in Terraform instead of importing it?**

Usually not. Rebuilding means migrating data and traffic, which is where outages happen. Importing what already runs puts it under control without moving it; refactor into cleaner modules later, behind plan-on-PR.

**Q: What should we import first?**

Rank by blast radius. Networking and identity (VPCs or VNets, security groups or firewall rules, IAM roles and policies) come first because a mistake there affects everything, then data stores, then compute and the long tail.

**Q: How do we know the import worked?**

Run a plan right after it. A plan that shows no changes means the code describes what's actually running. Any change in that plan is a mismatch to fix in code before you move on.

Source: https://kaansystems.com/library/click-ops-to-terraform · Work with Kaan Systems: https://kaansystems.com/contact
