All skills
hashicorp avatar

/terraform-stacks

@4451cec official
by hashicorphashicorp/agent-skills880 stars
130

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.

Use this Skill: https://skilld.dev/gh/hashicorp/agent-skills/terraform-stacks

This session only. Nothing lands on disk.

referencesexamples.md

≈7.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Terraform Stacks Complete Examples

Complete, working examples for common Terraform Stacks scenarios.

Table of Contents

  1. Simple Single-Region Stack
  2. Stack with Private Registry Modules
  3. Multi-Environment Stack
  4. Multi-Region Stack
  5. Linked Stacks (Cross-Stack Dependencies)
  6. Multi-Cloud Stack
  7. Complete AWS Production Stack
  8. Destroying Deployments

Simple Single-Region Stack

Basic Stack with a single environment deployment.

File Structure

simple-stack/
├── variables.tfcomponent.hcl
├── providers.tfcomponent.hcl
├── components.tfcomponent.hcl
├── deployments.tfdeploy.hcl
└── modules/
    └── webapp/
        ├── main.tf
        ├── variables.tf
        └── outputs.tf

variables.tfcomponent.hcl

variable "aws_region" {
  type    = string
  default = "us-west-1"
}

variable "identity_token" {
  type      = string
  ephemeral = true
}

variable "role_arn" {
  type = string
}

variable "app_name" {
  type = string
}

providers.tfcomponent.hcl

required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "~> 5.7.0"
  }
}

provider "aws" "main" {
  config {
    region = var.aws_region

    assume_role_with_web_identity {
      role_arn           = var.role_arn
      web_identity_token = var.identity_token
    }
  }
}

components.tfcomponent.hcl

component "webapp" {
  source = "./modules/webapp"

  inputs = {
    app_name = var.app_name
    region   = var.aws_region
  }

  providers = {
    aws = provider.aws.main
  }
}

deployments.tfdeploy.hcl

identity_token "aws" {
  audience = ["aws.workload.identity"]
}

deployment "production" {
  inputs = {
    aws_region     = "us-west-1"
    app_name       = "my-webapp"
    role_arn       = "arn:aws:iam::123456789012:role/terraform-stacks"
    identity_token = identity_token.aws.jwt
  }
}

# Deployment groups
deployment_group "production" {
  deployments = [deployment.production]
}

Stack with Private Registry Modules

Example Stack using modules from a private HCP Terraform registry, combining both private and public registry sources.

File Structure

private-registry-stack/
├── variables.tfcomponent.hcl
├── providers.tfcomponent.hcl
├── components.tfcomponent.hcl
├── outputs.tfcomponent.hcl
└── deployments.tfdeploy.hcl

variables.tfcomponent.hcl

variable "aws_region" {
  type    = string
  default = "us-west-2"
}

variable "environment" {
  type = string
}

variable "identity_token" {
  type      = string
  ephemeral = true
}

variable "role_arn" {
  type = string
}

variable "vpc_cidr" {
  type    = string
  default = "10.0.0.0/16"
}

variable "app_name" {
  type = string
}

variable "db_password" {
  type      = string
  sensitive = true
}

providers.tfcomponent.hcl

required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "~> 5.7.0"
  }
  random = {
    source  = "hashicorp/random"
    version = "~> 3.5.0"
  }
}

provider "aws" "main" {
  config {
    region = var.aws_region

    assume_role_with_web_identity {
      role_arn           = var.role_arn
      web_identity_token = var.identity_token
    }

    default_tags {
      tags = {
        Environment = var.environment
        ManagedBy   = "Terraform Stacks"
        Application = var.app_name
      }
    }
  }
}

provider "random" "main" {
  config {}
}

components.tfcomponent.hcl

locals {
  name_prefix = "${var.app_name}-${var.environment}"
  common_tags = {
    Project     = var.app_name
    Environment = var.environment
  }
}

# Using a private registry module for VPC
component "vpc" {
  source  = "app.terraform.io/my-org/vpc/aws"
  version = "2.1.0"

  inputs = {
    name_prefix         = local.name_prefix
    cidr_block          = var.vpc_cidr
    availability_zones  = ["${var.aws_region}a", "${var.aws_region}b", "${var.aws_region}c"]
    enable_nat_gateway  = true
    single_nat_gateway  = var.environment != "prod"
    tags                = local.common_tags
  }

  providers = {
    aws = provider.aws.main
  }
}

# Using a private registry module for security groups
component "security_groups" {
  source  = "app.terraform.io/my-org/security-groups/aws"
  version = "1.5.2"

  inputs = {
    vpc_id      = component.vpc.vpc_id
    name_prefix = local.name_prefix
    environment = var.environment
  }

  providers = {
    aws = provider.aws.main
  }
}

# Using a public registry module for RDS
component "database" {
  source  = "terraform-aws-modules/rds/aws"
  version = "~> 6.0"

  inputs = {
    identifier           = "${local.name_prefix}-db"
    engine               = "postgres"
    engine_version       = "15.3"
    family               = "postgres15"
    major_engine_version = "15"
    instance_class       = var.environment == "prod" ? "db.t3.large" : "db.t3.micro"

    allocated_storage     = var.environment == "prod" ? 100 : 20
    db_name               = replace(var.app_name, "-", "_")
    username              = "dbadmin"
    password              = var.db_password
    port                  = 5432

    db_subnet_group_name   = component.vpc.database_subnet_group_name
    vpc_security_group_ids = [component.security_groups.database_sg_id]

    backup_retention_period = var.environment == "prod" ? 30 : 7
    skip_final_snapshot     = var.environment != "prod"
    deletion_protection     = var.environment == "prod"

    tags = local.common_tags
  }

  providers = {
    aws = provider.aws.main
  }
}

# Using a private registry module for application infrastructure
component "application" {
  source  = "app.terraform.io/my-org/ecs-application/aws"
  version = "3.2.1"

  inputs = {
    name_prefix           = local.name_prefix
    vpc_id                = component.vpc.vpc_id
    private_subnet_ids    = component.vpc.private_subnet_ids
    public_subnet_ids     = component.vpc.public_subnet_ids
    app_security_group_id = component.security_groups.app_sg_id

    container_image       = "my-org/my-app:latest"
    container_port        = 8080
    desired_count         = var.environment == "prod" ? 3 : 1

    environment_variables = {
      ENVIRONMENT    = var.environment
      DATABASE_HOST  = component.database.db_instance_endpoint
      DATABASE_NAME  = component.database.db_instance_name
    }

    tags = local.common_tags
  }

  providers = {
    aws = provider.aws.main
  }
}

outputs.tfcomponent.hcl

output "vpc_id" {
  type        = string
  description = "VPC ID"
  value       = component.vpc.vpc_id
}

output "application_url" {
  type        = string
  description = "Application load balancer URL"
  value       = component.application.load_balancer_dns
}

output "database_endpoint" {
  type        = string
  description = "Database endpoint"
  value       = component.database.db_instance_endpoint
  sensitive   = true
}

deployments.tfdeploy.hcl

identity_token "aws" {
  audience = ["aws.workload.identity"]
}

locals {
  role_arn = "arn:aws:iam::123456789012:role/terraform-stacks"
}

deployment "development" {
  inputs = {
    aws_region     = "us-west-2"
    environment    = "dev"
    app_name       = "myapp"
    vpc_cidr       = "10.0.0.0/16"
    db_password    = "dev-password-change-me"
    role_arn       = local.role_arn
    identity_token = identity_token.aws.jwt
  }
}

deployment "production" {
  inputs = {
    aws_region     = "us-east-1"
    environment    = "prod"
    app_name       = "myapp"
    vpc_cidr       = "10.1.0.0/16"
    db_password    = "prod-password-use-secrets-manager"
    role_arn       = local.role_arn
    identity_token = identity_token.aws.jwt
  }
}

# Deployment groups
deployment_group "development" {
  deployments = [deployment.development]
}

deployment_group "production" {
  deployments = [deployment.production]
}

Key Points

  • Private registry modules use the format app.terraform.io/<org>/<module>/<provider>
  • Version constraints ensure consistent module versions across environments
  • Mixed sources: Combining private registry modules (VPC, security groups, application) with public registry modules (RDS)
  • Authentication: HCP Terraform workspaces automatically authenticate to private registries; CLI users need credentials configured
  • Terraform Enterprise: Replace app.terraform.io with your instance hostname

Multi-Environment Stack

Stack with development, staging, and production deployments.

variables.tfcomponent.hcl

variable "aws_region" {
  type = string
}

variable "environment" {
  type = string
}

variable "instance_count" {
  type = number
}

variable "instance_type" {
  type = string
}

variable "identity_token" {
  type      = string
  ephemeral = true
}

variable "role_arn" {
  type = string
}

providers.tfcomponent.hcl

required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "~> 5.7.0"
  }
}

provider "aws" "this" {
  config {
    region = var.aws_region

    assume_role_with_web_identity {
      role_arn           = var.role_arn
      web_identity_token = var.identity_token
    }

    default_tags {
      tags = {
        Environment = var.environment
        ManagedBy   = "Terraform Stacks"
      }
    }
  }
}

components.tfcomponent.hcl

locals {
  name_prefix = "myapp-${var.environment}"
}

component "vpc" {
  source = "./modules/vpc"

  inputs = {
    name_prefix = local.name_prefix
    cidr_block  = "10.0.0.0/16"
  }

  providers = {
    aws = provider.aws.this
  }
}

component "compute" {
  source = "./modules/compute"

  inputs = {
    name_prefix    = local.name_prefix
    vpc_id         = component.vpc.vpc_id
    subnet_ids     = component.vpc.private_subnet_ids
    instance_count = var.instance_count
    instance_type  = var.instance_type
  }

  providers = {
    aws = provider.aws.this
  }
}

outputs.tfcomponent.hcl

output "vpc_id" {
  type  = string
  value = component.vpc.vpc_id
}

output "load_balancer_url" {
  type  = string
  value = component.compute.load_balancer_url
}

deployments.tfdeploy.hcl

identity_token "aws" {
  audience = ["aws.workload.identity"]
}

locals {
  role_arn = "arn:aws:iam::123456789012:role/terraform-stacks"

  environments = {
    dev = {
      region         = "us-east-1"
      instance_count = 1
      instance_type  = "t3.micro"
    }
    staging = {
      region         = "us-west-1"
      instance_count = 2
      instance_type  = "t3.small"
    }
    prod = {
      region         = "us-west-1"
      instance_count = 5
      instance_type  = "t3.large"
    }
  }
}

deployment "development" {
  inputs = {
    aws_region     = local.environments.dev.region
    environment    = "dev"
    instance_count = local.environments.dev.instance_count
    instance_type  = local.environments.dev.instance_type
    role_arn       = local.role_arn
    identity_token = identity_token.aws.jwt
  }
}

deployment "staging" {
  inputs = {
    aws_region     = local.environments.staging.region
    environment    = "staging"
    instance_count = local.environments.staging.instance_count
    instance_type  = local.environments.staging.instance_type
    role_arn       = local.role_arn
    identity_token = identity_token.aws.jwt
  }
}

deployment "production" {
  inputs = {
    aws_region     = local.environments.prod.region
    environment    = "prod"
    instance_count = local.environments.prod.instance_count
    instance_type  = local.environments.prod.instance_type
    role_arn       = local.role_arn
    identity_token = identity_token.aws.jwt
  }
}

# Deployment groups
deployment_group "development" {
  deployments = [deployment.development]
}

deployment_group "non_production" {
  deployments = [deployment.staging]
}

deployment_group "production" {
  deployments = [deployment.production]
}

# Auto-approve dev deployments
deployment_auto_approve "dev_auto" {
  deployment_group = deployment_group.development

  check {
    condition = context.plan.applyable
    reason    = "Development plans must be applyable"
  }
}

Multi-Region Stack

Stack that deploys identical infrastructure across multiple AWS regions.

variables.tfcomponent.hcl

variable "regions" {
  type = set(string)
}

variable "identity_token" {
  type      = string
  ephemeral = true
}

variable "role_arn" {
  type = string
}

variable "app_name" {
  type = string
}

providers.tfcomponent.hcl

required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "~> 5.7.0"
  }
}

provider "aws" "regional" {
  for_each = var.regions

  config {
    region = each.value

    assume_role_with_web_identity {
      role_arn           = var.role_arn
      web_identity_token = var.identity_token
    }

    default_tags {
      tags = {
        Region    = each.value
        ManagedBy = "Terraform Stacks"
        AppName   = var.app_name
      }
    }
  }
}

components.tfcomponent.hcl

component "regional_infrastructure" {
  for_each = var.regions

  source = "./modules/regional-infra"

  inputs = {
    region      = each.value
    app_name    = var.app_name
    name_suffix = each.value
  }

  providers = {
    aws = provider.aws.regional[each.value]
  }
}

component "global_route53" {
  source = "./modules/route53"

  inputs = {
    app_name     = var.app_name
    domain_name  = "example.com"
    regional_lbs = {
      for region, comp in component.regional_infrastructure :
      region => comp.load_balancer_dns
    }
  }

  # Use one region's provider for global resources
  providers = {
    aws = provider.aws.regional["us-west-1"]
  }
}

outputs.tfcomponent.hcl

output "regional_endpoints" {
  type = map(string)
  value = {
    for region, comp in component.regional_infrastructure :
    region => comp.load_balancer_url
  }
}

output "global_domain" {
  type  = string
  value = component.global_route53.domain_name
}

deployments.tfdeploy.hcl

identity_token "aws" {
  audience = ["aws.workload.identity"]
}

locals {
  regions = ["us-west-1", "us-east-1", "eu-west-1"]
}

deployment "multi_region_prod" {
  inputs = {
    regions        = toset(local.regions)
    app_name       = "my-global-app"
    role_arn       = "arn:aws:iam::123456789012:role/terraform-stacks"
    identity_token = identity_token.aws.jwt
  }
}

# Deployment groups
deployment_group "production" {
  deployments = [deployment.multi_region_prod]
}

Linked Stacks (Cross-Stack Dependencies)

Two Stacks where the application Stack depends on the network Stack.

Network Stack

network-stack/variables.tfcomponent.hcl
variable "vpc_cidr" {
  type = string
}

variable "environment" {
  type = string
}

variable "aws_region" {
  type = string
}

variable "identity_token" {
  type      = string
  ephemeral = true
}

variable "role_arn" {
  type = string
}
network-stack/providers.tfcomponent.hcl
required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "~> 5.7.0"
  }
}

provider "aws" "this" {
  config {
    region = var.aws_region

    assume_role_with_web_identity {
      role_arn           = var.role_arn
      web_identity_token = var.identity_token
    }
  }
}
network-stack/components.tfcomponent.hcl
component "vpc" {
  source = "./modules/vpc"

  inputs = {
    cidr_block  = var.vpc_cidr
    environment = var.environment
  }

  providers = {
    aws = provider.aws.this
  }
}

component "security_groups" {
  source = "./modules/security-groups"

  inputs = {
    vpc_id      = component.vpc.vpc_id
    environment = var.environment
  }

  providers = {
    aws = provider.aws.this
  }
}
network-stack/outputs.tfcomponent.hcl
output "vpc_id" {
  type  = string
  value = component.vpc.vpc_id
}

output "private_subnet_ids" {
  type  = list(string)
  value = component.vpc.private_subnet_ids
}

output "public_subnet_ids" {
  type  = list(string)
  value = component.vpc.public_subnet_ids
}

output "app_security_group_id" {
  type  = string
  value = component.security_groups.app_sg_id
}
network-stack/deployments.tfdeploy.hcl
identity_token "aws" {
  audience = ["aws.workload.identity"]
}

locals {
  role_arn = "arn:aws:iam::123456789012:role/terraform-stacks"
}

deployment "network" {
  inputs = {
    aws_region     = "us-west-1"
    environment    = "production"
    vpc_cidr       = "10.0.0.0/16"
    role_arn       = local.role_arn
    identity_token = identity_token.aws.jwt
  }
}

# Publish outputs for other stacks
publish_output "vpc_id_network" {
  type  = string
  value = deployment.network.vpc_id
}

publish_output "private_subnet_ids" {
  type  = list(string)
  value = deployment.network.private_subnet_ids
}

publish_output "public_subnet_ids" {
  type  = list(string)
  value = deployment.network.public_subnet_ids
}

publish_output "app_security_group_id" {
  type  = string
  value = deployment.network.app_security_group_id
}

# Deployment groups
deployment_group "network" {
  deployments = [deployment.network]
}

Application Stack

application-stack/variables.tfcomponent.hcl
variable "vpc_id" {
  type = string
}

variable "subnet_ids" {
  type = list(string)
}

variable "security_group_id" {
  type = string
}

variable "instance_count" {
  type = number
}

variable "aws_region" {
  type = string
}

variable "identity_token" {
  type      = string
  ephemeral = true
}

variable "role_arn" {
  type = string
}
application-stack/providers.tfcomponent.hcl
required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "~> 5.7.0"
  }
}

provider "aws" "this" {
  config {
    region = var.aws_region

    assume_role_with_web_identity {
      role_arn           = var.role_arn
      web_identity_token = var.identity_token
    }
  }
}
application-stack/components.tfcomponent.hcl
component "application" {
  source = "./modules/app"

  inputs = {
    vpc_id            = var.vpc_id
    subnet_ids        = var.subnet_ids
    security_group_id = var.security_group_id
    instance_count    = var.instance_count
  }

  providers = {
    aws = provider.aws.this
  }
}
application-stack/deployments.tfdeploy.hcl
identity_token "aws" {
  audience = ["aws.workload.identity"]
}

# Reference the network stack
upstream_input "network" {
  type   = "stack"
  source = "app.terraform.io/my-org/my-project/network-stack"
}

deployment "application" {
  inputs = {
    aws_region        = "us-west-1"
    vpc_id            = upstream_input.network.vpc_id_network
    subnet_ids        = upstream_input.network.private_subnet_ids
    security_group_id = upstream_input.network.app_security_group_id
    instance_count    = 3
    role_arn          = "arn:aws:iam::123456789012:role/terraform-stacks"
    identity_token    = identity_token.aws.jwt
  }
}

# Deployment groups
deployment_group "application" {
  deployments = [deployment.application]
}

Multi-Cloud Stack

Stack that deploys to both AWS and Azure.

variables.tfcomponent.hcl

variable "aws_region" {
  type = string
}

variable "azure_location" {
  type = string
}

variable "aws_identity_token" {
  type      = string
  ephemeral = true
}

variable "aws_role_arn" {
  type = string
}

variable "azure_identity_token" {
  type      = string
  ephemeral = true
}

variable "azure_subscription_id" {
  type = string
}

variable "azure_tenant_id" {
  type = string
}

variable "azure_client_id" {
  type = string
}

variable "app_name" {
  type = string
}

providers.tfcomponent.hcl

required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "~> 5.7.0"
  }
  azurerm = {
    source  = "hashicorp/azurerm"
    version = "~> 3.0"
  }
}

provider "aws" "this" {
  config {
    region = var.aws_region

    assume_role_with_web_identity {
      role_arn           = var.aws_role_arn
      web_identity_token = var.aws_identity_token
    }
  }
}

provider "azurerm" "this" {
  config {
    features {}

    subscription_id = var.azure_subscription_id
    tenant_id       = var.azure_tenant_id
    client_id       = var.azure_client_id

    use_oidc = true
    oidc_token = var.azure_identity_token
  }
}

components.tfcomponent.hcl

component "aws_infrastructure" {
  source = "./modules/aws-infra"

  inputs = {
    region   = var.aws_region
    app_name = var.app_name
  }

  providers = {
    aws = provider.aws.this
  }
}

component "azure_infrastructure" {
  source = "./modules/azure-infra"

  inputs = {
    location = var.azure_location
    app_name = var.app_name
  }

  providers = {
    azurerm = provider.azurerm.this
  }
}

deployments.tfdeploy.hcl

identity_token "aws" {
  audience = ["aws.workload.identity"]
}

identity_token "azure" {
  audience = ["api://AzureADTokenExchange"]
}

deployment "multi_cloud" {
  inputs = {
    aws_region             = "us-west-1"
    azure_location         = "westus2"
    app_name               = "my-multi-cloud-app"
    aws_role_arn           = "arn:aws:iam::123456789012:role/terraform-stacks"
    aws_identity_token     = identity_token.aws.jwt
    azure_subscription_id  = "12345678-1234-1234-1234-123456789012"
    azure_tenant_id        = "87654321-4321-4321-4321-210987654321"
    azure_client_id        = "11111111-1111-1111-1111-111111111111"
    azure_identity_token   = identity_token.azure.jwt
  }
}

# Deployment groups
deployment_group "multi_cloud" {
  deployments = [deployment.multi_cloud]
}

Complete AWS Production Stack

Full production-grade Stack with VPC, RDS, ECS, and monitoring.

variables.tfcomponent.hcl

variable "aws_region" {
  type        = string
  description = "AWS region"
}

variable "environment" {
  type        = string
  description = "Environment name"
}

variable "vpc_cidr" {
  type        = string
  description = "VPC CIDR block"
}

variable "app_name" {
  type        = string
  description = "Application name"
}

variable "db_instance_class" {
  type        = string
  description = "RDS instance class"
}

variable "ecs_desired_count" {
  type        = number
  description = "Desired ECS task count"
}

variable "identity_token" {
  type      = string
  ephemeral = true
}

variable "role_arn" {
  type = string
}

providers.tfcomponent.hcl

required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "~> 5.7.0"
  }
  random = {
    source  = "hashicorp/random"
    version = "~> 3.5.0"
  }
}

provider "aws" "this" {
  config {
    region = var.aws_region

    assume_role_with_web_identity {
      role_arn           = var.role_arn
      web_identity_token = var.identity_token
    }

    default_tags {
      tags = {
        Environment = var.environment
        Application = var.app_name
        ManagedBy   = "Terraform Stacks"
      }
    }
  }
}

provider "random" "this" {
  config {}
}

components.tfcomponent.hcl

locals {
  name_prefix = "${var.app_name}-${var.environment}"
}

component "vpc" {
  source = "./modules/vpc"

  inputs = {
    name_prefix = local.name_prefix
    cidr_block  = var.vpc_cidr
    azs_count   = 3
  }

  providers = {
    aws = provider.aws.this
  }
}

component "security_groups" {
  source = "./modules/security-groups"

  inputs = {
    name_prefix = local.name_prefix
    vpc_id      = component.vpc.vpc_id
  }

  providers = {
    aws = provider.aws.this
  }
}

component "rds" {
  source = "./modules/rds"

  inputs = {
    name_prefix        = local.name_prefix
    instance_class     = var.db_instance_class
    subnet_ids         = component.vpc.private_subnet_ids
    security_group_ids = [component.security_groups.database_sg_id]
  }

  providers = {
    aws    = provider.aws.this
    random = provider.random.this
  }
}

component "ecs_cluster" {
  source = "./modules/ecs-cluster"

  inputs = {
    name_prefix = local.name_prefix
  }

  providers = {
    aws = provider.aws.this
  }
}

component "ecs_service" {
  source = "./modules/ecs-service"

  inputs = {
    name_prefix      = local.name_prefix
    cluster_id       = component.ecs_cluster.cluster_id
    desired_count    = var.ecs_desired_count
    subnet_ids       = component.vpc.private_subnet_ids
    security_group_id = component.security_groups.app_sg_id
    database_endpoint = component.rds.endpoint
  }

  providers = {
    aws = provider.aws.this
  }
}

component "alb" {
  source = "./modules/alb"

  inputs = {
    name_prefix       = local.name_prefix
    vpc_id            = component.vpc.vpc_id
    subnet_ids        = component.vpc.public_subnet_ids
    security_group_id = component.security_groups.alb_sg_id
    target_group_arn  = component.ecs_service.target_group_arn
  }

  providers = {
    aws = provider.aws.this
  }
}

component "cloudwatch" {
  source = "./modules/cloudwatch"

  inputs = {
    name_prefix  = local.name_prefix
    cluster_name = component.ecs_cluster.cluster_name
    service_name = component.ecs_service.service_name
  }

  providers = {
    aws = provider.aws.this
  }
}

outputs.tfcomponent.hcl

output "load_balancer_url" {
  type        = string
  description = "Application load balancer URL"
  value       = component.alb.dns_name
}

output "database_endpoint" {
  type        = string
  description = "RDS endpoint"
  value       = component.rds.endpoint
  sensitive   = true
}

output "vpc_id" {
  type  = string
  value = component.vpc.vpc_id
}

output "ecs_cluster_name" {
  type  = string
  value = component.ecs_cluster.cluster_name
}

deployments.tfdeploy.hcl

identity_token "aws" {
  audience = ["aws.workload.identity"]
}

locals {
  role_arn = "arn:aws:iam::123456789012:role/terraform-stacks"
}

deployment "staging" {
  inputs = {
    aws_region        = "us-west-1"
    environment       = "staging"
    app_name          = "myapp"
    vpc_cidr          = "10.1.0.0/16"
    db_instance_class = "db.t3.small"
    ecs_desired_count = 2
    role_arn          = local.role_arn
    identity_token    = identity_token.aws.jwt
  }
}

deployment "production" {
  inputs = {
    aws_region        = "us-west-1"
    environment       = "production"
    app_name          = "myapp"
    vpc_cidr          = "10.0.0.0/16"
    db_instance_class = "db.r5.large"
    ecs_desired_count = 5
    role_arn          = local.role_arn
    identity_token    = identity_token.aws.jwt
  }
}

# Deployment groups
deployment_group "staging" {
  deployments = [deployment.staging]
}

deployment_group "production" {
  deployments = [deployment.production]
}

# Auto-approve staging with safety checks
deployment_auto_approve "staging_safe" {
  deployment_group = deployment_group.staging

  check {
    condition = context.plan.changes.remove == 0
    reason    = "Cannot auto-approve deletions in staging"
  }

  check {
    condition = context.plan.applyable
    reason    = "Plan must be applyable"
  }
}

Testing Configurations

Validate Stack Configuration

terraform stacks providers lock
terraform stacks validate

Plan Specific Deployment

terraform stacks plan --deployment=development
terraform stacks plan --deployment=production

Apply Deployment

terraform stacks apply --deployment=staging

Destroying Deployments

Example of safely removing a deployment from your Stack.

Scenario

You want to decommission the "development" deployment while keeping staging and production active.

Step 1: Mark Deployment for Destruction

Update your deployments.tfdeploy.hcl file to set destroy = true:

identity_token "aws" {
  audience = ["aws.workload.identity"]
}

locals {
  role_arn = "arn:aws:iam::123456789012:role/terraform-stacks"
}

# Mark this deployment for destruction
deployment "development" {
  inputs = {
    aws_region     = "us-east-1"
    environment    = "dev"
    instance_count = 1
    role_arn       = local.role_arn
    identity_token = identity_token.aws.jwt
  }
  destroy = true  # This tells HCP Terraform to destroy all resources
}

# Keep these deployments active
deployment "staging" {
  inputs = {
    aws_region     = "us-west-1"
    environment    = "staging"
    instance_count = 2
    role_arn       = local.role_arn
    identity_token = identity_token.aws.jwt
  }
}

deployment "production" {
  inputs = {
    aws_region     = "us-west-1"
    environment    = "prod"
    instance_count = 5
    role_arn       = local.role_arn
    identity_token = identity_token.aws.jwt
  }
}

# Deployment groups
deployment_group "staging" {
  deployments = [deployment.staging]
}

deployment_group "production" {
  deployments = [deployment.production]
}

Step 2: Plan and Apply

# Review the destruction plan
terraform stacks plan --deployment=development

# Apply the destruction
terraform stacks apply --deployment=development

HCP Terraform will destroy all resources in the development deployment.

Step 3: Remove the Deployment Block

After the deployment is successfully destroyed, remove the entire deployment block from your configuration:

identity_token "aws" {
  audience = ["aws.workload.identity"]
}

locals {
  role_arn = "arn:aws:iam::123456789012:role/terraform-stacks"
}

# deployment "development" block has been removed

deployment "staging" {
  inputs = {
    aws_region     = "us-west-1"
    environment    = "staging"
    instance_count = 2
    role_arn       = local.role_arn
    identity_token = identity_token.aws.jwt
  }
}

deployment "production" {
  inputs = {
    aws_region     = "us-west-1"
    environment    = "prod"
    instance_count = 5
    role_arn       = local.role_arn
    identity_token = identity_token.aws.jwt
  }
}

# Deployment groups
deployment_group "staging" {
  deployments = [deployment.staging]
}

deployment_group "production" {
  deployments = [deployment.production]
}

Important Notes

  • Provider Authentication: The destroy argument ensures your configuration retains the provider authentication needed to destroy resources
  • Do Not Remove Immediately: Don't remove the deployment block until after the destruction is complete
  • Verify Before Removing: Check HCP Terraform UI to confirm all resources are destroyed before removing the block
  • Alternative: You could manually destroy resources through HCP Terraform UI, but using destroy = true is the recommended approach for maintaining infrastructure-as-code practices

Source: SKILL.md on GitHub

1 warning17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill serves as a technical reference guide and documentation suite for managing infrastructure deployments using stack configurations. It provides structural examples, CLI usage guidelines, and API monitoring practices that follow industry-standard workflows. No security concerns were identified.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer7mo

    5/7 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 4451cec. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated 2 months ago
Other metadata
metadata
{
  "lifecycle-status": "active",
  "copyright": "Copyright IBM Corp. 2026",
  "version": "0.0.1"
}
  • terraform
  • hcl
  • infrastructure-as-code
  • stacks
  • multi-environment
  • deployment
  • hcp-terraform
  • orchestration
  • components
  • oidc

README badge

README badge for hashicorp/agent-skills/terraform-stacks

Provides configuration and deployment patterns for Terraform Stacks, HashiCorp's declarative infrastructure orchestration layer. Covers component definitions, provider configuration with OIDC workload identity, deployment instances across environments, and HCP Terraform integration including variable sets and linked Stacks. Requires Terraform v1.13 or later.

Generated from the current SKILL.md.

What Terraform version is required?
Terraform v1.13.x or later is required to use Terraform Stacks and the CLI plugin. Specify the exact version in a .terraform-version file at the root of your Stack repository.
What file extensions do Stacks use?
Component configuration uses .tfcomponent.hcl and deployment configuration uses .tfdeploy.hcl. Both must be at the root level of the Stack repository.
How do I reference module sources?
Components can reference modules from local paths (./modules/vpc), the public registry (terraform-aws-modules/vpc/aws), private registry (app.terraform.io/org-name/vpc/aws), or Git sources (git::https://...). A modules/ directory is only required when using local module sources.
What authentication method does this skill recommend?
Use workload identity (OIDC) with identity_token blocks and assume_role_with_web_identity in provider configuration. This avoids long-lived static credentials and provides temporary, scoped credentials per deployment run.
How do I destroy a deployment?
Set destroy = true in the deployment block, upload the configuration, approve the destroy run, then remove the deployment block entirely.

Generated from the current SKILL.md. These answers refresh after source changes.