Infrastructure automation on one open source orchestrator
Wrap the Terraform or OpenTofu modules, Ansible playbooks and scripts you already have in flows with approvals, retries and audit logs. Provision on vSphere, Kubernetes or cloud, then run the Day 2 jobs from the same place.
Infrastructure automation ends up in five places
Terraform runs from a CI pipeline, playbooks from AWX, the catalog lives in vRA or ServiceNow, patching is a cron job on a jump host, and the cleanup scripts sit in someone's home directory. Each tool has its own credentials, its own logs and its own idea of who is allowed to run what. Answering "who changed this VM, and who approved it" means opening four of them.
Windmill does not replace Terraform, OpenTofu or Ansible: it runs them. A flow takes typed inputs, calls each tool as a step, pauses for approval where policy says so, retries what is safe to retry, and keeps the inputs, logs and approver of every run. The same platform runs the schedules, the event-driven remediation and the self-service forms, so there is one place to look.
OpenTofu and Terraform runs with shared inputs and secrets
Windmill has no proprietary Terraform step: a script calls the tofu or terraform CLI on a worker where the binary is installed, through a custom image or preinstalled binaries. Credentials come from resources as typed arguments and reach the CLI as environment variables. State stays in the backend you choose, and the plan and apply output stream to the run log.
def main(
action: Literal["apply", "destroy"],
vm_name: str,
vsphere: c_vsphere, # resource, picked in the form
tf_state: postgresql, # pg backend, one workspace per VM
cpus: int = 2,
memory_mb: int = 4096,
dry_run: bool = False,
):
env = {
"PG_CONN_STR": _pg_conn_str(tf_state),
"TF_VAR_vsphere_password": vsphere["password"],
...
}
_run("tofu", ["init", "-input=false"], env)
_run("tofu", ["workspace", "select", "-or-create", vm_name], env)
_run("tofu", ["apply", "-auto-approve"], env)Ansible playbooks as a native script language
A playbook is a script in Windmill. A short header declares its inputs, inventories, files and Galaxy collections; Windmill generates the form, installs the dependencies, writes SSH keys from secrets with the right mode, and passes the inputs as --extra-vars. Write result.json and the next step of the flow gets a structured result.
---
extra_vars:
host_ip: { type: string }
hostname: { type: string }
files:
- variable: f/infra/ssh_private_key
target: ./ssh_key
mode: '0600'
dependencies:
galaxy:
collections:
- name: community.general
---
- hosts: localhost
tasks:
- add_host:
name: "{{ hostname }}"
ansible_host: "{{ host_ip }}"
groups: new_vm
- hosts: new_vm
become: true
tasks: ...Approvals and self-service requests
A flow's input schema becomes a form with enums, patterns and defaults, which is the catalog item. An approval step suspends the run until someone approves from the run page, a resume link, Slack or Teams, and a condition decides when it applies: prod only, large sizes only. For a richer front end, build an app on top of the same flow. Approval forms and approver restrictions are Cloud and Enterprise features.
Learn more about generated forms
The ticket lifecycle, closed by the flow
Keep the catalog in ServiceNow if that is where requesters live. A business rule calls the flow's webhook or HTTP route, a preprocessor maps the request to typed inputs, and the last step writes the result back on the RITM. When a step fails, the flow's failure handler rolls back and puts the error on the ticket.
See the provisioning flow
export async function preprocessor(event: Event) {
const body = event.body ?? {};
const v = body.variables ?? body;
return {
vm_name: String(v.vm_name).toLowerCase(),
size: v.size ?? "small",
os: v.os ?? "ubuntu-24.04",
environment: v.environment ?? "dev",
ticket: body.number, // RITM0010001
};
}Run the job next to the infrastructure
Tag a script vsphere-dc1 and only the workers in that data center pick it up. For segments that should not open inbound ports, agent workers connect out to Windmill over HTTP and never touch its database. When the only way in is a jump host, SSH execution reroutes a Bash script over SSH. Agent workers and SSH execution are Enterprise features; worker tags are in every edition.
- · Terraform / OpenTofu + vSphere
- · pyvmomi
- · Ansible to DC1 VMs
- · kubectl / Helm
- · Cluster maintenance
- · Namespace audits
- · Agent worker
- · Outbound HTTP only
- · No DB access
- · PowerShell / PowerCLI
- · Native Windows worker
- · Active Directory
Day 2 operations
Provisioning is one request. Patching, cleanup and compliance run every night for years. Each of these is a script or flow on a schedule or a trigger, with its own run history and error handler.
Ansible or PowerShell across hosts, a canary first, then batches. The first failed batch stops the run before the rest of the fleet.
Nightly sweeps for old snapshots, orphaned disks and powered-off VMs, with a dry run until you trust them.
Certificate expiry, drift against a source of truth, credential rotation. A failed check is a failed job, which reaches your error handler.
Point an Alertmanager, Datadog or PagerDuty webhook at a flow: restart the service, scale the pool, or open a ticket and page a human.
Start from working flows
The windmill-vmware-vra-replacement repository holds the flows on this page as files you push with wmill sync push: VM provisioning and decommissioning on vSphere, rolling patching, the snapshot sweeper and the certificate check. Each flow has a dry_run input, so you can walk it before pointing it at vCenter.
git clone https://github.com/windmill-labs/windmill-vmware-vra-replacement
cd windmill-vmware-vra-replacement
wmill workspace add infra <workspace_id> https://windmill.example.com/
wmill sync push
wmill flow preview f/infra/rolling_patch__flow \
-d '{"hosts":["web-01","web-02","web-03"],"require_approval":false,"dry_run":true}'Works with the tools you already run
Anything with a CLI, an SDK or an API is reachable from a script in Python, TypeScript, Go, Bash, PowerShell or another supported language.
OpenTofu or Terraform for lifecycle, pyvmomi or PowerCLI for everything else.
Playbooks as a native script language, with Galaxy collections and inventories as resources.
Run the CLI from a script on workers where the binary is installed. State stays in your backend.
kubectl, Helm and client libraries from Bash, Python or TypeScript. Windmill itself runs on Kubernetes.
SDKs in any language, OIDC tokens instead of long-lived keys.
Resource type with basic auth or OAuth. Catalog requests start flows, flows close tickets.
NetBox resource type for IP allocation, device lookup and inventory reconciliation.
Vault, Azure Key Vault or AWS Secrets Manager as the secret backend on Enterprise.
Any API or CLI the worker can reach, from code you already have.
Built for how infrastructure teams work
Every script, flow, schedule and trigger is a file. Sync with the CLI or Git sync, review in pull requests, promote between workspaces.
Who ran what, with which inputs, who approved it. 365 days of retention on Enterprise, 14 on Community Edition.
Folders, groups and per-resource permissions. Operators run the flow without ever reading the vCenter password.
Encrypted per workspace and injected at run time. Vault, Azure Key Vault or AWS Secrets Manager as backends on Enterprise.
Per-step retries with backoff, flow failure handlers for rollback, workspace and schedule error handlers for alerting.
Every run keeps its inputs, logs, per-step results and duration, searchable and replayable.
Docker Compose, Kubernetes or bare binaries in your data center, including air-gapped networks.
Expose flows to AI agents and MCP clients, or let Claude Code write scripts with the Windmill CLI skills.
Moving off vRA, vRO, Rundeck or cron
Most infrastructure teams come to Windmill to replace a workflow engine, not their hypervisor. vRO workflows become flows, JavaScript actions become TypeScript scripts with few changes, configuration elements become resources, and packages become a Git repository. The guide covers what moves, what stays, and how to size the migration.
Read the vRA replacement guideMore you can build on Windmill
The same platform runs data pipelines, internal apps, AI agents and Windows automation.
Write scripts in TypeScript, Python, Go, Bash, SQL and trigger them from webhooks, schedules, queues or the auto-generated UI.
Build production-grade internal apps with backend scripts, data tables and React, Vue or Svelte frontends.
Orchestrate ETL jobs with parallel branches, DuckDB queries and connections to any database or S3 bucket.
Build AI agents with tool-calling, DAG orchestration, sandboxes and direct access to your scripts and resources.
Chain scripts into flows with approval steps, parallel branches, loops and conditional logic.
Run cron jobs with a visual builder, execution history, error handlers, recovery handlers and alerting.
Run PowerShell, MSSQL with Kerberos and C# natively on domain-joined Windows servers, no Docker required.
Frequently asked questions
Put your infrastructure automation in one place
Self-host Windmill, push the example flows, and run the first provisioning request in dry run.