Skip to content
willowark

Every environment in version control, every change reviewable

Infrastructure as code means your servers, networks, databases, and permissions are defined in files, reviewed like software, and applied by machines. It removes the environment nobody can rebuild, the change nobody remembers making, and the Friday-afternoon console edit that takes production down.

Willowark works primarily with Terraform and OpenTofu, plus Pulumi where a general-purpose language earns its place, and CloudFormation or Bicep where a platform-native fit is required. We structure code into modules with clear interfaces, keep state remote and locked, and wire plans into pull requests so every change is visible before it applies.

Illustrative: a quiet server room with blue status lightsCloud & Infrastructure

How the work gets done

The same way every time: scope, build, hand over.

The real work is more than writing HCL. Existing resources have to be imported without disruption, which means careful state surgery on live systems. Modules need boundaries that match how teams actually own things. Secrets have to come from a vault, not the repository. And drift detection has to run continuously, because the console is always one hurried afternoon away from diverging from the code.

When it is working, infrastructure changes go through the same motions as application changes: branch, plan, review, apply. A reviewer sees exactly which resources will change before approval. A new region or a full disaster-recovery environment is a variable file and a pipeline run, and an auditor's question about who changed a security group has a commit hash for an answer.

Scoping starts with an inventory: which accounts, which resources, and who owns them. We then decide the order of adoption — usually foundational resources like networks and IAM first, then the workloads that change most often, since those benefit most from review. The trade-offs are about structure: how coarse or fine the modules should be, whether to split state per environment or per service, and how much to abstract before the team has felt the pain that justifies it. Over-engineered module hierarchies are a common failure, so we start flat and refactor when duplication makes the case.

The most frequent problem after adoption is a pipeline that is slower or scarier than the console, which quietly pushes people back to clicking. We design against that with fast plans, small state files, and clear plan output in the pull request, so review takes minutes. State corruption and lock contention are the other classic failures; remote state with locking, versioned backups, and a documented recovery procedure address them before they happen. Handover means your engineers have written and applied changes themselves under our review, the modules are documented, and there is a runbook for the situations that require manual intervention.

  1. Scope it in writing

    What we agree before work starts

    • Terraform or OpenTofu codebase with reusable modules and remote state
    • Import of existing infrastructure into managed state
  2. Build with checkpoints

    Working results, not slide decks

    • Plan-and-apply pipeline integrated with pull request review
    • Drift detection and scheduled compliance checks
  3. Hand over something you own

    Documentation, source, and training

    • Module documentation and team training on the workflow
    • State recovery and emergency-change runbook for situations where the pipeline cannot be used

Sound familiar?

Where infrastructure as code earns its keep.

A production environment built by hand over three years that no one can reproduce

Two cloud accounts that were supposed to be identical and are not

A compliance audit asking for change history the console cannot provide

A disaster-recovery plan that assumes someone will rebuild everything from memory

Common questions

Asked before every infrastructure as code project.

Terraform, OpenTofu, or Pulumi — which should we use?

For most teams, Terraform or OpenTofu: the ecosystem is mature and hiring for it is easy. Pulumi makes sense when your infrastructure logic is genuinely complex and your team is strongest in TypeScript, Python, or Go. We recommend one after seeing your stack, not before.

Can you bring existing infrastructure under IaC without downtime?

Yes — importing resources into state is a read-and-record operation, not a rebuild. We import incrementally, verify each plan shows no unintended changes, and only then allow applies. Live systems stay untouched until the code provably matches reality.

What happens when someone changes something in the console anyway?

Drift detection catches it: scheduled plans compare reality against code and flag divergence. The fix is either reverting the console change or codifying it — and over time, making the pipeline faster than the console is what actually ends the habit.

How do you handle secrets in infrastructure code?

They never go in the repository. Secrets are stored in a vault or the cloud provider's secrets service and referenced by name from the code, with the value fetched at apply time. Where a provider must generate a secret — a database password, for example — we write it directly to the secrets store rather than into state, or encrypt state at rest and restrict who can read it. We also scan commits for accidental leaks as a pipeline gate.

Our team has never used Terraform. How steep is the learning curve?

Reading and reviewing Terraform is learned in days; writing modules well takes longer. We structure the codebase so that the common changes — adding an environment, resizing an instance, opening a port — are edits to variables rather than new modules, which is what most engineers need day to day. Training is part of the engagement: pairing on real changes, a written workflow guide, and a review period where we check pull requests before they merge.

Where this sits

Infrastructure as Code, inside a cloud & infrastructure system.

The lit component is the part of the system this service delivers; the rest is what it has to work with.

Plant to cloud, and what watches itVPNMQTTSQLeventspushPlant networkTunnelVPN / private linkServicescontainersData storesDB, objectsMonitoringlogs, metricsOn-call

Hover or focus a component to see what it is and what it talks to. Arrow keys move between them.

Plant systems reach the cloud over an encrypted tunnel; services run in containers over managed data stores, and monitoring alerts a person before a customer notices.

Components:

  1. Plant network: OT network, segmented from IT.
  2. Tunnel (VPN / private link): Encrypted, allow-listed, no inbound ports on the plant side.
  3. Services (containers): Deployed from CI, rolled back in one command.
  4. Data stores (DB, objects): Managed database and object storage with backups tested, not assumed.
  5. Monitoring (logs, metrics): Health, latency, error rate, cost.
  6. On-call: Someone is paged before a customer emails.

Connections:

  • Plant network to Tunnel over VPN
  • Tunnel to Services over MQTT
  • Services to Data stores over SQL
  • Services to Monitoring over events
  • Monitoring to On-call over push
A typical architecture, drawn to explain the pattern — not a specific client's system.

Strategy. Software. Systems.

Have a system that should exist?

Tell us what your operation is doing manually, what isn't connected, or what you're trying to build. We'll tell you plainly whether and how we can help.