hashicorp/terraform
Command package
Active contributors: terraform-core team.
Purpose
internal/command/ contains the per-subcommand handlers — PlanCommand, ApplyCommand, InitCommand, WorkspaceListCommand, etc. Each handler parses its own flags, builds a backendrun.Operation (for workflow commands) or directly performs its work (for plumbing commands), wires up a backend, and renders results through a view.
It is the largest package by file count: 245 non-test Go files. The size comes from three things — many subcommands, many sub-flags per command, and a deep view layer with both human and JSON renderings.
Directory layout
internal/command/
├── meta.go # command.Meta — shared dependency bag
├── meta_backend.go # backend selection / configuration / migration (114 KB)
├── meta_backend_migrate.go# state migration paths between backends
├── meta_config.go # loading + parsing the user's configuration
├── meta_dependencies.go # provider dependency resolution helpers
├── meta_providers.go # bridging command.Meta to internal/getproviders
├── meta_new.go # constructors for fresh metas (used in tests)
├── command.go # shared utilities for subcommands
├── apply.go, plan.go # the workflow commands
├── init.go, init_run.go # 50 KB total — the most complex command
├── refresh.go, validate.go# more workflow commands
├── show.go, output.go # post-plan readers
├── state*.go # state subcommands (list, mv, rm, pull, push, …)
├── workspace_*.go # workspace subcommands
├── providers*.go # providers subcommand and its sub-subcommands
├── login.go, logout.go # HCP Terraform credentials commands
├── stacks.go # `terraform stacks ...` wrapper
├── test.go, test_cleanup.go # native test runner
├── query.go # read-only resource queries (1.16+)
├── cloud.go, cloud_mock.go# cloud {} block plumbing
├── arguments/ # one file per command's flag-parser
├── views/ # one directory per output area; Human + JSON
├── format/ # diagnostic formatting and color
├── jsonplan/, jsonstate/, jsonconfig/, jsonchecks/, jsonprovider/, jsonfunction/, jsonformat/
├── junit/ # JUnit XML output for test results
├── webbrowser/ # native browser launcher used by `terraform login`
├── workdir/ # working-directory abstraction
├── cliconfig/ # ~/.terraformrc parsing
├── clistate/ # state lock UI helpers
├── e2etest/ # binary-level CLI tests
└── testdata/ # 192 sub-directories of fixture configurationsKey types
| Type | File | Description |
|---|---|---|
command.Meta |
meta.go |
Shared context every command embeds. ~30 KB; covers working dir, streams, view, services, plugin source, allow-experimental, shutdown channel. |
command.PlanCommand |
plan.go |
terraform plan handler. Parses flags, builds an Operation, dispatches to the backend, renders the plan. |
command.ApplyCommand |
apply.go |
terraform apply and terraform destroy (with Destroy: true). |
command.InitCommand |
init.go |
terraform init. Module installation, backend init, provider installation, lock-file maintenance. |
command.RefreshCommand |
refresh.go |
Standalone refresh (deprecated for plan/apply but still present). |
command.ValidateCommand |
validate.go |
Configuration validation without a backend. |
command.WorkspaceCommand |
workspace_command.go |
Workspace top-level help; subcommands handled in their own files. |
command.StateCommand |
state_command.go |
state top-level help. |
command.LoginCommand |
login.go |
HCP Terraform login flow. Drives the OAuth dance via webbrowser. |
command.LogoutCommand |
logout.go |
Companion to login. |
command.TestCommand |
test.go |
Native test runner; delegates most work to internal/moduletest. |
command.StacksCommand |
stacks.go |
terraform stacks ... umbrella for stack subcommands. |
command.ProvidersCommand |
providers.go |
providers summary; sub-subcommands: lock, mirror, schema. |
How it works
The shape of a workflow subcommand:
graph TD
Run[Run args] --> Args[arguments.ParsePlan / ParseApply / …]
Args --> Meta[command.Meta + view selection]
Meta --> Config[Meta.loadSingleModule / .loadConfig]
Config --> Backend[Meta.Backend select + configure]
Backend --> Op[Construct backendrun.Operation]
Op --> Run2[backend.Operation.RunOperation]
Run2 --> Local[local.Local wraps non-ops backends]
Local --> Ctx[terraform.Context.Plan / .Apply]
Ctx --> Result[*plans.Plan or *states.State]
Result --> Render[view.Plan / view.Apply]
Render --> Exit[diagnostic-aware exit code]For plumbing subcommands like state mv, the flow short-circuits: parse flags → load state via state manager → manipulate *states.State in memory → write it back. No graph walk, no providers.
Flag parsing
The newer subcommands use internal/command/arguments/ to centralize their flag definitions and produce typed argument structs (e.g. arguments.Plan, arguments.Apply). Older subcommands still use the standard flag.FlagSet pattern in their Run method. The codebase is migrating toward arguments/. A recent commit e24abdf6ff shows this migration in action: refactor: Update workspace select and delete subcommands to use the arguments package.
Backend selection (Meta.Backend)
meta_backend.go is the single largest non-test file in the repo (114 KB). It implements:
- Reading the
terraform { backend "..." {} }orcloud {}block from the configuration. - Reconciling that block with what was last initialized (stored in
.terraform/). - Configuring the chosen backend with merged values (from config +
-backend-configflags + previously stored values). - Detecting when the backend has changed and offering migration via
meta_backend_migrate.go. - Wrapping the chosen backend in
local.Localif the backend doesn't itself implementbackendrun.OperationsBackend.
The migration paths in meta_backend_migrate.go cover a small matrix (local→remote, remote→local, remote→remote, with or without workspaces) and are guarded by interactive prompts unless -input=false.
View layer
Every command writes user-facing output through a view. The pattern:
type Plan interface {
Operation() Operation
Plan(plan *plans.Plan, schemas *schemarepo.Schemas)
Diagnostics(diags tfdiags.Diagnostics)
HelpPrompt()
}
type PlanHuman struct { /* ... */ }
type PlanJSON struct { /* ... */ }The arguments.ViewType flag (set by -json) selects which implementation. Tests (in internal/command/views/...) typically use a fake view that records calls.
init_run.go and the shape of a long command
internal/command/init.go is 50 KB on its own. It coordinates an enormous matrix:
- Module installation (
internal/configs/configload→internal/initwd). - Backend selection (
Meta.Backend). - Provider installation (
Meta.providerInstaller). - Lock-file regeneration (
internal/depsfile). - Auto-detection of
cloud {}migration. - Optional plugin lock for hash-only verification.
The -upgrade, -reconfigure, -migrate-state, -input=false, -lockfile=…, and -plugin-dir=… flags interact across these steps and init.go's structure is dominated by their precedence rules.
Integration points
- Entrypoint: invoked from
commands.goat the repo root. Each subcommand factory closes over a singlecommand.Meta. - Configuration: loads via
internal/configs/configload(see configs). - Backends: registered via
internal/backend/init(see backends). - Engine: instantiates
terraform.Contextand callsPlan/Apply/Refresh/Validate/Eval(see terraform-core). - Provider install: uses
internal/getproviders(see getproviders). - State: reads/writes via
internal/states/statemgrandinternal/states/statefile. - Plans: writes plans via
internal/plans/planfile.
Sub-areas worth their own deep dive
internal/command/jsonformat/— the diff renderer that produces the colored, indented diff users see inplanoutput. Big enough to be its own package; uses a structural representation injsonformat/structured/and emits viajsonformat/computed/.internal/command/jsonplan/,jsonstate/,jsonconfig/,jsonchecks/,jsonprovider/,jsonfunction/— each defines a stable JSON schema for a particular Terraform artifact. Together these are the public API for-jsonoutput.internal/command/cliconfig/— the parser for~/.terraformrc(andterraform.rcon Windows) including credentials, host overrides, and theprovider_installationblock.internal/command/clistate/— UI for state-locking races (the "Acquiring state lock..." messages and timeout warnings).
Entry points for modification
- Adding a flag to an existing subcommand: edit the file in
internal/command/arguments/<command>.go(or theflag.FlagSetsetup in older commands) and the correspondingRunmethod. - Adding a new view output: extend the view interface in
internal/command/views/<command>.goand implement in both Human and JSON variants. Update fake views used by tests. - Adding a new top-level command: implement a new
command.<Name>Commandtype, register it incommands.go(see CLI dispatch). - Changing backend migration behavior:
internal/command/meta_backend_migrate.go. Be prepared to write extensive tests inmeta_backend_test.go.
For the JSON output formats specifically, treat them as a public API — their structure is not safe to change without a deprecation cycle.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.