A powerful wrapper for Terragrunt and Terraform that revolutionizes infrastructure as code management. Inspired by the simplicity and efficiency of Kubernetes, terra allows you to apply Terraform code with the ease of specifying paths directly in your commands.
- Seamless integration with Terraform and Terragrunt
- Intuitive command-line interface with path-based syntax
- Enhanced state management
- Simplified module path specification
- Cross-platform compatibility
- Non-interactive execution via
--yes/-y(or--no/-n) that maps to Terraform's-auto-approveand Terragrunt's--non-interactive-- no PTY pattern matching, works reliably withterraform apply - Self-update capability to automatically update terra to the latest version
- Version checking for Terra, Terraform, and Terragrunt dependencies
- Automatic dependency installation and management
- Support for AWS and Azure cloud provider switching
- Parallel execution for any command - Run any Terragrunt command across multiple modules simultaneously using the
--parallel=Nflag, where N is the number of concurrent threads. Use--only=mod1,mod2to select specific modules or--skip=mod3to exclude modules. Each worker's output is prefixed with its module name (e.g.[module-a]) and colorized per module on a terminal, so interleaved logs from concurrent modules stay attributable. - Centralized module and provider caching - Automatically configures
TG_DOWNLOAD_DIRandTG_PROVIDER_CACHE_DIRso Terragrunt modules and providers are downloaded once and reused across all stacks, repos, and terminals. Enables the Terragrunt Provider Cache Server (TG_PROVIDER_CACHE=1) for concurrent-safe provider deduplication with file locking, and pinsTG_NO_AUTO_PROVIDER_CACHE_DIR=trueso Terragrunt'sauto-provider-cache-dirfeature (auto-enabled alongside CAS) does not silently override the shared cache path. Override defaults withTERRA_MODULE_CACHE_DIRandTERRA_PROVIDER_CACHE_DIRenvironment variables. Disable the Provider Cache Server withTERRA_NO_PROVIDER_CACHE=true. - CAS (Content Addressable Store) - Terragrunt ships CAS as a stable, default-on feature since
1.1(it deduplicates Git clones via hard links for faster subsequent clones and reduced disk usage), so terra relies on that default instead of the retiredTG_EXPERIMENT=casopt-in. Disable withTERRA_NO_CAS=true, which sets Terragrunt'sTG_NO_CAS=true. - Partial Parse Config Cache - Enables Terragrunt's Partial Parse Config Cache by default (
TG_USE_PARTIAL_PARSE_CONFIG_CACHE=true), which caches parsed HCL configs across modules sharing the same root include for faster config parsing. Disable withTERRA_NO_PARTIAL_PARSE_CACHE=true. - Auto-initialization with upgrade detection - Automatically detects when terraform/terragrunt needs
init --upgrade(backend changes, provider conflicts, uninitialized modules) and runs it transparently before retrying the original command.
Install terra with a single command:
curl -fsSL https://raw.githubusercontent.com/rios0rios0/terra/main/install.sh | shOr using wget:
wget -qO- https://raw.githubusercontent.com/rios0rios0/terra/main/install.sh | shThe installer supports several options:
# Install specific version
curl -fsSL https://raw.githubusercontent.com/rios0rios0/terra/main/install.sh | sh -s -- --version v1.0.0
# Install to custom directory
curl -fsSL https://raw.githubusercontent.com/rios0rios0/terra/main/install.sh | sh -s -- --install-dir /usr/local/bin
# Show what would be installed without doing it
curl -fsSL https://raw.githubusercontent.com/rios0rios0/terra/main/install.sh | sh -s -- --dry-run
# Force reinstallation
curl -fsSL https://raw.githubusercontent.com/rios0rios0/terra/main/install.sh | sh -s -- --forcegit clone https://github.com/rios0rios0/terra.git
cd terra
make installDownload pre-built binaries from the releases page.
After installation, you can install Terraform and Terragrunt dependencies automatically:
terra installTo update terra itself to the latest version:
terra self-updateHere's how to use terra with Terraform/Terragrunt:
# it's going to apply all subdirectories inside "path"
terra apply --all /path
# it's going to plan all subdirectories inside "to"
terra plan --all /path/to
# it's going to plan just the "module" subdirectory inside "to"
terra plan --all /path/to/module
# or using Terraform approach, plan just the "module" subdirectory inside "to"
terra plan /path/to/module
# skip all confirmation prompts (apply/destroy use Terraform's -auto-approve)
terra apply --yes --all /path
terra -y plan --all /path/to
# run non-interactively and abort on any confirmation prompt (no auto-approve)
terra apply --no --all /path
terra -n plan --all /path/toclear Clear all cache and modules directories
format Format all files in the current directory
install Install or update Terraform and Terragrunt to the latest versions
update Install or update Terraform and Terragrunt to the latest versions (alias for install)
self-update Update terra to the latest version
version Show Terra, Terraform, and Terragrunt versionsTerra translates two terra-level confirmation flags into native Terraform and Terragrunt flags so CI/CD pipelines and parallel workers never get stuck on prompts:
| Flag | Short | Injected into the forwarded command |
|---|---|---|
--yes |
-y |
Terragrunt's --non-interactive + Terraform's -auto-approve (for apply / destroy) |
--no |
-n |
Terragrunt's --non-interactive only (Terraform's apply prompt aborts instead of proceeding, matching a "no" answer) |
No PTY, no regex-based prompt detection. The translation happens before the command is forwarded to Terragrunt, so the behavior is reliable across Terraform and Terragrunt versions.
# Non-interactive apply across a stack (Terraform's -auto-approve is injected automatically)
terra apply --yes /path
terra -y apply --all /path
terra apply --parallel=4 --yes /path
# Non-interactive execution that aborts on any confirmation prompt
terra plan --no --all /path
terra apply --parallel=4 --no /pathWhen using --parallel with apply / destroy, a confirmation flag is required because parallel workers cannot share stdin:
# ERROR: parallel workers cannot prompt
terra apply --parallel=4 /path
# CORRECT: inject native non-interactive flags
terra apply --parallel=4 --yes /pathThe legacy --reply and -r flags still work but emit a deprecation warning. They are translated to the same native flags as their --yes / --no equivalents:
| Legacy form | Mapped to |
|---|---|
--reply=y, -r=y, bare --reply, bare -r |
--yes |
--reply=n, -r=n |
--no |
Migrate scripts at your earliest convenience; --reply will be removed in a future release.
Terra provides two independent parallel execution strategies. They are not interchangeable -- each one owns its own set of filter flags, and mixing them produces a validation error.
Choosing a strategy:
- State operation across multiple modules from a root directory? → must use
--parallel=N. Terragrunt's--alldoes not support state commands. Single-module state commands (e.g.,terra state rm <addr> /path/to/one/module) still work without--parallel. - Need terragrunt DAG ordering /
dependenciesblock awareness? → must use--all. - Flat stack, want basename filtering? → either works;
--parallel=Nis simpler. - Need glob, graph, or git-diff filtering? → must use
--allwith terragrunt's--filter.
Terra-managed parallel (--parallel=N) -- terra discovers modules and runs N goroutine workers. Filter modules with terra's --only/--skip:
# Run init across all modules with 4 parallel threads
terra init --parallel=4 /path/to/infrastructure
# Select specific directories with --only
terra plan --parallel=4 --only=dev,staging,prod /path/to/infrastructure
# Skip specific directories with --skip
terra apply --parallel=4 --skip=test,backup /path/to/infrastructure
# State commands across a root with multiple modules use --parallel
# (single-module state commands can still be forwarded without --parallel)
terra import --parallel=4 null_resource.example resource-id /path/to/infrastructure
terra state rm --parallel=2 null_resource.example /path/to/infrastructureTerragrunt-managed parallel (--all) -- forwarded directly to terragrunt. Filter modules with terragrunt's --filter (preferred) or --queue-exclude-dir:
# Terragrunt's native run-all
terra apply --all /path/to/infrastructure
# Terragrunt's run-all with concurrency control
terra plan --all --parallelism=4 /path/to/infrastructure
# Terragrunt's run-all skipping a module with --filter (preferred)
terra apply --all --filter='!excluded-mod' /path/to/infrastructure
# Terragrunt's run-all skipping a module with the legacy flag
terra apply --all --queue-exclude-dir=excluded-mod /path/to/infrastructureFilter equivalence table -- use the column that matches your chosen strategy:
| Intent | With --parallel=N |
With --all |
|---|---|---|
| Skip one module | --skip=mod1 |
--filter='!mod1' |
| Skip multiple | --skip=mod1,mod2 |
--filter='!mod1' --filter='!mod2' |
| Only specific modules | --only=mod1,mod2 |
--filter='mod1' --filter='mod2' |
| Path glob | (not supported) | --filter='./prod/**' |
| Graph expression | (not supported) | --filter='service...' |
| Git-diff expression | (not supported) | --filter='[main...HEAD]' |
Note:
--paralleland--allcannot be used together -- they represent competing execution strategies. Similarly, terra's--only/--skiponly work with--parallel; passing them alongside--allproduces an educational validation error that shows the--filterequivalent for your command. In the reverse direction, terragrunt-owned flags (--filter,--queue-exclude-dir,--queue-include-dir) trigger a warning when combined with--parallel=Nbecause terra's worker pool silently ignores them.
For comprehensive documentation, see docs/parallel-execution.md. If you encounter Git clone errors (BUG: refs/files-backend.c) during parallel execution, see docs/parallel-git-clone-race.md.
terra versionThis displays Terra, Terraform, and Terragrunt versions.
# Interactive update (prompts for confirmation)
terra self-update
# Force update without prompts
terra self-update --force
# Dry run to see what would be updated
terra self-update --dry-run# Install dependencies (prompts for updates if newer versions available)
terra install
# Alternative command (alias for install)
terra updateTerra can be configured with environment variables for cloud provider integration. Create a .env file in your project root:
# Optional: Cloud provider (if specified, must be "aws" or "azure")
TERRA_CLOUD=aws
# AWS specific (required for role switching when using AWS)
TERRA_AWS_ROLE_ARN=arn:aws:iam::123456789012:role/terraform-role
# Azure specific (required for subscription switching when using Azure)
TERRA_AZURE_SUBSCRIPTION_ID=12345678-1234-1234-1234-123456789012
# Optional: Terraform workspace
TERRA_WORKSPACE=dev
# Optional: Terraform variables (any TF_VAR_* variables)
TF_VAR_environment=development
TF_VAR_region=us-west-2
# Optional: Centralized cache directories (defaults shown below)
# TERRA_MODULE_CACHE_DIR=~/.cache/terra/modules
# TERRA_PROVIDER_CACHE_DIR=~/.cache/terra/providers
# Optional: Disable Terragrunt CAS (Content Addressable Store) experiment
# TERRA_NO_CAS=true
# Optional: Disable Terragrunt Partial Parse Config Cache
# TERRA_NO_PARTIAL_PARSE_CACHE=true
# Optional: Disable automatic workspace selection from TERRA_WORKSPACE
# TERRA_NO_WORKSPACE=true
# Optional: Override the per-download deadline that `terra install`
# applies to the Terraform / Terragrunt fetch (default 10 minutes).
# Useful when slower transports (corporate proxies, low-bandwidth
# links, QEMU-emulated multi-arch container builds) push the
# Terragrunt download past the default. Accepts any value parseable
# by `time.ParseDuration` -- e.g. `30m`, `1h`, `20m30s`. Malformed
# or non-positive values fall back to the default and log a warning.
# TERRA_DOWNLOAD_TIMEOUT=30mNote: If TERRA_CLOUD is specified, it must be set to either "aws" or "azure". This enables cloud-specific features like role switching for AWS or subscription switching for Azure.
If you have some input variables, you can use environment variables (.env) with the prefix TF_VAR_:
# .env example for Terraform variables
TF_VAR_foo=bar
# command (that depends on the environment variable called "foo")
terra apply --all /path/to/moduleMore about it in:
- Notice that Windows has
pathsize limitations (256 characters). If you are using WSL interoperability (calling.exefiles inside WSL), you could have errors like this:That means you exceeded the/mnt/c/WINDOWS/system32/notepad.exe: Invalid argument
pathsize limitation on the currentpathyou are running the command. To avoid this issue, move your infrastructure project to a shorterpath, closer to your "/home" directory, for example.
Provider caching strategy comparison using terragrunt init with azurerm provider v4.42.0 on a single module:
| Strategy | Cold cache | Warm cache (median) |
|---|---|---|
| No cache | 12.4s | 12.4s |
TF_PLUGIN_CACHE_DIR only |
11.9s | 8.9s |
TG_PROVIDER_CACHE only |
11.8s | 10.6s |
| Both combined | 11.4s | 9.5s |
| Strategy | Shared cache | Per module | Total (N modules) |
|---|---|---|---|
| No cache | 0 | 238 MB (full copy) | 238 MB x N |
TF_PLUGIN_CACHE_DIR only |
219 MB | 19 MB (symlink) | 219 MB + 19 MB x N |
TG_PROVIDER_CACHE only |
219 MB | 19 MB (symlink) | 219 MB + 19 MB x N |
| Both combined | 219 MB | 19 MB (symlink) | 219 MB + 19 MB x N |
While TF_PLUGIN_CACHE_DIR provides slightly better single-module warm-cache performance (8.9s vs 10.6s), it causes "text file busy" (ETXTBSY) errors during parallel execution (--parallel=N) because Terraform creates symlinks without file locking. Terra uses TG_PROVIDER_CACHE (Provider Cache Server) by default because it serializes provider downloads with file locking, making it safe for concurrent access from parallel goroutines. Disable with TERRA_NO_PROVIDER_CACHE=true.
Contributions are welcome. See CONTRIBUTING.md for guidelines.
terra is released under the MIT License. See the LICENSE file for more details.