hashicorp/terraform
HCP Terraform / Cloud integration
Active contributors: terraform-core team, tf-core-cloud.
Purpose
Two coordinated subsystems implement Terraform's relationship with HCP Terraform (formerly Terraform Cloud):
internal/cloud/— the integration that backs thecloud {}configuration block.internal/backend/remote/— the older backend that backsterraform { backend "remote" {} }.
Both speak to HCP Terraform via the hashicorp/go-tfe client and present the same OperationsBackend interface to the rest of Terraform Core. They share an enormous amount of code; you'll see commits that touch both directories together.
Why two
The remote backend is the original (2018-era) integration with Terraform Enterprise / Terraform Cloud. The cloud {} block is the newer (2021+) opt-in form, simpler to configure and with first-class support for the terraform login flow. The team gradually migrated functionality and docs to cloud {} while keeping remote for backward compatibility. As of 1.16-dev, both are still supported; the remote backend is increasingly thin and the cloud integration carries most of the active development.
Directory layout
internal/cloud/
├── README.md
├── cli.go # CLI integration helpers
├── backend.go # 54 KB — the OperationsBackend implementation
├── backend_apply.go # apply-specific handling (run polling, log streaming)
├── backend_plan.go # plan-specific handling
├── backend_query.go # query (1.16) handling
├── backend_show.go # show against remote run
├── backend_context.go # build a backend context for a run
├── backend_common.go # shared helpers
├── backend_taskStages.go # pre/post task stages
├── backend_taskStage_policyEvaluation.go # policy enforcement (Sentinel/OPA)
├── backend_taskStage_taskResults.go # third-party run tasks
├── backend_run_warning.go # warnings to display when run is queued
├── cloud_integration.go # coordinating with the cloud block
├── cloud_run.go # run-creation logic
├── cloud_variables.go # variable handling
├── cloudplan/ # cloud-stored plan handling
├── e2e/ # acceptance tests
├── errors.go, retry.go # error mapping and retry
├── migration.go # state migration into HCP Terraform
├── state.go # statemgr.Full implementation backed by HCP
├── test.go # test integration
├── testing.go, tfe_client_mock.go # test scaffolding (~70 KB mock)
└── testdata/ # 46 sub-directories of fixtures
internal/backend/remote/
└── (similar structure, older)Key types
| Type | File | Description |
|---|---|---|
cloud.Cloud |
internal/cloud/backend.go |
Implements Backend and OperationsBackend for the cloud {} block. |
cloud.Cloud.Operation |
cloud_run.go |
Creates a run on HCP Terraform, polls for completion, streams logs back to the local CLI view. |
cloud.RemoteState |
internal/cloud/state.go |
statemgr.Full implementation that reads/writes state via HCP API. |
remote.Remote |
internal/backend/remote/backend.go |
The older remote backend; mirrors cloud.Cloud's shape. |
How it works
sequenceDiagram
autonumber
actor User
participant CLI as command.PlanCommand
participant Cloud as cloud.Cloud
participant TFE as HCP Terraform API
participant Run as Remote Run
User->>CLI: terraform plan
CLI->>Cloud: Operation(ctx, op)
Cloud->>TFE: CreateRun(workspace, configVersion, planOnly=true)
TFE-->>Cloud: Run handle
Cloud->>TFE: Subscribe to run events
loop poll until terminal
Cloud->>TFE: GetRun(id)
TFE-->>Cloud: status, plan ID
TFE-->>Cloud: log stream chunks
Cloud->>CLI: forward via view
end
alt task stages configured
TFE->>Cloud: pre-plan task stage results
Cloud->>CLI: render
TFE->>Cloud: post-plan policy results
Cloud->>CLI: render
end
Cloud-->>CLI: Operation completed (or errored)
CLI-->>User: exitOperation lifecycle
The cloud.Cloud.Operation method (in cloud_run.go) is the centerpiece:
- Configuration version. A tarball of the working directory is uploaded as a "configuration version" so HCP Terraform has the source.
- Run creation. A new run is created against the target workspace, with parameters reflecting the local CLI options (auto-apply, plan-only, refresh-only, target lists, replace lists, variables).
- Polling. The run is polled for status changes. Logs are streamed and printed to the local terminal so the user sees the same output as if it ran locally.
- Task stages. HCP Terraform supports pre-plan, post-plan, pre-apply, and post-apply task stages — extension points where third-party services (Sentinel, OPA, run-task webhooks) can gate the run.
backend_taskStages.goand the_policyEvaluation/_taskResultsfiles render their results. - Apply. If applicable, the user is prompted to confirm and the apply runs remotely.
- State. State is fetched from HCP Terraform after the run completes (or, for plan-only runs, the plan itself is retrieved and saved locally).
Variables
cloud_variables.go reconciles three sources of variables: the user's local .tfvars files, -var flags, and workspace-level variables stored in HCP Terraform. The reconciliation rules are subtle — workspace-level vs CLI-provided values have different precedences depending on whether the workspace is in "remote" or "local" execution mode.
Tests with HCP Terraform
The test runner (test.go in internal/cloud/) integrates with HCP Terraform's "test runs" feature. When cloud {} is configured and the user runs terraform test, the runs can be executed remotely with the workspace's saved variables and credentials.
Locks and lineage
cloud.RemoteState implements optimistic locking via HCP Terraform's API. The state's serial and lineage are used the same way as in the local file format — every write increments the serial; lineage UUIDs detect "different state on the other end."
Migration
migration.go handles the user's first transition into HCP Terraform: copying their local state up, mapping local workspaces onto HCP workspaces, and (optionally) deleting the original local file.
Configuration block
A typical cloud {} block:
terraform {
cloud {
organization = "my-org"
workspaces {
name = "production-network"
}
}
}The matching parser is in internal/configs/cloud.go. Multiple-workspaces variants use workspaces { tags = ["env:prod"] } to select multiple workspaces by tag (used with terraform workspace select).
Login flow
terraform login (internal/command/login.go) opens a browser, has the user authenticate, and writes a token to ~/.terraform.d/credentials.tfrc.json. The token is consumed by svchost/auth (HashiCorp's service-host authentication library) when the cloud integration makes API calls. terraform logout revokes the token.
The browser-launching is wrapped in internal/command/webbrowser/.
Integration points
- CLI: the
cloudsubcommand (internal/command/cloud.go) is enabled whenmeta.AllowExperimentalFeaturesis true; it's the developer-facing surface for the integration. Day-to-day, users interact via thecloud {}block plus standard subcommands. - Backends: registered via
internal/backend/init/init.go. Theremoteentry points atinternal/backend/remote.Newand thecloudintegration is constructed via theCloudfield oncommand.Meta. - State:
cloud.RemoteStateis astatemgr.Fulllike any other. - Engine: when run remotely, the engine doesn't run locally at all — HCP Terraform spins up its own Terraform Core. The local CLI only orchestrates and renders.
Entry points for modification
- Adding support for a new HCP Terraform feature: usually a new method on
cloud.Cloudplus updates tocloud_run.goto surface the result through the CLI. - Adding a CLI flag that should be forwarded to HCP runs:
cloud_run.gobuilds theRunCreateOptions; add the field there and in the matching argument struct. - Changing the migration flow:
internal/cloud/migration.goandinternal/command/meta_backend_migrate.go.
For HCP Terraform-only behaviors (Sentinel, OPA), see HCP Terraform's own documentation; this codebase only consumes their results.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.