Back to Resources
DevOps & Platform Engineering

From Click-Ops to IaC: Migrating Cloud Infrastructure to Terraform Without Breaking Production

Most growing teams have cloud infrastructure that was clicked together in the console. Here is a structured approach to migrating to Terraform without taking down production.

Cloudvorn TeamApril 8, 20268 min readDevOps & Platform Engineering

Almost every growing engineering organization has the same story. You shipped fast to find product-market fit. Cloud resources were created through the AWS, GCP, or Azure console because that was the fastest path. Then more were added. Then more. And now you have a production environment that nobody fully understands, no version control, no audit trail, and no safe way to reproduce it.


This is what we call click-ops, and it is one of the most common reasons growing teams hit a wall.


The good news: you do not need to rebuild your platform from scratch to get out of click-ops. You need a structured migration to Infrastructure-as-Code (IaC), executed in a way that does not put production at risk.


Why move off click-ops at all


Before discussing the migration, it is worth being precise about why this matters:


  • No audit trail. When something changes and breaks, you cannot easily see who changed what, when, or why.
  • Environments drift. Dev, staging, and production diverge in subtle ways that surface as production-only bugs.
  • Onboarding is slow. New engineers cannot read the platform — they have to ask, click around, and reverse-engineer it.
  • Disaster recovery is theoretical. If you lose an account or region, you cannot recreate the environment cleanly.
  • Security and compliance audits are painful. Reviewers want to see how the infrastructure is configured. “Go to the console and look” is not an answer that holds up.

  • The migration approach that actually works


    Trying to import every existing resource into Terraform on day one is a recipe for outage. Here is the approach we use with Cloudvorn clients.


    1. Start with discovery, not coding


    Before writing a single line of Terraform, inventory what exists. Use 'aws-resources-export', 'gcloud asset export', or similar to dump current-state resources. Group them by criticality and rate of change. This is the single most undervalued step.


    2. Choose your tool deliberately


    Terraform is the default for good reason — it has the largest provider ecosystem and the deepest community. OpenTofu is the open-source fork worth considering if you want to avoid HashiCorp licensing concerns. Pulumi is excellent if your team would prefer real programming languages over HCL. AWS CDK is reasonable if you are AWS-only and want TypeScript or Python.


    Pick one. Do not mix.


    3. Codify new resources first


    The lowest-risk path is to start writing IaC for *new* resources from day one. Anything created from now on lives in version control. This stops the bleeding even before you migrate existing resources.


    4. Migrate one bounded surface at a time


    Pick something with low blast radius and low rate of change — IAM policies, S3 buckets, security groups. Use 'terraform import' to bring them under management. Verify the plan output is empty (i.e., your code matches reality). Commit. Move to the next surface.


    The rule: never migrate something you cannot afford to break. Save your highest-risk resources for last, after you have built migration muscle memory.


    5. Set up remote state with locking before you go further


    State files contain sensitive data and cannot be lost or corrupted. Use S3 + DynamoDB locking, GCS, or Terraform Cloud. Never check state files into git.


    6. Build CI/CD for plan and apply


    Manual 'terraform apply' from someone’s laptop is barely better than the console. Set up a pipeline that runs 'plan' on every pull request, requires approval, and runs 'apply' only after merge. This is where IaC starts paying compounding dividends.


    7. Document as you go


    Every module gets a README. Every architecture decision gets an ADR. Every non-obvious choice gets a comment. Future-you will thank present-you.


    Common pitfalls to avoid


  • Importing everything at once. You will hit edge cases that take weeks to resolve. Bite-sized migrations win.
  • Mixing tools. Some Terraform, some CDK, some console. Pick one and commit.
  • Ignoring secrets. Move them to a secrets manager *before* you start codifying infrastructure that depends on them.
  • No environment separation. If your IaC cannot distinguish dev from prod, you will eventually 'apply' to the wrong account.

  • How long does this take


    For a typical SME with a moderately complex AWS environment, a structured IaC migration runs 2–6 weeks depending on scope. The first 2 weeks usually cover discovery, tooling setup, and the first low-risk surfaces. The remaining time is spent migrating progressively more critical resources with confidence built up from earlier wins.


    If you want a fixed-price, structured approach to this migration, that is exactly what our [DevOps & Platform Engineering Foundation](/services#devops-foundation) engagement is built for. We come in with a proven methodology, do the migration alongside your team, and hand off documentation your engineers can maintain long after the engagement ends.


    The takeaway


    Click-ops is fixable. The trick is not heroics — it is sequencing. Start with discovery, codify new resources first, migrate bounded surfaces one at a time, and build CI/CD around plan/apply before you go deep. Done right, you end up with infrastructure your whole team can read, review, and operate — without a single minute of production downtime.

    Ready to Improve Your Reliability Posture?

    Book a free consultation to discuss how Cloudvorn can help your team build resilient, well-monitored systems.