Terranova is a thin wrapper for Terraform that provides extra tools and logic to handle Terraform configurations at scale.
Note: This is a fork of elastic/terranova maintained and developed by techcode.io.
See the original repository for the upstream source.
- Ability to share terraform configuration without modules.
- Ability to define arbitrary resource layout.
- Ability to auto-generate documentation using metadata attached to resource definition.
- Ability to execute runbooks to interact with resources.
- Ability to import variables between resource group.
- Ability to run commands across resource groups in parallel, honoring dependency order.
- We needed a way to manage resources as code at scale.
- The solution should leverage terraform to avoid re-implementing the wheel.
- The solution shouldn't leverage the terraform configuration DSL to add features since it can change.
The following steps will ensure your project is cloned properly.
- Clone repository:
git clone https://github.com/techcode-io/terranova cd terranova - Install dependencies and setup environment:
uv sync uv run poe env:configure
- To lint you have to use the workflow.
uv run poe lint- It will lint the project code using
pylint.
- To format you have to use the workflow.
uv run poe fmt- It will format the project code using
blackandisort.
- To run Claude Code against this repository without exposing your machine, use the workflow.
uv run poe claude:sandbox- It builds (or reuses) the
.devcontainerimage withpodmanordockerand starts Claude Code inside it with permission prompts skipped: the container is the boundary. - The container engine is the first of
podmananddockerfound on thePATH; setCONTAINER_ENGINEto force one. - Outbound network access is restricted by an egress firewall to an allowlist of hosts (package registries, VCS, Anthropic services). IPv6 is disabled.
- The workspace is writable, but
.devcontainer,.claude,.git/hooksand.git/configare mounted read-only, since they are executed on the host. - Claude Code auth, the
uvcache and the shell history persist across rebuilds in named volumes. - If an IDE with the Claude Code plugin has the project open, its selection and diagnostics context is bridged into the sandbox through a host-side relay on
127.0.0.1:41337. The IDE auth token never enters the container. - The container is recreated automatically when
.devcontainerchanges. - The same
.devcontainercan also be opened directly from VS Code.
curl -fsSL https://raw.githubusercontent.com/techcode-io/terranova/main/contrib/install.sh | sh
# Pin a version
curl -fsSL https://raw.githubusercontent.com/techcode-io/terranova/main/contrib/install.sh | sh -s -- --version 0.7.2The script installs the .deb/.rpm package on Linux (under /opt/terranova, symlinked
into /usr/bin/terranova) and the tarball on macOS. See contrib/install.sh --help for
--prefix and --bin-dir.
terranovarely on the concept of resource groups.- You can define as many resource groups as you want.
- The base layout should contain the directory
resourcesandshared. - The
resourcesdirectory contains resource groups. - The
shareddirectory contains any sharable resource that will be symlink if defined as dependency. - A resource group is defined when a
manifest.ymlis present. - By default,
terranovawill look for aconfdirectory in the working directory that contain both above directories.
conf
βββ resources
β βββ resource_group_1
β β βββ runbooks
β β β βββ pyinfra.py
β β βββ main.tf
β β βββ manifest.yml
β βββ resource_group_2
β βββ main.tf
β βββ manifest.yml
βββ shared
βββ providers
β βββ github.tf
βββ config.tf
terranovawill rely on the layout to apply change using terraform.- In the above case, running
terranova apply resource_group_1will runterraformon resources present in that directory. terranovasupports any depth within the layout.- This allows you to reflect any structure.
- Create a new directory in
conf/resources. - Create a new
manifest.ymlfile with the following content.
version: "1.2"
metadata:
name: Terranova Hello World
description: Hello World
url: https://github.com/techcode-io/terranova
contact: mailto:adrien.mannocci@gmail.com- Define any resource using standard
terraformconfiguration. - Add metadata on each resource to allow auto-generate documentation.
/*
@attr-name attr-value
*/
data "null_data_source" "values" {
inputs = {}
}
/*
@attr-name attr-value
*/
resource "null_resource" "foobar" {}- You can now run
terranova init <resource_group_name>andterranova apply <resource_group_name>.
- In some case, we need to share common terraform configuration or scripts across many resource group.
- It's possible to define dependencies in the manifest and symlink them in any resource group.
- Those common resources should be defined in the
shareddirectory. - All symlink are maintained by
terranovaand are updated when theterranova initcommand is run.
# Supported since 1.0 manifest version.
---
dependencies:
- source: providers/github.tf # Which file or directory to symlink.
target: 00-github-provider.tf # Where to symlink the file or directory.- In some case, we need to interact with terraform resources using specific tooling.
- It's possible to define a runbook in the manifest and invoke arbitrary tools.
- It's also possible to interact with
terranovato extract information usingoutputs.
# Supported since 1.1 manifest version.
---
runbooks:
- name: "<runbook_name>" # Used as argument in the command
entrypoint: "<tool_entrypoint>" # Tool to invoke
workdir: "<working_directory>" # Optional: Used to navigate in sub-directories.
args:
- <arguments> # List of arguments to pass
env:
- name: PATH # Inherit environment value
- name: FOO # Override or define environment value
value: bar- In some case, we need to interact across many resource groups and need to import variables from a resource group to another one.
- It's possible to define imports in the manifest.
# Supported since 1.2 manifest version.
---
imports:
- from: "<resource_group_path>" # Relative resource group path
import: "<output_variable>" # Name of the output variable to import
as: "<working_directory>" # Optional: Name of the input variable to map to.- By default,
terraformis looked up on thePATH. - It's possible to pin the version per resource group in the manifest.
- An exact version is downloaded from
releases.hashicorp.com, verified against the published SHA-256 checksums and cached in~/.terranova/engines/terraform/<version>/. latestis looked up on HashiCorp's checkpoint API at most once every 24 hours (the answer is kept in~/.terranova/engines/terraform/.latest.json), so runs stay consistent and keep working offline with a previously installed version.- With
planandapply, distinct versions are downloaded in parallel before any resource group runs.
# Supported since 1.4 manifest version.
---
version: "1.4"
engine:
name: terraform # Only `terraform` is supported.
version: "1.9.5" # Exact version, `latest` for the newest stable one, or `system` to use the `PATH` lookup.- Downloads are available for Linux, macOS and Windows (amd64 and arm64).
- Runbooks of that resource group also get the pinned binary first on their
PATH. Without anengineblock, or withsystem, they keep the systemPATH.
- By default,
terranovaruns with--strategy sequential: one resource group after another. plan,apply,fmtandvalidatealso accept--strategy parallelto run independent resource groups concurrently instead.- Use
--group-concurrency <n>to cap how many resource groups run at once under--strategy parallel(defaults to a sane pool size if unset). This is distinct from--parallelism, which limits terraform's own resource-level concurrency inside a single invocation. - Use
--fail-at-endto let unaffected resource groups keep running after a failure instead of stopping the whole run immediately.
terranova plan --strategy parallel --group-concurrency 4
terranova apply --strategy parallel --auto-approve --fail-at-end- For
planandapply, execution order still honors dependencies declared through manifestimports: resource groups are grouped into topological "waves", where every group in a wave has all its dependencies satisfied by earlier waves.--strategy parallelruns a wave's groups concurrently and waits for the whole wave to finish before starting the next one;--strategy sequentialruns the same waves flattened, one group at a time. A cyclicimportschain is rejected with an error before anything runs. fmtandvalidatedon't resolveimports, so there's no dependency ordering to respect: every discovered resource group is treated as a single wave and is safe to run concurrently with every other.apply --strategy parallelrequires--auto-approve(or applying a saved.tnplanfile): running severalterraform applyprocesses at once means none of them can fall back to an interactive approval prompt.
plan,apply,destroyanddocsaccept--auto-scope(-A) to only target the resource groups affected by your current git changes, instead of passing an explicitpath.- The changes considered are the working tree and staged changes compared to
HEAD, plus untracked files (respecting.gitignore). - Each changed file is mapped to its nearest ancestor directory containing a
manifest.yml; a change outside any resource group is ignored, and several changes in one group select it only once. - It can't be combined with an explicit
path(or, forapply, a.tnplanfile), and it must be run from inside a git repository. - With
docs, the docs directory isn't wiped first, so documentation of groups outside the scope is kept.
terranova plan --auto-scope
terranova apply -A --auto-approve- To apply exactly what was planned, combine it with
plan --out: the saved.tnplanfile only contains the scoped resource groups, andapplytakes its targets from that file. Don't pass--auto-scopetoapplyin that case, it can't be combined with a plan file.
terranova plan --auto-scope --out changes.tnplan
terranova apply changes.tnplan- Run the following command
terranova docs.
- Run the following command
terranova apply <path>.
- Run the following command
terranova import <path> <resource_address> <identifier>. - The
importterraform command is used under the hood.
If you find this project useful here's how you can help, please click the ποΈ Watch button to avoid missing notifications about new versions, and give it a π GitHub Star!
You can also contribute by:
- Sending a Pull Request with your awesome new features and bug fixed.
- Be part of the community and help resolve Issues.
The terranova project is free and open-source software licensed under the Apache-2.0 license.