Hand-built cloud to Terraform or OpenTofu
Cohash brings hand-built cloud estates under infrastructure as code. We start by inventorying what actually exists in AWS, Azure or Google Cloud. Then we import it into Terraform or OpenTofu with import blocks, without rebuilding anything, and split state by team and environment. Every change moves into pull requests with plan review and policy checks. Once that works, console write access goes away for everyone except a break-glass role.
- 01Inventory and ownership
- 02Module and state layout
- 03Import without rebuild
- 04Pipelines and policy
- 05Lock the console
The story
Told without the client's name or anything that could identify them.
A logistics company's platform team ran production that had been built in the cloud console, documented only by an old wiki page. An audit question about who changed a security group was answered from CloudTrail, rebuilding a test environment meant repeating every click by hand, and the bits of Terraform that existed had drifted so far that nobody ran apply. We inventoried resources by account with owners, flagged the ownerless ones for retirement, and agreed module boundaries and a state layout per team and environment. Existing resources were brought in with import blocks until each plan showed no changes, with nothing rebuilt. Changes now go through pull requests with plan review and policy checks, and console write access is limited to a logged break-glass role.
why it was urgentTerraform 1.16 came out on 26 August 2026 and allows import blocks inside modules. That makes bulk imports of existing resources far less painful. Licensing is the other reason teams are looking again. HashiCorp moved Terraform to the Business Source License in August 2023, IBM closed its purchase of HashiCorp on 27 February 2025, and many teams now choose between Terraform and the OpenTofu fork at the same time.
- openSomebody built production in the console. What's written down is a wiki page from two years ago.
- recurringAn audit asked who changed a security group and the answer came from CloudTrail, not a pull request.
- blockedRebuilding a test environment takes a week of clicking.
- openSome Terraform exists, but drift is so large that nobody runs apply anymore.
The estate, before and after
Before: production built by hand in the console
Engineers changed AWS, Azure and Google Cloud resources directly in the console with broad rights. A small amount of Terraform existed, but drift meant nobody ran apply, and the only change record was the cloud audit log.
Read the diagram as text
- Engineers → Cloud console: click changes
- Cloud console → Cloud resources: create · modify
- Drifted Terraform → Cloud resources: rare apply
- Cloud resources → Audit logs: change events
- Engineers → Wiki page: notes
After: every change goes through a pipeline
Terraform or OpenTofu changes go through pull requests, where we review the plan and run policy checks before apply. The pipeline gets into the cloud through OIDC federation, and drift detection flags whatever changed outside code.
Read the diagram as text
- Engineers → Module and state repos: pull request
- Module and state repos → Terraform or OpenTofu CI: plan
- Terraform or OpenTofu CI → Policy checks: check plan
- Terraform or OpenTofu CI → Cloud resources: OIDC apply
- Terraform or OpenTofu CI → Remote state: read · lock
- Drift detection → Cloud resources: compare
- Break-glass role → Cloud resources: emergency change
How we run it, phase by phase.
5 phases take you from Console-built cloud resources to Terraform or OpenTofu in pipelines. Each one ends with something you can check before the next starts. As you scroll, the blocks show the estate moving across.
- phase 01
Inventory and ownership
Go through each account and subscription and list its resources, with tags, creators and last change. Flag the ones nobody owns for retirement, before anyone writes code for them.
- phase 02
Module and state layout
Agree module boundaries (network, identity, data, application stacks) and a state layout per environment and team, so one plan never touches everything.
- phase 03
Import without rebuild
Import blocks plus the provider's config generation give us a first cut of the configuration. We tweak it until plan comes back with no changes. At no point is anything destroyed and recreated to get it into code.
- phase 04
Pipelines and policy
Plan on pull request, apply on merge, with policy checks for public storage, open security groups, missing tags and broad IAM. Pipeline credentials come from OIDC federation, not long-lived keys.
- phase 05
Lock the console
Remove console write access for day-to-day roles, keep a logged break-glass path, and run scheduled drift detection so changes outside code get caught.
What the client got
What changed for the client once the work was done. Described, not scored: we don't publish client figures.
- result 01
Every change has a pull request
Plan runs on every pull request and apply runs on merge. If someone asks who changed a security group, the answer is a reviewed commit, and nobody has to dig through CloudTrail.
- result 02
Existing resources are in code, unchanged
Everything came in through import blocks and generated configuration. No stack was applied until it showed a zero-change plan and a person had read that plan.
- result 03
Policy checks run before apply
Public storage, open security groups, missing tags and overly broad IAM now get caught in the pipeline. And the pipeline signs in to the cloud with OIDC federation instead of long-lived keys.
- result 04
The console is read-only for daily work
Day-to-day roles lost console write access. A logged break-glass path remains, and scheduled drift detection catches changes made outside code.
- result 05
State is split and protected
We split state per environment and team, and one plan can't reach across all of it any more. Remote state is encrypted, locked and readable by pipelines alone.
Risks we plan for
-
Import that quietly changes resources
One wrong default in a generated config and the first apply replaces your database. That's why each import finishes with a zero-change plan that a person reads.
-
State with secrets in it
Terraform state can hold passwords and keys. We keep remote state encrypted and locked, readable by pipelines only.
-
Choosing a tool on licence alone
OpenTofu and Terraform have diverged on newer features. The choice is checked against the features and HCP Terraform integrations you actually use.
Where AI helps, and where it needs guardrails
HashiCorp's Terraform MCP server lets an assistant look up provider docs and modules. Turn on operations and it can manage HCP Terraform workspaces as well. We use AI to draft import configs and module refactors, and plan review catches its mistakes. We keep operations off by default, and assistants only ever get read-only cloud credentials. Apply rights stay with people.
forward deployed engineers →What you're left with
We write these for your setup, and they're working documents. Your team should be able to keep things running from them long after we've gone.
- inventory01
Resource inventory with owner and retire-or-keep decision
yours to keep - code02
Module library and state layout per environment
yours to keep - plan03
Imported configuration with a zero-change plan per stack
yours to keep - code04
CI pipeline with policy checks and OIDC cloud credentials
yours to keep - plan05
Drift detection schedule and break-glass procedure
yours to keep
Platforms and services on this move
Services involved
Certified engineers, wherever you need them.
Some clients want a project team, some want a couple of engineers on theirs. Either works. Onsite, nearshore or offshore, with handovers across time zones that keep things moving.
Onsite
Engineers at your sites in the US
Nearshore
Overlapping hours with US teams
Offshore
Overnight progress on your backlog
All time zones
Coverage handed over across time zones
01Terraform or OpenTofu?
It depends on your legal team's view of the Business Source License and whether you rely on HCP Terraform or the newest Terraform-only features. Most code moves between them with small changes. We review your usage and tooling before recommending either.
02Do we have to rebuild resources to get them into code?
No. Import blocks bring existing resources into state. We only rebuild something when its current setup is wrong enough to fix anyway, and we schedule that separately.
03How long does it take?
It depends on resource count, how many accounts or subscriptions you run and how far existing Terraform has drifted. We import one representative stack first and size the rest from that.
04Can you also set up the landing zone?
Yes, and a lot of teams do both at once. New accounts get vended from code while the old ones are being imported.
Often on the same roadmap
On-premises Active Directory to cloud-first Microsoft Entra ID
We shrink on-premises Active Directory one application at a time, moving groups, users and app authentication to Microsoft Entra ID as each app is ready.
read the case studyAD FS federation to Entra ID authentication
Converting federated domains from AD FS to Entra ID cloud authentication, moving relying parties app by app and retiring the AD FS farm.
read the case studyApache Airflow 2 to Airflow 3
Upgrading self-managed Apache Airflow 2, now past end of life, to Airflow 3, and fixing removed features, auth and scheduling changes before DAGs move.
read the case study22fd4601 - 01GitHub: Terraform v1.16.0 releasegithub.com ↗
- 02HashiCorp: HashiCorp adopts Business Source Licensehashicorp.com ↗
- 03IBM: IBM completes acquisition of HashiCorp (27 Feb 2025)newsroom.ibm.com ↗
- 04OpenTofuopentofu.org ↗
- 05GitHub: HashiCorp Terraform MCP servergithub.com ↗
Planning this move?
A 30-minute working session with a senior engineer. No slides.
- The platform and the version you're on
- Where you need to get to, and by when
- What's custom: rules, plugins, integrations
- Any audit finding or deadline driving it