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:
480
.agents/skills/terraform-stacks/SKILL.md
Normal file
480
.agents/skills/terraform-stacks/SKILL.md
Normal file
@@ -0,0 +1,480 @@
|
||||
---
|
||||
name: terraform-stacks
|
||||
description: Comprehensive guide for working with HashiCorp Terraform Stacks. Use when creating, modifying, or validating Terraform Stack configurations (.tfcomponent.hcl, .tfdeploy.hcl files), working with stack components and deployments from local modules, public registry, or private registry sources, managing multi-region or multi-environment infrastructure, or troubleshooting Terraform Stacks syntax and structure.
|
||||
metadata:
|
||||
copyright: Copyright IBM Corp. 2026
|
||||
version: "0.0.1"
|
||||
---
|
||||
|
||||
# Terraform Stacks
|
||||
|
||||
Terraform Stacks simplify infrastructure provisioning and management at scale by providing a configuration layer above traditional Terraform modules. Stacks enable declarative orchestration of multiple components across environments, regions, and cloud accounts.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
**Stack**: A complete unit of infrastructure composed of components and deployments that can be managed together.
|
||||
|
||||
**Component**: An abstraction around a Terraform module that defines infrastructure pieces. Each component specifies a source module, inputs, and providers.
|
||||
|
||||
**Deployment**: An instance of all components in a stack with specific input values. Use deployments for different environments (dev/staging/prod), regions, or cloud accounts.
|
||||
|
||||
**Stack Language**: A separate HCL-based language (not regular Terraform HCL) with distinct blocks and file extensions.
|
||||
|
||||
## File Structure
|
||||
|
||||
Terraform Stacks use specific file extensions:
|
||||
|
||||
- **Component configuration**: `.tfcomponent.hcl`
|
||||
- **Deployment configuration**: `.tfdeploy.hcl`
|
||||
- **Provider lock file**: `.terraform.lock.hcl` (generated by CLI)
|
||||
|
||||
All configuration files must be at the root level of the Stack repository. HCP Terraform processes all files in dependency order.
|
||||
|
||||
### Recommended File Organization
|
||||
|
||||
```
|
||||
my-stack/
|
||||
├── .terraform-version # The required Terraform version for this Stack
|
||||
├── variables.tfcomponent.hcl # Variable declarations
|
||||
├── providers.tfcomponent.hcl # Provider configurations
|
||||
├── components.tfcomponent.hcl # Component definitions
|
||||
├── outputs.tfcomponent.hcl # Stack outputs
|
||||
├── deployments.tfdeploy.hcl # Deployment definitions
|
||||
├── .terraform.lock.hcl # Provider lock file (generated)
|
||||
└── modules/ # Local modules (optional - only if using local modules)
|
||||
├── s3/
|
||||
└── compute/
|
||||
```
|
||||
|
||||
**Note**: The `modules/` directory is only required when using local module sources. Components can reference modules from:
|
||||
- Local file paths: `./modules/vpc`
|
||||
- Public registry: `terraform-aws-modules/vpc/aws`
|
||||
- Private registry: `app.terraform.io/<org-name>/vpc/aws`
|
||||
- Git: `git::https://github.com/org/repo.git//path?ref=v1.0.0`
|
||||
|
||||
HCP Terraform processes all `.tfcomponent.hcl` and `.tfdeploy.hcl` files in dependency order.
|
||||
|
||||
## Required Terraform version (.terraform-version)
|
||||
|
||||
Use Terraform v1.13.x or later to access the Stacks CLI plugin and to run
|
||||
terraform stacks CLI commands. Begin by adding a .terraform-version file to
|
||||
your Stack's root directory to specify the Terraform version required for your
|
||||
Stack. For example, the following file specifies Terraform v1.14.5:
|
||||
|
||||
```
|
||||
1.14.5
|
||||
```
|
||||
|
||||
## Component Configuration (.tfcomponent.hcl)
|
||||
|
||||
### Variable Block
|
||||
|
||||
Declare input variables for the Stack configuration. Variables must define a `type` field and do not support the `validation` argument.
|
||||
|
||||
```hcl
|
||||
variable "aws_region" {
|
||||
type = string
|
||||
description = "AWS region for deployments"
|
||||
default = "us-west-1"
|
||||
}
|
||||
|
||||
variable "identity_token" {
|
||||
type = string
|
||||
description = "OIDC identity token"
|
||||
ephemeral = true # Does not persist to state file
|
||||
}
|
||||
|
||||
variable "instance_count" {
|
||||
type = number
|
||||
nullable = false
|
||||
}
|
||||
```
|
||||
|
||||
**Important**: Use `ephemeral = true` for credentials and tokens (identity tokens, API keys, passwords) to prevent them from persisting in state files. Use `stable` for longer-lived values like license keys that need to persist across runs.
|
||||
|
||||
### Required Providers Block
|
||||
|
||||
```hcl
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 6.0"
|
||||
}
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
version = "~> 3.5.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Provider Block
|
||||
|
||||
Provider blocks differ from traditional Terraform:
|
||||
|
||||
1. Support `for_each` meta-argument
|
||||
2. Define aliases in the block header (not as an argument)
|
||||
3. Accept configuration through a `config` block
|
||||
|
||||
**Single 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
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Multiple Provider Configurations with for_each:**
|
||||
|
||||
```hcl
|
||||
provider "aws" "configurations" {
|
||||
for_each = var.regions
|
||||
|
||||
config {
|
||||
region = each.value
|
||||
assume_role_with_web_identity {
|
||||
role_arn = var.role_arn
|
||||
web_identity_token = var.identity_token
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Authentication Best Practice**: Use **workload identity** (OIDC) as the preferred authentication method for Stacks. This approach:
|
||||
- Avoids long-lived static credentials
|
||||
- Provides temporary, scoped credentials per deployment run
|
||||
- Integrates with cloud provider IAM (AWS IAM Roles, Azure Managed Identities, GCP Service Accounts)
|
||||
- Eliminates need for platform-managed environment variables
|
||||
|
||||
Configure workload identity using `identity_token` blocks and `assume_role_with_web_identity` in provider configuration. For detailed setup instructions for AWS, Azure, and GCP, see: https://developer.hashicorp.com/terraform/cloud-docs/dynamic-provider-credentials
|
||||
|
||||
### Component Block
|
||||
|
||||
Each Stack requires at least one component block. Add a component for each module to include in the Stack. Components reference modules from local paths, registries, or Git.
|
||||
|
||||
```hcl
|
||||
component "vpc" {
|
||||
source = "app.terraform.io/my-org/vpc/aws" # Local, registry, or Git URL
|
||||
version = "2.1.0" # For registry modules
|
||||
|
||||
inputs = {
|
||||
cidr_block = var.vpc_cidr
|
||||
name_prefix = var.name_prefix
|
||||
}
|
||||
|
||||
providers = {
|
||||
aws = provider.aws.this
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See `references/component-blocks.md` for examples of dependencies, for_each, public registry modules, Git sources, and more.
|
||||
|
||||
**Key Points:**
|
||||
- Reference outputs: `component.<name>.<output>` or `component.<name>[key].<output>` for for_each
|
||||
- Dependencies inferred automatically from component references
|
||||
- Aggregate with for expressions: `[for x in component.s3 : x.bucket_name]`
|
||||
- For components with `for_each`, reference specific instances: `component.<name>[each.value].<output>`
|
||||
- Provider references are normal values: `provider.<type>.<alias>` or `provider.<type>.<alias>[each.value]`
|
||||
|
||||
### Output Block
|
||||
|
||||
Outputs require a `type` argument and do not support `preconditions`:
|
||||
|
||||
```hcl
|
||||
output "vpc_id" {
|
||||
type = string
|
||||
description = "VPC ID"
|
||||
value = component.vpc.vpc_id
|
||||
}
|
||||
|
||||
output "endpoint_urls" {
|
||||
type = map(string)
|
||||
value = {
|
||||
for region, comp in component.api : region => comp.endpoint_url
|
||||
}
|
||||
sensitive = false
|
||||
}
|
||||
```
|
||||
|
||||
### Locals Block
|
||||
|
||||
Locals blocks work the same in both `.tfcomponent.hcl` and `.tfdeploy.hcl` files:
|
||||
|
||||
```hcl
|
||||
locals {
|
||||
common_tags = {
|
||||
Environment = var.environment
|
||||
ManagedBy = "Terraform Stacks"
|
||||
Project = var.project_name
|
||||
}
|
||||
|
||||
region_config = {
|
||||
for region in var.regions : region => {
|
||||
name_suffix = "${var.environment}-${region}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Removed Block
|
||||
|
||||
Use to safely remove components from a Stack. HCP Terraform requires the component's providers to remove it.
|
||||
|
||||
```hcl
|
||||
removed {
|
||||
from = component.old_component
|
||||
source = "./modules/old-module"
|
||||
|
||||
providers = {
|
||||
aws = provider.aws.this
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Deployment Configuration (.tfdeploy.hcl)
|
||||
|
||||
### Identity Token Block
|
||||
|
||||
Generate JWT tokens for OIDC authentication with cloud providers:
|
||||
|
||||
```hcl
|
||||
identity_token "aws" {
|
||||
audience = ["aws.workload.identity"]
|
||||
}
|
||||
|
||||
identity_token "azure" {
|
||||
audience = ["api://AzureADTokenExchange"]
|
||||
}
|
||||
```
|
||||
|
||||
Reference tokens in deployments using `identity_token.<name>.jwt`
|
||||
|
||||
### Store Block
|
||||
|
||||
Access HCP Terraform variable sets within Stack deployments:
|
||||
|
||||
```hcl
|
||||
store "varset" "aws_credentials" {
|
||||
id = "varset-ABC123" # Alternatively use: name = "varset_name"
|
||||
source = "tfc-cloud-shared"
|
||||
category = "terraform" # Alternatively use: category = "env" for environment variables
|
||||
}
|
||||
|
||||
deployment "production" {
|
||||
inputs = {
|
||||
aws_access_key = store.varset.aws_credentials.AWS_ACCESS_KEY_ID
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use to centralize credentials and share variables across Stacks. See `references/deployment-blocks.md` for details.
|
||||
|
||||
### Deployment Block
|
||||
|
||||
Define deployment instances (minimum 1, maximum 20 per Stack):
|
||||
|
||||
```hcl
|
||||
deployment "production" {
|
||||
inputs = {
|
||||
aws_region = "us-west-1"
|
||||
instance_count = 3
|
||||
role_arn = local.role_arn
|
||||
identity_token = identity_token.aws.jwt
|
||||
}
|
||||
}
|
||||
|
||||
# Create multiple deployments for different environments
|
||||
deployment "development" {
|
||||
inputs = {
|
||||
aws_region = "us-east-1"
|
||||
instance_count = 1
|
||||
name_suffix = "dev"
|
||||
role_arn = local.role_arn
|
||||
identity_token = identity_token.aws.jwt
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To destroy a deployment**: Set `destroy = true`, upload configuration, approve destroy run, then remove the deployment block. See `references/deployment-blocks.md` for details.
|
||||
|
||||
### Deployment Group Block
|
||||
|
||||
Group deployments together for shared settings (HCP Terraform Premium tier feature). Free/standard tiers use default groups named `{deployment-name}_default`.
|
||||
|
||||
```hcl
|
||||
deployment_group "canary" {
|
||||
auto_approve_checks = [deployment_auto_approve.safe_changes]
|
||||
}
|
||||
|
||||
deployment "dev" {
|
||||
inputs = { /* ... */ }
|
||||
deployment_group = deployment_group.canary
|
||||
}
|
||||
```
|
||||
|
||||
Multiple deployments can reference the same group. See `references/deployment-blocks.md` for details.
|
||||
|
||||
### Deployment Auto-Approve Block
|
||||
|
||||
Define rules to automatically approve deployment plans (HCP Terraform Premium tier feature):
|
||||
|
||||
```hcl
|
||||
deployment_auto_approve "safe_changes" {
|
||||
deployment_group = deployment_group.canary
|
||||
|
||||
check {
|
||||
condition = context.plan.changes.remove == 0
|
||||
reason = "Cannot auto-approve plans with resource deletions"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Available context variables**: `context.plan.applyable`, `context.plan.changes.add/change/remove/total`, `context.success`
|
||||
|
||||
**Note:** `orchestrate` blocks are deprecated. Use `deployment_group` and `deployment_auto_approve` instead.
|
||||
|
||||
See `references/deployment-blocks.md` for all context variables and patterns.
|
||||
|
||||
### Publish Output and Upstream Input Blocks
|
||||
|
||||
Link Stacks together by publishing outputs from one Stack and consuming them in another:
|
||||
|
||||
```hcl
|
||||
# In network Stack - publish outputs
|
||||
publish_output "vpc_id_network" {
|
||||
type = string
|
||||
value = deployment.network.vpc_id
|
||||
}
|
||||
|
||||
# In application Stack - consume outputs
|
||||
upstream_input "network_stack" {
|
||||
type = "stack"
|
||||
source = "app.terraform.io/my-org/my-project/networking-stack"
|
||||
}
|
||||
|
||||
deployment "app" {
|
||||
inputs = {
|
||||
vpc_id = upstream_input.network_stack.vpc_id_network
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See `references/linked-stacks.md` for complete documentation and examples.
|
||||
|
||||
## Terraform Stacks CLI
|
||||
|
||||
**Note**: Terraform Stacks is Generally Available (GA) as of Terraform CLI v1.13+. Stacks now count toward Resources Under Management (RUM) for HCP Terraform billing.
|
||||
|
||||
### Initialize and Validate
|
||||
|
||||
```bash
|
||||
terraform stacks init # Download providers, modules, generate lock file
|
||||
terraform stacks providers-lock # Regenerate lock file (add platforms if needed)
|
||||
terraform stacks validate # Check syntax without uploading
|
||||
```
|
||||
|
||||
### Deployment Workflow
|
||||
|
||||
**Important**: No `plan` or `apply` commands. Upload configuration triggers deployment runs automatically.
|
||||
|
||||
```bash
|
||||
# 1. Upload configuration (triggers deployment runs)
|
||||
terraform stacks configuration upload
|
||||
|
||||
# 2. Monitor deployments
|
||||
terraform stacks deployment-run list # List runs (non-interactive)
|
||||
terraform stacks deployment-group watch -deployment-group=... # Stream status updates
|
||||
|
||||
# 3. Approve deployments (if auto-approve not configured)
|
||||
terraform stacks deployment-run approve-all-plans -deployment-run-id=...
|
||||
terraform stacks deployment-group approve-all-plans -deployment-group=...
|
||||
terraform stacks deployment-run cancel -deployment-run-id=... # Cancel if needed
|
||||
```
|
||||
|
||||
### Configuration Management
|
||||
|
||||
```bash
|
||||
terraform stacks configuration list # List configuration versions
|
||||
terraform stacks configuration fetch -configuration-id=... # Download configuration
|
||||
terraform stacks configuration watch # Monitor upload status
|
||||
```
|
||||
|
||||
### Other Commands
|
||||
|
||||
```bash
|
||||
terraform stacks create # Create new Stack (interactive)
|
||||
terraform stacks fmt # Format Stack files
|
||||
terraform stacks list # Show all Stacks
|
||||
terraform stacks version # Display version
|
||||
terraform stacks deployment-group rerun -deployment-group=... # Rerun deployment
|
||||
```
|
||||
|
||||
## Monitoring Deployments with HCP Terraform API
|
||||
|
||||
For programmatic monitoring in automation, CI/CD, or non-interactive environments (like AI agents), use the HCP Terraform API instead of CLI watch commands. The API provides endpoints for:
|
||||
|
||||
- Configuration status and validation
|
||||
- Deployment group summaries
|
||||
- Deployment run status
|
||||
- Deployment step details (plan/apply)
|
||||
- Error diagnostics with file locations and code snippets
|
||||
- Stack outputs via artifacts endpoint
|
||||
|
||||
**Key points:**
|
||||
- CLI watch commands stream indefinitely and don't work in automation
|
||||
- Use artifacts endpoint to retrieve Stack outputs: `GET /api/v2/stack-deployment-steps/{step-id}/artifacts?name=apply-description`
|
||||
- Diagnostics endpoint requires `stack_deployment_step_id` query parameter
|
||||
- Artifacts endpoint returns HTTP 307 redirect (use `curl -L`)
|
||||
|
||||
For complete API workflow, authentication, polling best practices, and example scripts, see `references/api-monitoring.md`.
|
||||
|
||||
## Common Patterns
|
||||
|
||||
**Component Dependencies**: Dependencies are automatically inferred when one component references another's output (e.g., `subnet_ids = component.vpc.private_subnet_ids`).
|
||||
|
||||
**Multi-Region Deployment**: Use `for_each` on providers and components to deploy across multiple regions. Each region gets its own provider configuration and component instances.
|
||||
|
||||
**Deferred Changes**: Stacks support deferred changes to handle dependencies where values are only known after apply. This enables complex multi-component deployments where some resources depend on runtime values from other components (cluster endpoints, generated passwords, etc.).
|
||||
|
||||
For complete examples including multi-region deployments, component dependencies, deferred changes patterns, and linked Stacks, see `references/examples.md`.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Component Granularity**: Create components for logical infrastructure units that share a lifecycle
|
||||
2. **Module Compatibility**:
|
||||
- Modules used with Stacks cannot include provider blocks (configure providers in Stack configuration)
|
||||
- **Test public registry modules** before using in production Stacks - some modules may have compatibility issues
|
||||
- Consider using raw resources for critical infrastructure if module compatibility is uncertain
|
||||
- Example: Some terraform-aws-modules versions have been found to have compatibility issues with Stacks (e.g., ALB and ECS modules)
|
||||
3. **State Isolation**: Each deployment has its own isolated state
|
||||
4. **Input Variables**: Use variables for values that differ across deployments; use locals for shared values
|
||||
5. **Provider Lock Files**: Always generate and commit `.terraform.lock.hcl` to version control
|
||||
6. **Naming Conventions**: Use descriptive names for components and deployments
|
||||
7. **Deployment Groups**: You can organize deployments into deployment groups. Deployment groups enable auto-approval rules, logical organization, and provide a foundation for scaling. Deployment groups are an HCP Terraform Premium tier feature
|
||||
8. **Testing**: Test Stack configurations in dev/staging deployments before production
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Circular Dependencies**: Refactor to break circular references or use intermediate components.
|
||||
|
||||
**Deployment Destruction**: Cannot destroy from UI. Set `destroy = true` in deployment block, upload configuration, and HCP Terraform creates a destroy run.
|
||||
|
||||
**Empty Diagnostics**: Add required `stack_deployment_step_id` query parameter to diagnostics API requests.
|
||||
|
||||
**Module Compatibility**: Test public registry modules before production use. Some modules may have compatibility issues with Stacks.
|
||||
|
||||
## References
|
||||
|
||||
For detailed documentation, see:
|
||||
- `references/component-blocks.md` - Complete component block reference with all arguments and syntax
|
||||
- `references/deployment-blocks.md` - Complete deployment block reference with all configuration options
|
||||
- `references/linked-stacks.md` - Publish outputs and upstream inputs for linking Stacks together
|
||||
- `references/examples.md` - Complete working examples for multi-region and component dependencies
|
||||
- `references/api-monitoring.md` - Full API workflow for programmatic monitoring and automation
|
||||
- `references/troubleshooting.md` - Detailed troubleshooting guide for common issues and solutions
|
||||
543
.agents/skills/terraform-stacks/references/api-monitoring.md
Normal file
543
.agents/skills/terraform-stacks/references/api-monitoring.md
Normal 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.
|
||||
476
.agents/skills/terraform-stacks/references/component-blocks.md
Normal file
476
.agents/skills/terraform-stacks/references/component-blocks.md
Normal 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
|
||||
}
|
||||
```
|
||||
391
.agents/skills/terraform-stacks/references/deployment-blocks.md
Normal file
391
.agents/skills/terraform-stacks/references/deployment-blocks.md
Normal 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`.
|
||||
1529
.agents/skills/terraform-stacks/references/examples.md
Normal file
1529
.agents/skills/terraform-stacks/references/examples.md
Normal file
File diff suppressed because it is too large
Load Diff
187
.agents/skills/terraform-stacks/references/linked-stacks.md
Normal file
187
.agents/skills/terraform-stacks/references/linked-stacks.md
Normal 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`.
|
||||
671
.agents/skills/terraform-stacks/references/troubleshooting.md
Normal file
671
.agents/skills/terraform-stacks/references/troubleshooting.md
Normal 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
|
||||
Reference in New Issue
Block a user