Infrastructure Automation

A GitOps control plane for OpenTofu: Git-synced Projects and Stacks, a plan-then-approve-then-apply workflow, and an encrypted secrets vault.

Infrastructure Automation provisions and configures real infrastructure from code you keep in Git: OpenTofu for provisioning, Ansible for configuration management. It's available on the Enterprise plan, given the blast radius of a feature that can create, destroy, and reconfigure real infrastructure.

This feature holds live cloud provider credentials and executes against real infrastructure, the same category of risk as [Autonomous Remediation](/ai-ops/autonomous-remediation), and its guardrails (mandatory plan review, project-scoped access) are equally non-negotiable.

What it does

  • Projects: the top-level container, pointed at a Git repository (branch), with an optional credential for private repos. A Project holds one or more Stacks, each a subdirectory of either OpenTofu (.tf/.tofu) code or an Ansible playbook and inventory.
  • Vaults & Secrets: every Project can hold multiple named vaults. A secret's value is encrypted (AES-256-GCM) the moment you save it, and is never returned by any API call again, not even to you. Saving a new value for the same name creates a new version rather than overwriting the old one, so history is preserved. A dedicated ssh_key secret type holds the private key Ansible uses to connect to target hosts.
  • Variables: non-secret key/value pairs, either project-wide or scoped to a specific stack. For an OpenTofu stack, variables and secrets are injected as TF_VAR_<name> environment variables. For an Ansible stack, they're passed as --extra-vars. Both are matched by name, so there's no separate mapping step to configure.
  • Runs: an OpenTofu stack is acted on via plan, apply, destroy, or refresh. An Ansible stack is acted on via check (a --check --diff dry run) or run (the real playbook execution). Every run streams its live output to a log viewer on the run's own page.
  • Targets: a Stack can optionally be linked, for display purposes, to a cluster or host you already monitor elsewhere in KubeWatch, so its dashboard page shows what it manages at a glance. This link is purely informational. What actually decides what a Stack provisions or connects to is its own OpenTofu provider configuration or Ansible inventory.

The plan-then-approve-then-apply workflow

This is the one guardrail that can't be turned off, and it applies the same way to both engines: applying a real change always requires a specific, already-succeeded dry-run on the same stack that a human has explicitly approved. There's no way to skip straight to a real change nobody has reviewed. For OpenTofu, apply and destroy each require an approved plan run on the same stack. Destroy specifically requires a plan_destroy run (a plan that shows what would be deleted), since an ordinary plan run can never authorize a destroy. For Ansible, run requires an approved check run (Ansible's own --check --diff dry run) on the same stack.

In practice: click Run plan (or Run destroy plan for OpenTofu, Run check for Ansible), wait for it to succeed, review its output in the log viewer, then click Approve. Only then does Apply, Destroy, or Run appear, and only on that specific dry-run, pre-filled with the right reference, never asking you to type a run ID by hand. Destroying anything additionally requires typing the stack's name to confirm, since it's the one action here that can't be undone.

Today, approving a dry run is a judgment call based on its logged output. There's no structured diff view yet, and the real run re-executes against whatever the Git ref resolves to at execution time rather than replaying the exact reviewed dry run byte-for-byte. If the underlying code changes between approval and execution, review the dry run again first. Closing this gap needs a persisted plan artifact carried between runs, which is on the roadmap. An approved dry run can also only ever be used once: applying, destroying, or running with it consumes it, so a stale approval can't be replayed later against code that's since changed.
Ansible connects to target hosts with host key checking disabled, since a freshly-provisioned host has no entry in a known-hosts file yet and there's no mechanism today to seed one. This is a deliberate, documented tradeoff rather than an oversight, so treat the SSH private key you store for it with the same care as any other credential here.

Access control

Every Project has its own membership list, independent of your organization's broader role assignments. Being a member of the org does not automatically grant access to a Project's Stacks, Vaults, or secrets. Three roles, each a strict superset of the one below it:

  • Viewer: see everything, run nothing.
  • Operator: create/run Stacks, manage Vaults and Variables, approve plans.
  • Admin: everything Operator can do, plus manage Project settings and membership. Every Project always keeps at least one Admin, so removing the last one, or demoting them, is blocked.

Every mutation (creating a Stack, saving a secret, approving a plan, running a destroy) is written to the same append-only audit trail every other privileged action in this platform uses.

Private repositories

A Project can attach a Git credential from its own Vaults (an api_token secret, used as an HTTPS access token) under its Edit panel. Once attached, every clone for that Project's Stacks authenticates with it automatically. Only HTTPS token auth is supported. SSH-based Git authentication isn't.

A Stack doesn't have to start from code already sitting in your repo. The blueprint gallery, opened from a Project's Stacks tab, is a catalog of 50 built-in starter templates across provisioning, Kubernetes, databases, messaging, networking, security, observability, and CI/CD, split evenly between OpenTofu and Ansible. Each entry shows its description, suggested variables (with sensible defaults for ordinary config, and clearly flagged names you should add as Vault secrets for anything sensitive), and the exact file content it would create.

Picking one and clicking Use this blueprint does more than the rest of this feature ever does elsewhere: it creates the Stack, then pushes the blueprint's files as a new commit directly to your Project's own Git repository. Every other action here only ever reads your repo, cloning it to plan, apply, check, or run. This is the one path that writes to it, authored under a generic automation identity rather than a real person's. Because of that, it needs a Git credential with push access attached to the Project, not just the read access cloning already required. Attaching a read-only token works for everything else in this feature but will fail here with a clear error telling you to grant it more.

A blueprint's files never contain a real secret value. Anything sensitive is left as a reference (an OpenTofu variable, an Ansible `{{ }}` placeholder) for you to fill in as a Vault secret with the matching name, resolved only when a run actually executes, never committed to Git.

What it doesn't do yet

  • SSH-based Git authentication isn't supported, only HTTPS with an optional token credential. (This is separate from the ssh_key secret type above, which is for Ansible's connection to its target hosts, not for cloning the Project's own repo.)
  • Custom, org-authored blueprints aren't supported yet: the gallery is currently the 50 built-in templates only, not a place to save your own Stack as a reusable one.