A Terraform provider for Dokploy

Plugin-framework provider covering projects, environments, applications,
Compose stacks, managed databases, domains, mounts, ports, redirects,
basic auth, registries, SSH keys, certificates and backup destinations,
over Dokploy's tRPC-over-REST API.

The shim package exposes the provider to other Go modules, which is how
pulumi-dokploy bridges it.
This commit is contained in:
2026-08-09 12:17:26 +03:00
commit a6d8aa8b52
160 changed files with 24260 additions and 0 deletions

View File

@@ -0,0 +1,543 @@
# API Monitoring Reference
Complete guide for monitoring Terraform Stack deployments using the HCP Terraform API. Use this approach for automation, CI/CD pipelines, and non-interactive environments like AI agents.
## Table of Contents
1. [When to Use the API](#when-to-use-the-api)
2. [Authentication](#authentication)
3. [API Monitoring Workflow](#api-monitoring-workflow)
4. [Detailed Endpoint Reference](#detailed-endpoint-reference)
5. [Notes for AI Agents and Automation](#notes-for-ai-agents-and-automation)
## When to Use the API
Use the HCP Terraform API instead of CLI commands when:
- Running in non-interactive environments (CI/CD, automation scripts)
- Building tools or integrations that need programmatic access
- Monitoring multiple Stacks simultaneously
- Implementing custom retry logic or error handling
- Working in environments where streaming CLI commands don't work
**CLI commands that don't work in automation:**
- `terraform stacks deployment-run watch` - Streams output, blocks indefinitely
- `terraform stacks deployment-group watch` - Streams output, blocks indefinitely
- `terraform stacks configuration watch` - Streams output, blocks indefinitely
## Authentication
### Extract API Token from Credentials File
```bash
TOKEN=$(jq -r '.credentials["app.terraform.io"].token' ~/.terraform.d/credentials.tfrc.json)
```
### Alternative: Use Environment Variable
```bash
export TFC_TOKEN="your-token-here"
TOKEN=$TFC_TOKEN
```
### API Request Headers
All API requests require these headers:
```bash
-H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/vnd.api+json"
```
## API Monitoring Workflow
After uploading a configuration with `terraform stacks configuration upload`, follow this sequence to monitor deployment progress:
### Step 1: Get Configuration Status
**Endpoint:** `GET /api/v2/stack-configurations/{configuration-id}`
**Purpose:** Verify configuration upload completed successfully and get the configuration details.
**Request:**
```bash
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/vnd.api+json" \
"https://app.terraform.io/api/v2/stack-configurations/{configuration-id}" | jq '.'
```
**Response Fields:**
- `attributes.status` - Configuration processing status (pending/completed)
- `attributes.sequence-number` - Version number of this configuration
- `attributes.components-detected` - Number of components found
- `attributes.deployments-detected` - Number of deployments found
**Example Response:**
```json
{
"data": {
"id": "stc-ABC123",
"type": "stack-configurations",
"attributes": {
"status": "completed",
"sequence-number": 5,
"components-detected": 3,
"deployments-detected": 2,
"created-at": "2024-01-15T10:30:00.000Z",
"updated-at": "2024-01-15T10:30:45.000Z"
}
}
}
```
### Step 2: Get Deployment Group Summaries
**Endpoint:** `GET /api/v2/stack-configurations/{configuration-id}/stack-deployment-group-summaries`
**Purpose:** Get list of deployment groups, their IDs, and current status summary.
**Request:**
```bash
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/vnd.api+json" \
"https://app.terraform.io/api/v2/stack-configurations/{configuration-id}/stack-deployment-group-summaries" | jq '.'
```
**Response Fields:**
- `id` - Deployment group ID (needed for next step)
- `attributes.name` - Deployment group name (e.g., `dev_default`)
- `attributes.status` - Overall status (running/succeeded/failed)
- `attributes.status-counts` - Breakdown of deployment statuses
**Example Response:**
```json
{
"data": [
{
"id": "sdg-XYZ789",
"type": "stack-deployment-group-summaries",
"attributes": {
"name": "dev_default",
"status": "running",
"status-counts": {
"pending": 0,
"running": 1,
"succeeded": 1,
"failed": 0
}
}
}
]
}
```
### Step 3: Get Deployment Runs
**Endpoint:** `GET /api/v2/stack-deployment-groups/{group-id}/stack-deployment-runs`
**Purpose:** Get list of deployment runs for a specific group with their current status.
**Request:**
```bash
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/vnd.api+json" \
"https://app.terraform.io/api/v2/stack-deployment-groups/{group-id}/stack-deployment-runs" | jq '.'
```
**Response Fields:**
- `id` - Deployment run ID (needed for next step)
- `attributes.status` - Current status (planning/planned/applying/applied/failed)
- `attributes.created-at` - Run start time
- `attributes.updated-at` - Last update time
**Example Response:**
```json
{
"data": [
{
"id": "sdr-123ABC",
"type": "stack-deployment-runs",
"attributes": {
"status": "planning",
"created-at": "2024-01-15T10:31:00.000Z",
"updated-at": "2024-01-15T10:31:15.000Z"
}
}
]
}
```
### Step 4: Get Deployment Steps
**Endpoint:** `GET /api/v2/stack-deployment-runs/{run-id}/stack-deployment-steps`
**Purpose:** Get detailed information about individual plan and apply steps.
**Request:**
```bash
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/vnd.api+json" \
"https://app.terraform.io/api/v2/stack-deployment-runs/{run-id}/stack-deployment-steps" | jq '.'
```
**Response Fields:**
- `id` - Step ID (needed for diagnostics and outputs)
- `attributes.operation-type` - Type of operation (plan/apply)
- `attributes.status` - Step status (running/completed/failed)
- `attributes.component-name` - Which component is being processed
**Example Response:**
```json
{
"data": [
{
"id": "sds-PlanStep123",
"type": "stack-deployment-steps",
"attributes": {
"operation-type": "plan",
"status": "completed",
"component-name": "vpc",
"created-at": "2024-01-15T10:31:05.000Z",
"completed-at": "2024-01-15T10:31:30.000Z"
}
},
{
"id": "sds-ApplyStep456",
"type": "stack-deployment-steps",
"attributes": {
"operation-type": "apply",
"status": "running",
"component-name": "vpc",
"created-at": "2024-01-15T10:32:00.000Z"
}
}
]
}
```
### Step 5: Get Error Diagnostics (When Deployment Fails)
**Endpoint:** `GET /api/v2/stack-deployment-steps/{step-id}/stack-diagnostics`
**Purpose:** Retrieve detailed error messages when a deployment step fails.
**Critical:** The `stack_deployment_step_id` query parameter is **required**. Without it, the API returns empty results.
**Request:**
```bash
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/vnd.api+json" \
"https://app.terraform.io/api/v2/stack-deployment-steps/{step-id}/stack-diagnostics?stack_deployment_step_id={step-id}" | jq '.'
```
**Response Fields:**
- `attributes.severity` - Diagnostic level (error/warning)
- `attributes.summary` - Brief error description
- `attributes.detail` - Detailed error message
- `attributes.diags` - Array of diagnostic objects with file locations and code snippets
**Example Response (Error with Details):**
```json
{
"data": [
{
"id": "stf-ErrorExampleId",
"type": "stack-diagnostics",
"attributes": {
"severity": "error",
"summary": "Diagnostics reported",
"detail": "2 errors",
"diags": [
{
"summary": "Unsupported attribute",
"detail": "This object does not have an attribute named \"target_id\".",
"range": {
"filename": "main.tf",
"start": {
"line": 634,
"column": 33
},
"end": {
"line": 634,
"column": 43
},
"source": "registry.terraform.io/terraform-aws-modules/alb/aws@9.17.0//main.tf"
},
"snippet": {
"code": " target_id = each.value.target_id",
"context": "resource \"aws_lb_target_group_attachment\" \"this\""
}
},
{
"summary": "Invalid reference",
"detail": "A reference to a resource type must be followed by at least one attribute access.",
"range": {
"filename": "main.tf",
"start": {
"line": 142,
"column": 15
},
"end": {
"line": 142,
"column": 28
},
"source": "local-module//main.tf"
},
"snippet": {
"code": " vpc_id = aws_vpc.main",
"context": "resource \"aws_subnet\" \"private\""
}
}
],
"acknowledged": false,
"created-at": "2024-01-15T10:32:15.000Z"
}
}
]
}
```
**Parsing Diagnostics:**
Extract error information with jq:
```bash
# Get error summaries
curl -s -H "Authorization: Bearer $TOKEN" \
"https://app.terraform.io/api/v2/stack-deployment-steps/{step-id}/stack-diagnostics?stack_deployment_step_id={step-id}" | \
jq -r '.data[].attributes.diags[]? | "\(.summary): \(.detail)"'
# Get file locations
curl -s -H "Authorization: Bearer $TOKEN" \
"https://app.terraform.io/api/v2/stack-deployment-steps/{step-id}/stack-diagnostics?stack_deployment_step_id={step-id}" | \
jq -r '.data[].attributes.diags[]? | "\(.range.filename):\(.range.start.line)"'
```
### Step 6: Get Stack Outputs (After Successful Deployment)
**Endpoint:** `GET /api/v2/stack-deployment-steps/{final-apply-step-id}/artifacts?name=apply-description`
**Purpose:** Retrieve Stack outputs after a successful deployment completes.
**Important Notes:**
- This endpoint returns HTTP 307 redirect - use `curl -L` to follow redirects automatically
- This is currently the **only way** to retrieve Stack outputs programmatically
- This endpoint is **not documented** in public API documentation
- You need the final apply step ID from Step 4
**Request:**
```bash
curl -L -s -H "Authorization: Bearer $TOKEN" \
"https://app.terraform.io/api/v2/stack-deployment-steps/{final-apply-step-id}/artifacts?name=apply-description"
```
**Response Structure:**
The artifact response includes an `.outputs` object where each output contains a `change.after` property with the actual output value:
```json
{
"outputs": {
"alb_url": {
"change": {
"actions": ["no-op"],
"before": "http://my-alb-123456789.us-west-2.elb.amazonaws.com",
"after": "http://my-alb-123456789.us-west-2.elb.amazonaws.com",
"after_unknown": false,
"before_sensitive": false,
"after_sensitive": false
},
"type": "string"
},
"ecr_repository_url": {
"change": {
"actions": ["no-op"],
"before": "123456789.dkr.ecr.us-west-2.amazonaws.com/my-repo",
"after": "123456789.dkr.ecr.us-west-2.amazonaws.com/my-repo",
"after_unknown": false,
"before_sensitive": false,
"after_sensitive": false
},
"type": "string"
}
}
}
```
**Extract Only Output Values:**
```bash
curl -L -s --header "Authorization: Bearer $TOKEN" \
"https://app.terraform.io/api/v2/stack-deployment-steps/{final-apply-step-id}/artifacts?name=apply-description" | \
jq -r '.outputs | to_entries | .[] | "\(.key): \(.value.change.after)"'
```
**Example Output:**
```
alb_url: http://my-alb-123456789.us-west-2.elb.amazonaws.com
ecr_repository_url: 123456789.dkr.ecr.us-west-2.amazonaws.com/my-repo
```
## Detailed Endpoint Reference
### Available Artifact Types
The artifacts endpoint accepts these `name` parameter values:
- `plan-description` - Terraform plan output in JSON format
- `plan-debug-log` - Detailed debug logs from plan operation
- `apply-description` - Terraform apply output including outputs (JSON format)
- `apply-debug-log` - Detailed debug logs from apply operation
### Polling Best Practices
**Recommended polling intervals:**
- Configuration status: Check every 5 seconds until status is "completed"
- Deployment runs: Check every 10 seconds during active deployment
- Deployment steps: Check every 10 seconds for individual step status
**Implement exponential backoff:**
```bash
# Example polling script with backoff
RETRY_COUNT=0
MAX_RETRIES=30
BACKOFF=5
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
STATUS=$(curl -s -H "Authorization: Bearer $TOKEN" \
"https://app.terraform.io/api/v2/stack-deployment-runs/{run-id}" | \
jq -r '.data.attributes.status')
if [ "$STATUS" = "applied" ] || [ "$STATUS" = "failed" ]; then
echo "Deployment finished with status: $STATUS"
break
fi
echo "Current status: $STATUS. Waiting ${BACKOFF}s..."
sleep $BACKOFF
RETRY_COUNT=$((RETRY_COUNT + 1))
done
```
## Notes for AI Agents and Automation
### CLI Command Limitations
**These CLI commands DO NOT work in automation:**
- `terraform stacks deployment-run watch` - Streams output, blocks indefinitely
- `terraform stacks deployment-group watch` - Streams output, blocks indefinitely
- `terraform stacks configuration watch` - Streams output, blocks indefinitely
**Solution:** Use API polling instead of watch commands.
### No Direct Output Command
There is currently no CLI command to retrieve Stack outputs. You must:
1. Use API to get deployment steps
2. Find the final apply step ID
3. Request the `apply-description` artifact
4. Parse JSON to extract outputs
### Handling Redirects
The artifacts endpoint returns HTTP 307 redirect to the actual artifact location. Ensure your HTTP client follows redirects:
**curl:** Use `-L` flag
**Python requests:** Set `allow_redirects=True` (default)
**Node.js fetch:** Set `redirect: 'follow'` (default)
### Error Handling
**Common API errors:**
- **401 Unauthorized:** Invalid or expired token - refresh credentials
- **404 Not Found:** Invalid ID or resource doesn't exist yet - retry with backoff
- **429 Too Many Requests:** Rate limited - implement exponential backoff
- **Empty diagnostics:** Missing required `stack_deployment_step_id` query parameter
### Complete Monitoring Script Example
```bash
#!/bin/bash
# Configuration
TOKEN=$(jq -r '.credentials["app.terraform.io"].token' ~/.terraform.d/credentials.tfrc.json)
CONFIG_ID="stc-ABC123"
BASE_URL="https://app.terraform.io/api/v2"
# Helper function
api_get() {
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/vnd.api+json" \
"$1"
}
# 1. Wait for configuration to complete
echo "Checking configuration status..."
while true; do
STATUS=$(api_get "$BASE_URL/stack-configurations/$CONFIG_ID" | jq -r '.data.attributes.status')
[ "$STATUS" = "completed" ] && break
echo "Configuration status: $STATUS. Waiting..."
sleep 5
done
# 2. Get deployment groups
echo "Getting deployment groups..."
GROUP_ID=$(api_get "$BASE_URL/stack-configurations/$CONFIG_ID/stack-deployment-group-summaries" | \
jq -r '.data[0].id')
# 3. Get deployment run
echo "Getting deployment run..."
RUN_ID=$(api_get "$BASE_URL/stack-deployment-groups/$GROUP_ID/stack-deployment-runs" | \
jq -r '.data[0].id')
# 4. Monitor deployment run
echo "Monitoring deployment run: $RUN_ID"
while true; do
STATUS=$(api_get "$BASE_URL/stack-deployment-runs/$RUN_ID" | jq -r '.data.attributes.status')
echo "Deployment status: $STATUS"
if [ "$STATUS" = "applied" ]; then
echo "Deployment succeeded!"
# 5. Get outputs from final apply step
APPLY_STEP=$(api_get "$BASE_URL/stack-deployment-runs/$RUN_ID/stack-deployment-steps" | \
jq -r '.data[] | select(.attributes["operation-type"] == "apply") | .id' | tail -1)
echo "Retrieving outputs from step: $APPLY_STEP"
curl -L -s -H "Authorization: Bearer $TOKEN" \
"$BASE_URL/stack-deployment-steps/$APPLY_STEP/artifacts?name=apply-description" | \
jq -r '.outputs | to_entries | .[] | "\(.key): \(.value.change.after)"'
break
fi
if [ "$STATUS" = "failed" ]; then
echo "Deployment failed!"
# Get error diagnostics
FAILED_STEP=$(api_get "$BASE_URL/stack-deployment-runs/$RUN_ID/stack-deployment-steps" | \
jq -r '.data[] | select(.attributes.status == "failed") | .id' | head -1)
echo "Error diagnostics from step: $FAILED_STEP"
api_get "$BASE_URL/stack-deployment-steps/$FAILED_STEP/stack-diagnostics?stack_deployment_step_id=$FAILED_STEP" | \
jq -r '.data[].attributes.diags[]? | "\(.summary): \(.detail)"'
exit 1
fi
sleep 10
done
```
This script demonstrates a complete monitoring workflow from configuration upload to output retrieval with error handling.

View File

@@ -0,0 +1,476 @@
# Component Configuration Block Reference
Complete reference for all blocks available in Terraform Stack component configuration files (`.tfcomponent.hcl`).
## Table of Contents
1. [Variable Block](#variable-block)
2. [Required Providers Block](#required-providers-block)
3. [Provider Block](#provider-block)
4. [Component Block](#component-block)
5. [Output Block](#output-block)
6. [Locals Block](#locals-block)
7. [Removed Block](#removed-block)
## Variable Block
Declares input variables for Stack configuration.
### Syntax
```hcl
variable "variable_name" {
type = <type>
description = "<description>"
default = <value>
sensitive = <bool>
nullable = <bool>
ephemeral = <bool>
}
```
### Arguments
- **type** (required): Data type (string, number, bool, list, map, object, set, tuple, any)
- **description** (optional): Variable description
- **default** (optional): Default value
- **sensitive** (optional, default false): Mark as sensitive to redact from logs
- **nullable** (optional, default true): Whether null is allowed
- **ephemeral** (optional, default false): Do not persist to state file
### Differences from Traditional Terraform
- **type** is required (not optional)
- **validation** argument is not supported
### Examples
```hcl
variable "aws_region" {
type = string
description = "AWS region for infrastructure"
default = "us-west-1"
}
variable "identity_token" {
type = string
description = "OIDC identity token"
ephemeral = true
}
variable "subnet_config" {
type = object({
cidr_block = string
availability_zone = string
map_public_ip = bool
})
}
```
For complete variable examples in context, see `examples.md`.
## Required Providers Block
Declares provider dependencies.
### Syntax
```hcl
required_providers {
<provider_name> = {
source = "<source>"
version = "<version_constraint>"
}
}
```
### Arguments
- **source** (required): Provider source address (e.g., "hashicorp/aws")
- **version** (optional): Version constraint (e.g., "~> 5.0")
### Examples
```hcl
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.7.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.5.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = ">= 3.0"
}
}
```
## Provider Block
Configures provider instances.
### Syntax
```hcl
provider "<provider_type>" "<alias>" {
for_each = <map_or_set> # Optional
config {
<provider_arguments>
}
}
```
### Arguments
- **provider_type** (label 1, required): Provider type (e.g., "aws", "azurerm")
- **alias** (label 2, required): Unique identifier for this provider configuration
- **for_each** (optional): Create multiple provider instances from a map or set
- **config** (required): Nested block containing provider-specific configuration
### Key Differences from Traditional Terraform
1. Alias is defined in block header, not as an argument
2. Configuration goes in a nested `config` block
3. Supports `for_each` meta-argument
4. Provider configurations are treated as first-class values
### Example
```hcl
provider "aws" "main" {
config {
region = var.aws_region
assume_role_with_web_identity {
role_arn = var.role_arn
web_identity_token = var.identity_token
}
}
}
```
For complete provider examples including for_each and multi-cloud patterns, see `examples.md`.
## Component Block
Defines infrastructure components to include in the Stack.
### Syntax
```hcl
component "<component_name>" {
for_each = <map_or_set> # Optional
source = "<module_source>"
inputs = {
<input_name> = <value>
}
providers = {
<provider_local_name> = provider.<type>.<alias>[<key>]
}
}
```
### Arguments
- **component_name** (label, required): Unique identifier for this component
- **for_each** (optional): Create multiple component instances
- **source** (required): Module source (see [Source Argument](#source-argument) below)
- **version** (optional): Version constraint for registry-based sources only
- **inputs** (required): Map of input variables for the module
- **providers** (required): Map of provider configurations
### Source Argument
The `source` argument accepts the same module sources as traditional Terraform configurations.
**Local File Path:**
```hcl
source = "./modules/vpc"
source = "../shared-modules/networking"
```
**Public Terraform Registry:**
```hcl
source = "terraform-aws-modules/vpc/aws"
source = "hashicorp/consul/aws"
```
Format: `<NAMESPACE>/<NAME>/<PROVIDER>`
**Private HCP Terraform Registry:**
```hcl
source = "app.terraform.io/my-org/vpc/aws"
source = "app.terraform.io/example-corp/networking/azurerm"
```
Format: `<HOSTNAME>/<ORGANIZATION>/<MODULE_NAME>/<PROVIDER_NAME>`
- **HCP Terraform (SaaS)**: Use hostname `app.terraform.io`
- **Terraform Enterprise**: Use your instance hostname (e.g., `terraform.mycompany.com`)
- **Generic hostname**: Use `localterraform.com` for deployments spanning multiple Terraform Enterprise instances
**Git Repository:**
```hcl
source = "git::https://github.com/org/repo.git//modules/vpc?ref=v1.0.0"
source = "git::ssh://git@github.com/org/repo.git//modules/vpc?ref=main"
```
**HTTP/HTTPS Archive:**
```hcl
source = "https://example.com/modules/vpc-module.tar.gz"
```
### Version Argument
The `version` argument is supported only for registry-based sources (public and private registries). Local file paths and Git sources do not support the `version` argument.
```hcl
component "vpc" {
source = "app.terraform.io/my-org/vpc/aws"
version = "~> 2.0" # Semantic versioning constraint
inputs = {
cidr_block = var.vpc_cidr
}
providers = {
aws = provider.aws.main
}
}
```
**Note**: Modules sourced from local file paths always share the same version as their caller and cannot have independent version constraints.
### Component References
Access component outputs using: `component.<name>.<output>`
For components with `for_each`: `component.<name>[<key>].<output>`
### Examples
**Basic Component:**
```hcl
component "vpc" {
source = "app.terraform.io/my-org/vpc/aws"
version = "2.1.0"
inputs = {
cidr_block = var.vpc_cidr
name_prefix = var.name_prefix
}
providers = {
aws = provider.aws.main
}
}
```
**Component with Dependencies:**
```hcl
component "database" {
source = "./modules/rds"
inputs = {
vpc_id = component.vpc.vpc_id
subnet_ids = component.vpc.private_subnet_ids
security_group_ids = [component.security.database_sg_id]
engine_version = var.db_engine_version
}
providers = {
aws = provider.aws.main
}
}
```
For complete component examples including for_each, multi-region, public registry, and multi-provider patterns, see `examples.md`.
## Output Block
Exposes values from Stack configuration.
### Syntax
```hcl
output "<output_name>" {
type = <type>
description = "<description>"
value = <expression>
sensitive = <bool>
ephemeral = <bool>
}
```
### Arguments
- **output_name** (label, required): Unique identifier for this output
- **type** (required): Data type of the output
- **description** (optional): Output description
- **value** (required): Expression to output
- **sensitive** (optional, default false): Mark as sensitive
- **ephemeral** (optional, default false): Ephemeral value
### Differences from Traditional Terraform
- **type** is required
- **precondition** block is not supported
### Examples
```hcl
output "vpc_id" {
type = string
description = "VPC ID"
value = component.vpc.vpc_id
}
output "instance_details" {
type = object({
id = string
public_ip = string
private_ip = string
})
description = "EC2 instance details"
value = {
id = component.compute.instance_id
public_ip = component.compute.public_ip
private_ip = component.compute.private_ip
}
}
```
For complete output examples including sensitive outputs and for expressions, see `examples.md`.
## Locals Block
Defines local values for reuse within the Stack configuration.
### Syntax
```hcl
locals {
<name> = <expression>
}
```
### Example
```hcl
locals {
common_tags = {
Environment = var.environment
ManagedBy = "Terraform Stacks"
Project = var.project_name
}
name_prefix = "${var.project_name}-${var.environment}"
region_config = {
for region in var.regions : region => {
name_suffix = region
instance_count = var.environment == "prod" ? 3 : 1
}
}
}
```
## Removed Block
Declares components to be removed from the Stack.
### Syntax
```hcl
removed {
from = component.<component_name>
source = "<original_module_source>"
providers = {
<provider_name> = provider.<type>.<alias>
}
}
```
### Arguments
- **from** (required): Reference to the component being removed
- **source** (required): Original module source
- **providers** (required): Provider configurations needed for removal
### Important Notes
- Required for safe component removal
- Must include all providers the component used
- Do not remove providers before removing components that use them
### Examples
```hcl
removed {
from = component.old_component
source = "./modules/deprecated-module"
providers = {
aws = provider.aws.main
}
}
removed {
from = component.legacy_regional
source = "registry.terraform.io/example/legacy/aws"
providers = {
aws = provider.aws.main
random = provider.random.main
}
}
```
## Provider References in Component Blocks
### Single Provider
```hcl
providers = {
aws = provider.aws.main
}
```
### Multiple Providers
```hcl
providers = {
aws = provider.aws.main
random = provider.random.main
tls = provider.tls.main
}
```
### Provider from for_each
```hcl
providers = {
aws = provider.aws.regional[each.value]
}
```
### Aliased Providers in Module
If module requires specific provider aliases:
```hcl
providers = {
aws.source = provider.aws.us_east
aws.dest = provider.aws.eu_west
}
```

View File

@@ -0,0 +1,391 @@
# Deployment Configuration Block Reference
Complete reference for all blocks available in Terraform Stack deployment configuration files (`.tfdeploy.hcl`).
## Table of Contents
1. [Identity Token Block](#identity-token-block)
2. [Locals Block](#locals-block)
3. [Deployment Block](#deployment-block)
4. [Deployment Group Block](#deployment-group-block)
5. [Deployment Auto-Approve Block](#deployment-auto-approve-block)
**Note**: For Publish Output and Upstream Input blocks (linked Stacks), see `linked-stacks.md`.
## Identity Token Block
Generates JWT tokens for OIDC authentication with cloud providers.
### Syntax
```hcl
identity_token "<token_name>" {
audience = [<audience_strings>]
}
```
### Arguments
- **token_name** (label, required): Unique identifier for this token
- **audience** (required): List of audience strings for the JWT
### Accessing Token
Reference the JWT using: `identity_token.<n>.jwt`
### Cloud Provider Audiences
**AWS:**
```hcl
identity_token "aws" {
audience = ["aws.workload.identity"]
}
```
**Azure:**
```hcl
identity_token "azure" {
audience = ["api://AzureADTokenExchange"]
}
```
**Google Cloud:**
```hcl
identity_token "gcp" {
audience = ["//iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/<POOL_ID>/providers/<PROVIDER_ID>"]
}
```
**Setup Documentation:** For detailed instructions on configuring OIDC/workload identity for each cloud provider (including IAM roles, trust policies, and federated credentials), see: https://developer.hashicorp.com/terraform/cloud-docs/dynamic-provider-credentials
### Examples
**Single Token:**
```hcl
identity_token "aws" {
audience = ["aws.workload.identity"]
}
deployment "production" {
inputs = {
identity_token = identity_token.aws.jwt
role_arn = var.role_arn
}
}
```
For complete working examples including multi-region identity token usage, see `examples.md`.
## Locals Block
Defines local values for reuse within deployment configuration.
### Syntax
```hcl
locals {
<n> = <expression>
}
```
### Example
```hcl
locals {
aws_regions = ["us-west-1", "us-east-1", "eu-west-1"]
role_arn = "arn:aws:iam::123456789012:role/hcp-terraform-stacks"
common_inputs = {
project_name = "my-app"
environment = "production"
}
}
```
## Deployment Block
Defines deployment instances of the Stack.
### Syntax
```hcl
deployment "<deployment_name>" {
inputs = {
<input_name> = <value>
}
}
```
### Arguments
- **deployment_name** (label, required): Unique identifier for this deployment
- **inputs** (required): Map of input variable values
- **destroy** (optional, default: false): Boolean flag to destroy this deployment
### Constraints
- Minimum 1 deployment per Stack
- Maximum 20 deployments per Stack
- No meta-arguments supported (no `for_each`, `count`)
### Destroying a Deployment
To safely remove a deployment from your Stack:
1. Set `destroy = true` in the deployment block
2. Apply the plan through HCP Terraform
3. After successful destruction, remove the deployment block from your configuration
**Important**: Using the `destroy` argument ensures your configuration has the provider authentication necessary to properly destroy the deployment's resources.
**Example:**
```hcl
deployment "old_environment" {
inputs = {
aws_region = "us-west-1"
instance_count = 2
role_arn = local.role_arn
identity_token = identity_token.aws.jwt
}
destroy = true # Mark for destruction
}
```
After applying this plan and the deployment is destroyed, remove the entire `deployment "old_environment"` block from your configuration.
### Examples
**Single Deployment:**
```hcl
deployment "production" {
inputs = {
aws_region = "us-west-1"
instance_count = 5
instance_type = "t3.large"
role_arn = local.role_arn
identity_token = identity_token.aws.jwt
}
}
```
**Using Locals for Multiple Deployments:**
```hcl
locals {
common_inputs = {
role_arn = "arn:aws:iam::123456789012:role/terraform"
identity_token = identity_token.aws.jwt
project_name = "my-app"
}
}
deployment "dev" {
inputs = merge(local.common_inputs, {
aws_region = "us-east-1"
instance_count = 1
environment = "dev"
})
}
deployment "prod" {
inputs = merge(local.common_inputs, {
aws_region = "us-west-1"
instance_count = 5
environment = "prod"
})
}
```
For complete multi-environment and multi-region deployment examples, see `examples.md`.
## Deployment Group Block
Groups deployments together to configure shared settings and auto-approval rules (HCP Terraform Premium tier feature).
### Syntax
```hcl
deployment_group "<group_name>" {
deployments = [<deployment_references>]
}
```
### Arguments
- **group_name** (label, required): Unique identifier for this deployment group
- **deployments** (required): List of deployment references to include in this group
### Purpose
Deployment groups allow you to:
- Organize deployments logically (by environment, team, region, etc.)
- Configure shared auto-approval rules for multiple deployments
- Manage deployments more effectively at scale
- Establish consistent configuration patterns across all Stacks
### Examples
**Single Deployment Group (Best Practice):**
```hcl
deployment "production" {
inputs = {
aws_region = "us-west-1"
instance_count = 5
role_arn = local.role_arn
identity_token = identity_token.aws.jwt
}
}
deployment_group "production" {
deployments = [deployment.production]
}
```
**Multiple Deployment Groups:**
```hcl
deployment_group "non_production" {
deployments = [
deployment.development,
deployment.staging
]
}
deployment_group "production" {
deployments = [
deployment.prod_us_east,
deployment.prod_us_west,
deployment.prod_eu_west
]
}
```
## Deployment Auto-Approve Block
Defines rules that automatically approve deployment plans based on specific conditions (HCP Terraform Premium feature).
### Syntax
```hcl
deployment_auto_approve "<rule_name>" {
deployment_group = deployment_group.<group_name>
check {
condition = <boolean_expression>
reason = "<failure_message>"
}
}
```
### Arguments
- **rule_name** (label, required): Unique identifier for this auto-approve rule
- **deployment_group** (required): Reference to the deployment group this rule applies to
- **check** (required, one or more): Condition that must be met for auto-approval
### Context Variables
Access plan information through `context` object:
- `context.plan.applyable` - Boolean: plan succeeded without errors
- `context.plan.changes.add` - Number: resources to add
- `context.plan.changes.change` - Number: resources to change
- `context.plan.changes.remove` - Number: resources to remove
- `context.plan.changes.import` - Number: resources to import
### Important Notes
- All checks must pass for auto-approval to occur
- If any check fails, manual approval is required
- HCP Terraform displays the failure reason from failed checks
- Auto-approve rules only apply to deployments in the specified deployment group
### Examples
**Auto-approve Successful Plans:**
```hcl
deployment_group "canary" {
deployments = [
deployment.dev,
deployment.staging
]
}
deployment_auto_approve "applyable_plans" {
deployment_group = deployment_group.canary
check {
condition = context.plan.applyable
reason = "Plan must be applyable without errors"
}
}
```
**Auto-approve Non-Destructive Changes:**
```hcl
deployment_group "production" {
deployments = [
deployment.prod_primary,
deployment.prod_secondary
]
}
deployment_auto_approve "safe_production_changes" {
deployment_group = deployment_group.production
check {
condition = context.plan.changes.remove == 0
reason = "Production deletions require manual approval"
}
check {
condition = context.plan.applyable
reason = "Plan must be successful"
}
}
```
**Graduated Rollout Pattern:**
```hcl
deployment_group "canary" {
deployments = [deployment.canary]
}
deployment_group "production" {
deployments = [
deployment.prod_us,
deployment.prod_eu,
deployment.prod_asia
]
}
# Canary auto-approves with strict checks
deployment_auto_approve "canary_strict" {
deployment_group = deployment_group.canary
check {
condition = context.plan.changes.remove == 0
reason = "Canary cannot delete resources"
}
check {
condition = context.plan.changes.change <= 5
reason = "Canary limited to 5 resource changes"
}
check {
condition = context.plan.applyable
reason = "Plan must be applyable"
}
}
# Production requires manual approval after canary validation
```
For complete deployment configuration examples with all blocks, see `examples.md`.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,187 @@
# Linked Stacks Reference
Complete reference for linking Terraform Stacks together using published outputs and upstream inputs.
## Publish Output Block
Exports outputs from a Stack for consumption by other Stacks (linked Stacks).
###Syntax
```hcl
publish_output "<output_name>" {
type = <type>
value = <expression>
}
```
### Arguments
- **output_name** (label, required): Unique identifier for this published output
- **type** (required): Data type of the output
- **value** (required): Expression to export
### Accessing Deployment Outputs
Reference deployment outputs using: `deployment.<deployment_name>.<output_name>`
### Important Notes
- Must apply the Stack's deployment configuration before downstream Stacks can reference outputs
- Published outputs create a snapshot that other Stacks can read
- Changes to published outputs automatically trigger runs in downstream Stacks
### Examples
**Basic Published Output:**
```hcl
publish_output "vpc_id" {
type = string
value = deployment.network.vpc_id
}
publish_output "subnet_ids" {
type = list(string)
value = deployment.network.private_subnet_ids
}
```
**Multiple Deployment Outputs:**
```hcl
publish_output "regional_vpc_ids" {
type = map(string)
value = {
us_east = deployment.us_east.vpc_id
us_west = deployment.us_west.vpc_id
eu_west = deployment.eu_west.vpc_id
}
}
```
**Complex Output:**
```hcl
publish_output "database_config" {
type = object({
endpoint = string
port = number
name = string
})
value = {
endpoint = deployment.production.db_endpoint
port = deployment.production.db_port
name = deployment.production.db_name
}
}
```
**Regional Endpoints:**
```hcl
publish_output "api_endpoints" {
type = map(object({
url = string
region = string
}))
value = {
for env in ["dev", "staging", "prod"] : env => {
url = deployment[env].api_url
region = deployment[env].region
}
}
}
```
## Upstream Input Block
References published outputs from another Stack (linked Stacks).
### Syntax
```hcl
upstream_input "<input_name>" {
type = "stack"
source = "<stack_address>"
}
```
### Arguments
- **input_name** (label, required): Local name for this upstream input
- **type** (required): Must be "stack"
- **source** (required): Full Stack address in format: `app.terraform.io/<org>/<project>/<stack-name>`
### Accessing Upstream Outputs
Reference upstream outputs using: `upstream_input.<input_name>.<output_name>`
### Important Notes
- Creates a dependency on the upstream Stack
- Upstream Stack must have applied its deployment configuration
- Changes in upstream Stack automatically trigger downstream Stack runs
- Only works with Stacks in the same HCP Terraform project
### Examples
**Basic Upstream Reference:**
```hcl
upstream_input "network" {
type = "stack"
source = "app.terraform.io/my-org/my-project/networking-stack"
}
deployment "application" {
inputs = {
vpc_id = upstream_input.network.vpc_id
subnet_ids = upstream_input.network.subnet_ids
}
}
```
**Multiple Upstream Stacks:**
```hcl
upstream_input "network" {
type = "stack"
source = "app.terraform.io/my-org/my-project/network-stack"
}
upstream_input "database" {
type = "stack"
source = "app.terraform.io/my-org/my-project/database-stack"
}
deployment "application" {
inputs = {
vpc_id = upstream_input.network.vpc_id
subnet_ids = upstream_input.network.private_subnet_ids
database_endpoint = upstream_input.database.endpoint
database_credentials = upstream_input.database.credentials
}
}
```
**Regional Upstream Dependencies:**
```hcl
upstream_input "regional_network" {
type = "stack"
source = "app.terraform.io/my-org/my-project/regional-networks"
}
deployment "us_east_app" {
inputs = {
region = "us-east-1"
vpc_id = upstream_input.regional_network.regional_vpc_ids["us_east"]
subnet_ids = upstream_input.regional_network.regional_subnet_ids["us_east"]
}
}
```
## Complete Working Example
For a complete example showing full Stack configurations with all files (variables, providers, components, outputs, deployments) for both upstream and downstream Stacks, see the "Linked Stacks (Cross-Stack Dependencies)" section in `examples.md`.

View File

@@ -0,0 +1,671 @@
# Troubleshooting Reference
Common issues and solutions when working with Terraform Stacks.
## Table of Contents
1. [Configuration Issues](#configuration-issues)
2. [Deployment Issues](#deployment-issues)
3. [Provider and Authentication Issues](#provider-and-authentication-issues)
4. [Module Compatibility Issues](#module-compatibility-issues)
5. [State and Dependency Issues](#state-and-dependency-issues)
6. [API and CLI Issues](#api-and-cli-issues)
## Configuration Issues
### Circular Dependencies
**Issue:** Component A references Component B, and Component B references Component A.
**Error Message:**
```
Error: Cycle detected in component dependencies
```
**Solutions:**
1. **Break the circular reference** by refactoring components:
```hcl
# Before (circular dependency)
component "vpc" {
source = "./modules/vpc"
inputs = {
security_group_id = component.app.security_group_id # References app
}
}
component "app" {
source = "./modules/app"
inputs = {
vpc_id = component.vpc.vpc_id # References vpc
}
}
# After (broken circular reference)
component "vpc" {
source = "./modules/vpc"
inputs = {
# Remove reference to app
}
}
component "security_group" {
source = "./modules/security-group"
inputs = {
vpc_id = component.vpc.vpc_id
}
}
component "app" {
source = "./modules/app"
inputs = {
vpc_id = component.vpc.vpc_id
security_group_id = component.security_group.id
}
}
```
2. **Use intermediate components** to break the dependency chain
3. **Refactor modules** to remove the circular dependency at the module level
### Validation Errors on Variables
**Issue:** Variable block validation errors during `terraform stacks validate`.
**Error Message:**
```
Error: Unsupported argument
on variables.tfcomponent.hcl line 5:
5: validation {
Validation blocks are not supported in Stack configurations
```
**Solution:** Remove `validation` blocks from variable declarations. Stacks do not support validation blocks:
```hcl
# Incorrect
variable "instance_count" {
type = number
validation {
condition = var.instance_count > 0
error_message = "Instance count must be positive"
}
}
# Correct
variable "instance_count" {
type = number
description = "Number of instances (must be positive)"
}
```
Move validation logic into the underlying modules if needed.
### Missing Type in Variable Declarations
**Issue:** Variables fail validation when `type` is not specified.
**Error Message:**
```
Error: Missing required argument
on variables.tfcomponent.hcl line 3:
3: variable "region" {
The argument "type" is required in Stack variable declarations
```
**Solution:** Always specify `type` for variables - it's required in Stacks (unlike traditional Terraform):
```hcl
# Incorrect
variable "region" {
default = "us-west-1"
}
# Correct
variable "region" {
type = string
default = "us-west-1"
}
```
### Provider Configuration in Modules
**Issue:** Modules with embedded provider blocks cause errors.
**Error Message:**
```
Error: Provider configuration not allowed in module
Modules used with Terraform Stacks cannot contain provider blocks
```
**Solution:**
1. **Remove provider blocks from modules** - configure providers in Stack configuration instead
2. **Use modules that don't contain provider blocks** (most public registry modules are compatible)
3. **Fork and modify modules** if necessary to remove provider blocks
## Deployment Issues
### Cannot Destroy Deployment from UI
**Issue:** The HCP Terraform UI doesn't provide an option to destroy Stack deployments.
**Why:** Stack deployment destruction is only available through configuration, not the UI.
**Solution:** Set `destroy = true` in the deployment block and upload the configuration:
```hcl
deployment "old_environment" {
inputs = {
aws_region = "us-west-1"
instance_count = 2
role_arn = local.role_arn
identity_token = identity_token.aws.jwt
}
destroy = true # Marks deployment for destruction
}
```
**Workflow:**
1. Add `destroy = true` to the deployment block
2. Run `terraform stacks configuration upload`
3. HCP Terraform creates a destroy run automatically
4. Approve the destroy run (if auto-approve is not configured)
5. After destruction completes, remove the deployment block entirely
6. Upload configuration again to clean up the deployment definition
**Important:** You cannot destroy deployments from the UI. This is by design to prevent accidental destruction.
### Deployment Stuck in "Planning" State
**Issue:** Deployment remains in "planning" state indefinitely.
**Possible Causes:**
1. **Provider authentication failed** - Check OIDC configuration and IAM roles
2. **Module download failed** - Verify module sources are accessible
3. **Provider version conflict** - Check `.terraform.lock.hcl` matches required providers
**Diagnosis:**
```bash
# Get deployment step diagnostics
terraform stacks deployment-run list
# Note the run ID, then:
curl -s -H "Authorization: Bearer $TOKEN" \
"https://app.terraform.io/api/v2/stack-deployment-runs/{run-id}/stack-deployment-steps" | \
jq '.data[] | {id, status: .attributes.status, component: .attributes["component-name"]}'
```
**Solutions:**
1. Check diagnostics for the stuck step
2. Verify provider authentication is configured correctly
3. Ensure all module sources are accessible
4. Check provider lock file matches required providers
### Deployment Requires Approval But No Approval Prompt
**Issue:** Deployment is waiting for approval but CLI doesn't show approval prompt.
**Why:** CLI monitoring commands are non-blocking and don't automatically prompt for approval.
**Solution:**
**Option 1: Approve via CLI**
```bash
# Approve all pending plans in a deployment run
terraform stacks deployment-run approve-all-plans -deployment-run-id=sdr-ABC123
# Or approve all plans in a deployment group
terraform stacks deployment-group approve-all-plans -deployment-group=canary
```
**Option 2: Configure auto-approve** (Premium feature)
```hcl
deployment_auto_approve "safe_changes" {
deployment_group = deployment_group.canary
check {
condition = context.plan.applyable
reason = "Plan must be successful"
}
}
```
## Provider and Authentication Issues
### OIDC Authentication Failing
**Issue:** Provider authentication fails with OIDC/workload identity.
**Error Messages:**
```
Error: Error assuming role with web identity
Error: Failed to retrieve credentials
Error: Invalid identity token
```
**Diagnosis Steps:**
1. **Verify identity token configuration:**
```hcl
# Check identity_token block exists
identity_token "aws" {
audience = ["aws.workload.identity"]
}
# Check deployment references the token
deployment "production" {
inputs = {
identity_token = identity_token.aws.jwt
}
}
```
2. **Verify provider configuration:**
```hcl
provider "aws" "this" {
config {
region = var.aws_region
assume_role_with_web_identity {
role_arn = var.role_arn
web_identity_token = var.identity_token
}
}
}
```
3. **Check IAM role trust policy:**
**AWS - Verify trust policy includes HCP Terraform:**
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<account-id>:oidc-provider/app.terraform.io"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"app.terraform.io:aud": "aws.workload.identity"
},
"StringLike": {
"app.terraform.io:sub": "organization:<org-name>:project:<project-name>:stack:<stack-name>:deployment:<deployment-name>"
}
}
}
]
}
```
**Azure - Verify federated credential:**
- Application ID matches the one in provider configuration
- Subject matches: `organization:<org>:project:<project>:stack:<stack>:deployment:<deployment>`
- Issuer is `https://app.terraform.io`
**GCP - Verify workload identity pool:**
- Provider configuration includes correct workload identity provider
- Service account has necessary IAM permissions
- Attribute mapping includes `google.subject` from token claims
**Solutions:**
1. Fix IAM role trust policy to include correct HCP Terraform OIDC provider
2. Ensure audience matches between identity_token block and IAM trust policy
3. Verify subject pattern matches your organization/project/stack/deployment names
4. Check that the role_arn is correct in provider configuration
### Provider Version Lock File Issues
**Issue:** Provider version conflicts or "could not retrieve provider" errors.
**Error Messages:**
```
Error: Failed to install provider
Error: Provider version not found
Error: Checksum mismatch for provider
```
**Solutions:**
1. **Regenerate provider lock file:**
```bash
terraform stacks providers-lock
```
2. **Add additional platforms** (if deploying from different OS):
```bash
terraform stacks providers-lock \
-platform=linux_amd64 \
-platform=darwin_amd64 \
-platform=darwin_arm64
```
3. **Verify required_providers block:**
```hcl
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.7.0" # Ensure version constraint is valid
}
}
```
4. **Commit `.terraform.lock.hcl`** to version control
## Module Compatibility Issues
### Public Registry Module Errors
**Issue:** Modules from the Terraform public registry cause errors during plan or apply.
**Common Errors:**
```
Error: Unsupported attribute
Error: Invalid reference
Error: Missing required argument
```
**Known Problematic Modules:**
- `terraform-aws-modules/alb/aws` - Some versions have compatibility issues
- `terraform-aws-modules/ecs-service/aws` - May have issues with certain configurations
**Solutions:**
1. **Test modules in dev deployment first** before using in production
2. **Check module compatibility** by reviewing recent issues on the module repository
3. **Use specific module versions** rather than latest:
```hcl
component "alb" {
source = "terraform-aws-modules/alb/aws"
version = "8.7.0" # Use specific version known to work
# ...
}
```
4. **Consider using raw resources** for critical infrastructure:
```hcl
# Instead of using a module that has issues
component "alb" {
source = "./modules/alb" # Create local module with raw resources
# ...
}
```
5. **Fork and fix modules** if you have the resources to maintain them
6. **Report compatibility issues** to module maintainers
### Local Module Not Found
**Issue:** Stack can't find local module sources.
**Error Message:**
```
Error: Module not found
Could not load module ./modules/vpc
```
**Solutions:**
1. **Verify module path is relative** to Stack root:
```hcl
# Correct
component "vpc" {
source = "./modules/vpc"
}
# Incorrect (absolute paths don't work)
component "vpc" {
source = "/Users/username/project/modules/vpc"
}
```
2. **Ensure module directory exists** with proper structure:
```
my-stack/
├── components.tfcomponent.hcl
└── modules/
└── vpc/
├── main.tf
├── variables.tf
└── outputs.tf
```
3. **Check file permissions** on module directories
## State and Dependency Issues
### Component Output Not Available
**Issue:** Component output is not available to referencing component.
**Error Message:**
```
Error: Reference to unknown component
Component "vpc" has not been defined
```
**Solutions:**
1. **Verify component exists** in configuration:
```hcl
component "vpc" {
source = "./modules/vpc"
# Must define component before referencing it
}
component "app" {
source = "./modules/app"
inputs = {
vpc_id = component.vpc.vpc_id # Now valid
}
}
```
2. **Check output is defined in module:**
```hcl
# In modules/vpc/outputs.tf
output "vpc_id" {
value = aws_vpc.main.id
}
```
3. **For components with for_each**, reference specific instance:
```hcl
component "regional" {
for_each = var.regions
# ...
}
component "app" {
inputs = {
# Correct - reference specific instance
vpc_id = component.regional["us-west-1"].vpc_id
# Incorrect - can't reference for_each component directly
# vpc_id = component.regional.vpc_id
}
}
```
### Deferred Changes Not Converging
**Issue:** Deployment with deferred changes doesn't complete after multiple iterations.
**Error Message:**
```
Error: Maximum deferred change iterations reached
```
**Cause:** Dependency cycle or values that never stabilize.
**Solutions:**
1. **Review component dependencies** for logical cycles
2. **Check for computed values that change on every run**
3. **Refactor to break dependency chain**
4. **Consider multi-stage deployments** if resources truly can't be created together
## API and CLI Issues
### Empty Diagnostics Response
**Issue:** API request for diagnostics returns empty results.
**Request:**
```bash
curl "https://app.terraform.io/api/v2/stack-deployment-steps/{step-id}/stack-diagnostics"
```
**Response:**
```json
{
"data": []
}
```
**Solution:** Add required `stack_deployment_step_id` query parameter:
```bash
curl "https://app.terraform.io/api/v2/stack-deployment-steps/{step-id}/stack-diagnostics?stack_deployment_step_id={step-id}"
```
### Cannot Retrieve Stack Outputs
**Issue:** No CLI command to retrieve Stack outputs after deployment.
**Why:** Currently no direct CLI command for outputs retrieval.
**Solution:** Use the artifacts API endpoint:
```bash
# Get final apply step ID first
APPLY_STEP=$(terraform stacks deployment-run list --json | \
jq -r '.[0].deployment_steps[] | select(.operation_type == "apply") | .id' | tail -1)
# Get outputs
curl -L -s -H "Authorization: Bearer $TOKEN" \
"https://app.terraform.io/api/v2/stack-deployment-steps/$APPLY_STEP/artifacts?name=apply-description" | \
jq -r '.outputs | to_entries | .[] | "\(.key): \(.value.change.after)"'
```
### CLI Watch Commands Hang in CI/CD
**Issue:** Commands like `terraform stacks deployment-run watch` never return in CI/CD pipelines.
**Why:** Watch commands stream output indefinitely and are designed for interactive use.
**Solution:** Use API polling instead of watch commands. See `api-monitoring.md` for complete workflow.
### Artifacts Endpoint Returns 404
**Issue:** Request to artifacts endpoint returns 404 Not Found.
**Possible Causes:**
1. **Step hasn't completed yet** - wait for step status to be "completed"
2. **Wrong artifact name** - use one of: plan-description, plan-debug-log, apply-description, apply-debug-log
3. **Invalid step ID** - verify step ID from deployment-steps endpoint
**Solution:**
```bash
# Check step status first
curl -s -H "Authorization: Bearer $TOKEN" \
"https://app.terraform.io/api/v2/stack-deployment-steps/{step-id}" | \
jq '.data.attributes.status'
# Only request artifacts when status is "completed"
if [ "$STATUS" = "completed" ]; then
curl -L -H "Authorization: Bearer $TOKEN" \
"https://app.terraform.io/api/v2/stack-deployment-steps/{step-id}/artifacts?name=apply-description"
fi
```
### HTTP 307 Redirect Not Followed
**Issue:** Artifacts endpoint returns redirect response instead of artifact content.
**Why:** The endpoint returns HTTP 307 redirect to the actual artifact URL.
**Solution:** Configure HTTP client to follow redirects:
```bash
# curl: Use -L flag
curl -L -H "Authorization: Bearer $TOKEN" \
"https://app.terraform.io/api/v2/stack-deployment-steps/{step-id}/artifacts?name=apply-description"
# Python requests: allow_redirects=True (default)
import requests
response = requests.get(url, headers=headers, allow_redirects=True)
# Node.js fetch: redirect: 'follow' (default)
const response = await fetch(url, {
headers: headers,
redirect: 'follow'
});
```
## Getting Additional Help
### Enable Debug Logging
For more detailed error information, enable debug logging:
```bash
# CLI commands
TF_LOG=DEBUG terraform stacks validate
TF_LOG=DEBUG terraform stacks configuration upload
# API artifacts
# Request the debug-log artifact instead of description
curl -L -H "Authorization: Bearer $TOKEN" \
"https://app.terraform.io/api/v2/stack-deployment-steps/{step-id}/artifacts?name=apply-debug-log"
```
### Check HCP Terraform Status
If experiencing widespread issues, check HCP Terraform status page:
- https://status.hashicorp.com
### Review Configuration Version
List recent configurations to identify when issues started:
```bash
terraform stacks configuration list
```
### Contact Support
For issues not covered here:
1. Gather relevant error messages and diagnostics
2. Note the configuration sequence number
3. Include deployment run IDs
4. Contact HashiCorp Support with details