Skip to main content

Manage the Terraform CLI Config from atmos.yaml

· 3 min read
Erik Osterman
Founder @ Cloud Posse

Atmos can now manage the Terraform/OpenTofu runtime configuration (RC) for you. Declare it once under components.terraform.rc, and Atmos writes a temporary CLI config file and points the subprocess at it via TF_CLI_CONFIG_FILE and TOFU_CLI_CONFIG_FILE — no hand-managed dotfiles, no manual env exports.

What is the "RC" file?​

"RC" is runtime configuration — the same convention as .bashrc or .npmrc. Terraform and OpenTofu read a CLI configuration file (named .terraformrc / terraform.rc for Terraform and .tofurc / .terraformrc for OpenTofu) that controls how the CLI itself behaves — distinct from your .tf code, which describes infrastructure. It is where you configure things like:

  • provider_installation — where providers come from: network mirrors, filesystem mirrors, or going direct to registries (and include/exclude rules for supply-chain control).
  • host — service-discovery overrides, e.g. redirecting a registry's modules.v1 endpoint.
  • credentials — API tokens for private registries.
  • plugin_cache_dir — a shared provider plugin cache.

This file is normally a hand-edited dotfile in your home directory, pointed to by the TF_CLI_CONFIG_FILE environment variable. Atmos now lets you declare it per stack/component instead.

What changed​

Until now there was no way to declare this runtime configuration in Atmos — you had to hand-author a .terraformrc and export TF_CLI_CONFIG_FILE yourself. Now you declare it in your stack configuration:

components:
terraform:
rc:
enabled: true
# Rendered verbatim into Terraform's native CLI configuration.
provider_installation:
- network_mirror:
url: "https://terraform-mirror.example.com/"
- direct:
exclude:
- "registry.terraform.io/hashicorp/*"
host:
"registry.terraform.io":
services:
"modules.v1": "https://modules.example.com/v1/modules/"

Atmos renders that into the native HCL CLI-config grammar and exposes it to terraform/tofu automatically. No Terraform code, provider declarations, or module sources change.

How it works​

When components.terraform.rc.enabled is set, then for each terraform/tofu invocation Atmos:

  1. Renders the rc: section into Terraform's native CLI-config (HCL) grammar — preserving block semantics like the ordered provider_installation methods and labeled host/credentials blocks.
  2. Writes it to a temporary file with an atomic write (so a partial file is never observed).
  3. Exposes it to the subprocess by setting both TF_CLI_CONFIG_FILE and TOFU_CLI_CONFIG_FILE to that file.
  4. Cleans up the temp file after the whole pipeline finishes — it survives init, workspace, and plan/apply within a single invocation.

If you already manage your own CLI config via either env var (or the legacy TERRAFORM_CONFIG), Atmos detects it and renders nothing, deferring to you.

A passthrough, not a new abstraction​

The rc: section is near-opaque: keys map directly to Terraform CLI-config directives (provider_installation, host, credentials, plugin_cache_dir, …). New directives work immediately without waiting for an Atmos release, because Atmos renders the section rather than modeling it.

Terraform and OpenTofu, no guessing​

Atmos sets both TF_CLI_CONFIG_FILE and TOFU_CLI_CONFIG_FILE to the generated file, so your config is honored whether the resolved binary is terraform or tofu — no heuristic about which tool you're running. If you already manage your own CLI config (via either env var or the legacy TERRAFORM_CONFIG), Atmos detects it and defers to you.

The foundation for the registry cache​

RC management is useful on its own, and it's also the injection point the new Terraform Registry Cache builds on — the cache contributes its network_mirror and module host directives into this same generated CLI config. Enable the cache and the RC plumbing is driven for you.

Get involved​

This feature is experimental. Try declaring a components.terraform.rc block in a stack and inspect the generated config Terraform receives. Feedback welcome.

For usage and configuration, see Terraform Configuration.