{
  "schema": "aaes/capability-pack/1",
  "provider": "terraform",
  "version": "1.0.0",
  "description": "HCP Terraform and Terraform Enterprise: read workspaces and runs; create workspaces with automation; queue, apply or discard runs that may deploy or destroy resources; and force-delete workspaces.",
  "notes": "The endpoint host 'terraform-cloud.invalid' is a reserved non-production placeholder (an RFC 2606 reserved TLD): substitute the customer's HCP Terraform host (app.terraform.io) or Terraform Enterprise hostname before enabling, in every endpoint, in the resource locator and in every egress allowlist entry. The allowlist matches exactly, so endpoints and allowlist must be substituted together, and substitution is a required deployment step. ORG, WORKSPACE_ID and RUN_ID in the endpoint paths are placeholders the deployment binds before enabling.\n\nPer the retrieved API overview the API is version 2 under the /api/v2 prefix and speaks JSON:API (Content-Type application/vnd.api+json), POST and PATCH endpoints take a JSON:API document as the request payload, and list endpoints paginate with page[number]/page[size] query parameters. Token types: according to the reviewed overview, ORGANIZATION tokens cannot perform plans and applies, so the run create/apply/discard capabilities require a user or team token, and forbidden requests with a valid token return 404 rather than 403 — a capability that inexplicably 404s is a permissions finding, not an absence of the resource.\n\nApproval classification: run creation registers as create, deploy AND delete at the minimum R3 tier, judged by effect — according to the reviewed Runs reference a run performs a plan and apply, and the caller-controlled body can set auto-apply (apply without a further confirmation step), is-destroy=true (the plan destroys ALL provisioned resources in the workspace) and target/replace addresses — so an unrestricted body can both deploy infrastructure change and destroy the whole estate a workspace manages; deploy and delete are designated actions requiring human approval and an installer's intention to queue only plan-only runs is not a restriction.\n\nApplying a run registers as approve, deploy AND delete at the minimum R3 tier: the apply action confirms a run paused for confirmation and queues the apply of the reviewed plan — it is precisely the approval moment — and the plan being applied may itself destroy managed resources, so delete is declared as well. Discarding a run registers as delete at the minimum R3 tier: discard is terminal (the run reaches its discarded final state and later runs can proceed), and queuing a fresh run afterward is a new run, not a reversal of the discard.\n\nWorkspace creation registers as create AND deploy at the minimum R3 tier: according to the reviewed Workspaces reference the unrestricted body can link a VCS repository with automatic run triggering, speculative plans and auto-apply, and those VCS-triggered executions run provider-side — they need not traverse terraform.run.create or terraform.run.apply or any other AAES capability. Workspace deletion registers as delete at the minimum R3 tier: according to the reviewed Workspaces reference, DELETE /workspaces/{id} FORCE-deletes the workspace WITHOUT first checking for managed resources — destroying nothing itself but orphaning real infrastructure whose management record is gone; the checking form (POST.../actions/safe-delete, which refuses while resources are managed) is deliberately not registered, so this pack's only workspace deletion is the force form and the description says so.\n\nDocumented exclusions — this pack does not register: state versions and state version outputs (state routinely contains cleartext secrets, and hosted-state download URLs are themselves bearer-secret URLs according to the reviewed overview), variable creation/update (writing variables injects configuration, including sensitive values, into future runs), configuration-version upload, run force-execute and force-cancel (according to the reviewed Runs reference, force-cancel warns of potentially dangerous side effects including loss of in-flight state), policy check overrides, workspace lock/unlock/force-unlock, team and organization membership management, agent pools and agent tokens, SSH keys, organization tokens and audit-trails tokens, and every other HCP Terraform API — an excluded operation.",
  "credential": {
    "secret_ref_id": "secret-ref-terraform-api-token",
    "custody": "customer_vault",
    "inject": "header",
    "inject_name": "Authorization",
    "note": "An HCP Terraform API token for the customer's own organization, held in the client's own secret manager. The stored material must be the complete Authorization header value: the scheme 'Bearer', one separating space, then the token — 'Bearer <token>'.\n\nIssuance is external and three token types exist according to the reviewed API overview: user tokens (each user can have any number, created on the user's account settings page), team tokens (created in the team's settings and intended for plans and applies via CI/CD; according to the reviewed token-management documentation the CURRENT team token API allows multiple valid tokens per team when their descriptions are unique — only legacy tokens are one-at-a-time) and organization tokens (one per organization, intended for automating team and workspace management — and per the same reference, organization tokens CANNOT perform plans and applies).\n\nThis pack's run capabilities require a user or team token; prefer a team token on a dedicated least-privilege team so the credential is not a person's identity.\n\nLeast privilege: place that team on only the workspaces the deployment binds, with Read permission for the collection reads, and Apply runs / Admin permissions only where the run and workspace-write capabilities are enabled.\n\nExpiry and revocation: per HashiCorp's token-management documentation (retrieved during the documented review and appended to the review sources), each user, team and organization token HAS an expiration date and time chosen at creation — it cannot be modified afterward, any API request made with an expired token fails, HCP Terraform sends notification emails 30 days and 7 days before expiry, and organization owners can set a maximum token time-to-live in the organization's settings; the custody job must therefore record the expiry stamped at issuance and rotate before it rather than treating the token as valid until revoked.\n\nRevocation is explicit: delete the individual user, team or organization token in settings (team tokens can be deleted individually or in bulk, and only organization owners can generate or revoke the organization token); a deleted value fails closed with a 401. Token multiplicity differs by type and matters for rotation: an organization can have only ONE valid organization token at a time, but the CURRENT team token API supports MULTIPLE valid team tokens at a time (descriptions unique within the team) and adding a new team token does NOT invalidate older ones — only the legacy team-token API is one-at-a-time, where generating a new legacy token invalidates the previous one; so minting a successor does not retire the old team token, and every issued token must be deleted individually at end of life. HCP Terraform displays an API token only once at creation and obfuscates it thereafter, so a lost value must be regenerated, not retrieved.\n\nRotation therefore sits with an external deployment process: the executor injects the stored value verbatim and performs no token lifecycle of its own. A fourth token type, the audit trails token, reads only the organization's audit trails and cannot serve any capability in this pack.\n\nExecutor hygiene: the value must live only in executor memory for the lifetime of a call, must never be written to logs, traces or diagnostics, and must not appear in crash dumps; a deployment that cannot meet that bar must not enable the pack."
  },
  "capabilities": [
    {
      "capability_id": "terraform.workspaces.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "terraform-cloud.invalid"
      ],
      "endpoint": "https://terraform-cloud.invalid/api/v2/organizations/ORG/workspaces",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-terraform-api-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "terraform-organization",
      "description": "List the workspaces in one HCP Terraform organization: names, execution modes, VCS repo identifiers, terraform versions, resource counts and lock state, which reveal internal infrastructure topology. According to the reviewed Workspaces reference the collection endpoint supports tag filtering and pagination; the registered path covers the collection read only — single-workspace reads (by id or by name) and remote-state-consumer listings are not registered by this pack. ORG is a placeholder the deployment binds before enabling; the host is a reserved placeholder: substitute the customer's HCP Terraform or Enterprise host before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "terraform.runs.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "terraform-cloud.invalid"
      ],
      "endpoint": "https://terraform-cloud.invalid/api/v2/workspaces/WORKSPACE_ID/runs",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-terraform-api-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "terraform-organization",
      "description": "List the runs in one workspace: statuses, messages, sources and creator references, which reveal change history. According to the reviewed Runs reference the endpoint supports operation, status, source and timeframe filters and search parameters, has an adjusted rate limit of 30 requests per minute, and by default excludes plan_only runs unless filtered in. The registered path covers the collection read only — single-run details (GET /runs/{run_id}), plan/apply JSON output and cost estimates are not registered. WORKSPACE_ID is a placeholder the deployment binds before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "terraform.workspace.create",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "terraform-cloud.invalid"
      ],
      "endpoint": "https://terraform-cloud.invalid/api/v2/organizations/ORG/workspaces",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-terraform-api-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "create",
        "deploy"
      ],
      "resource_id": "terraform-organization",
      "description": "Create a workspace with caller-supplied JSON:API attributes, including VCS repository linkage, execution mode, Terraform version, and auto-apply defaults.\n\nThe unrestricted body can enable VCS run triggers (file triggers or queue-all-runs) and auto-apply. Later VCS pushes can then plan and apply infrastructure changes inside Terraform without going through terraform.run.create, terraform.run.apply, or another AAES capability. The capability therefore declares create and deploy at R3. Enforce any restriction to inactive workspaces separately.\n\nCreation requires team owner, Manage-all-workspaces permission, or an organization token. Bind ORG before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "terraform.run.create",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "terraform-cloud.invalid"
      ],
      "endpoint": "https://terraform-cloud.invalid/api/v2/runs",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-terraform-api-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "create",
        "deploy",
        "delete"
      ],
      "resource_id": "terraform-organization",
      "description": "Queue a run that plans and applies infrastructure using a configuration version and workspace variables. The unrestricted body can set auto-apply (no later confirmation), is-destroy=true (destroy all resources managed by the workspace), refresh-only, save-plan, and target or replace addresses.\n\nBecause the request can deploy changes or destroy the workspace's managed resources, it declares create, deploy, and delete at R3. Enforce any restriction to speculative plan-only runs separately.\n\nThe caller needs queue-plan permission; organization tokens cannot create runs. The body selects the target, including WORKSPACE_ID in relationships. Review every workspace accessible to the token; the registration does not narrow that selection.",
      "requires_review": true
    },
    {
      "capability_id": "terraform.run.apply",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "terraform-cloud.invalid"
      ],
      "endpoint": "https://terraform-cloud.invalid/api/v2/runs/RUN_ID/actions/apply",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-terraform-api-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "approve",
        "deploy",
        "delete"
      ],
      "resource_id": "terraform-organization",
      "description": "Confirm a planned run and queue its apply. The run must be paused for confirmation, including needs-confirmation or policy-checked states. Applying changes real infrastructure and may destroy managed resources. Registered as approve, deploy, and delete at R3.\n\nThe endpoint returns 202 when queued, rather than when apply completes; apply is asynchronous. It returns 409 if the run is not paused for confirmation. Organization tokens cannot call this operation. Bind RUN_ID before enabling.\n\nOn supported AAES-mediated execution paths, these actions require approval by an authorized person; this does not prevent calls through bypass or observation-only paths. Terraform's provider-side confirmation is separate from AAES approval.",
      "requires_review": true
    },
    {
      "capability_id": "terraform.run.discard",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "terraform-cloud.invalid"
      ],
      "endpoint": "https://terraform-cloud.invalid/api/v2/runs/RUN_ID/actions/discard",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-terraform-api-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "delete"
      ],
      "resource_id": "terraform-organization",
      "description": "Discard a run paused for confirmation or priority. It skips remaining work, reaches the final discarded state, and lets later runs proceed. Replacing it requires a new run; the discarded plan cannot be restored.\n\nRegistered as delete at R3. The caller needs apply-runs permission; organization tokens cannot call it. Bind RUN_ID before enabling.\n\nCancel and force-cancel are excluded. Terraform warns that force-cancel can lose in-flight state.",
      "requires_review": true
    },
    {
      "capability_id": "terraform.workspace.delete",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "terraform-cloud.invalid"
      ],
      "endpoint": "https://terraform-cloud.invalid/api/v2/workspaces/WORKSPACE_ID",
      "method": "DELETE",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-terraform-api-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "delete"
      ],
      "resource_id": "terraform-organization",
      "description": "Force-delete a workspace without checking whether it manages resources. The workspace, state history, and run history are removed; its real infrastructure keeps running without the workspace that managed it.\n\nRegistered as delete at R3. The safe-delete operation (POST /workspaces/{id}/actions/safe-delete), which refuses while resources are managed, is excluded. This package supplies only force deletion. Bind WORKSPACE_ID before enabling.",
      "requires_review": true
    }
  ],
  "resources": [
    {
      "resource_id": "terraform-organization",
      "kind": "api",
      "locator": "https://terraform-cloud.invalid/api/v2/organizations/ORG",
      "data_class": "internal",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    }
  ],
  "review": {
    "state": "library",
    "date": "2026-09-22",
    "sources": [
      "https://developer.hashicorp.com/terraform/cloud-docs/api-docs (retrieved 2026-09-21)",
      "https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run (retrieved 2026-09-21)",
      "https://developer.hashicorp.com/terraform/cloud-docs/api-docs/workspaces (retrieved 2026-09-21)",
      "https://developer.hashicorp.com/terraform/cloud-docs/users-teams-organizations/api-tokens (retrieved 2026-09-21)"
    ],
    "reviewer_id": "aaes-eng"
  }
}
