This post builds on my previous article Terraform Stacks: A Revisit, where I reviewed what Terraform Stacks had evolved to from an idea or beta. The concept of having that layer of abstraction around deployment was novel and I am still looking forward to some of the improvements we will see in the future. In this post, I am looking to review the terraform stacks
subcommand in detail based on the updates from HashiConf. Some of the updates actually broke some live demo plans of some fellow HashiCorp ambassadors on Day 2 of HashiConf :) You know who you are.
Anyone wanting a good reference of the changes from beta to General availability(GA) should read the official documentation here. I do feel there are some elements missing or scattered around the documentation elsewhere which could have been surfaced up better. At a high level, terraform stacks
subcommand provides a comprehensive set of options organized into primary operations for configuration, deployment groups, and deployment runs.
Prerequisites and Setup
Before diving into the commands, you'll need an active HCP Terraform setup. This includes setting up an HCP Terraform organization, creating a project ( or use the Default Project
) within that organization, and generating a token with appropriate permissions for stack operations. The CLI relies on specific environment variables including TF_STACKS_ORGANIZATION_NAME
,TF_STACKS_PROJECT_NAME
,TF_STACKS_STACK_NAME
, and TF_TOKEN_app_terraform_io
for authentication and targeting the correct resources if not explicitly provided in the commands as arguments or options.
Building on the examples from my previous post, we'll explore the foundational commands that every Terraform Stacks workflow begins with. The terraform stacks init command prepares your configuration directory, while providers-lock ensures consistent provider versions across environments. The validate command checks your configuration syntax before deployment. These commands form the foundation that enables the more advanced operations covered in the following sections.
Note: I have
terraform
aliased totf
.
If you run terraform stacks
post GA, you would see these additional subcommands for configuration
, deployment-group
and deployment-run
. It would be a miss not to introduce these without which the post will be difficult to comprehend.
tf stacks
Usage: terraform stacks [global options] <command> [args]
The available commands for execution are listed below.
Primary Commands:
init Prepare the configuration directory for further commands
providers-lock Write out dependency locks for the configured providers
validate Check whether the configuration is valid
create Create a stack
list List stacks for a given organization and/or project.
version Show the current Stacks Plugin version
fmt Reformat your Terraform Stacks configuration to a canonical format
Sub-commands:
configuration Manage stack configuration
deployment-group Manage deployment groups
deployment-run Manage deployment runs
Global options (use these before the subcommand, if any):
-chdir=DIR Switch to a different working directory before executing the
given subcommand.
-plugin-cache-dir=DIR Override the default directory where the stack plugin binary is cached.
-stacks-version An alias for the "version" subcommand.
-no-color=BOOL Disable color output. Must be explicitly set to true or false.
Usage help:
-usage Show this usage output or the usage for a specified subcommand.
Use after the command: terraform stacks <command> -usage
- Components : is a replacement of modules in a way; though it is more than modules.Translates to what infrastructure, or components, should be part of the Stack.
- Deployments : Where and how many times to deploy the infrastructure in the Stack.
- Deployment Groups : Combine a set of deployment targets into a group. Allows to set some specific approval rules for each deployment groups based on specific contextual conditions.
- Configuration : Everything including the above with the terraform version , lock files which make up a Stack
General notes before we dive in
I do like the -json usage across the subcommands. May be we are gearing up to a usage of these commands from an agent :)
Note: Can we get some short form syntaxes for these subcommand options already please ?
Create a Stack from your terminal
I somehow feel this command is a little incomplete as it stands now. I am ignoring the global options as they are the same 4 always across Stacks' subcommands. Lets create a Stack in three different variants and see what it translates to in the HCP Terraform UI.
Option 1
tf stacks create -organization-name ne-devops -project-name "Default Project" -stack-name "manu-option1"
Creating stack...
Stack: Created stack 'manu-option1'
The result is ...
Option 2
tf stacks create -organization-name ne-devops -project-name "Default Project" -stack-name "manu-option2" -with-template
Creating stack...
Stack: Created stack 'manu-option2'
Creating example stacks configuration...
Stack: Created example stack configuration for 'manu-option2'
The result is ...
Reviewing the settings...
I do not see a difference in the stack created on the HCP Terraform UI with the -with-template
option supplied with the terraform stacks create
command. Now the argument reads as below
-with-template Generate boilerplate configuration
May be it is a placeholder for more things to come.
Option 3
Though this is more of a variant only in terms of how the data is presented back to the user on the completion of a command, I see the value when it comes to passing these logs to some external systems or even to an operations agent.
tf stacks create -organization-name ne-devops -project-name "Default Project" -stack-name "manu-option3" -json
{"@level":"info","@message":"Creating stack","@module":"terraform.stacks.ui","@timestamp":"2025-10-01T21:29:06.137667-04:00","format_version":"1.0","type":"create_in_progress"}
{"@level":"info","@message":"Created stack","@module":"terraform.stacks.ui","@timestamp":"2025-10-01T21:29:06.421967-04:00","format_version":"1.0","type":"create_success"}
What am I missing ?
The stacks which are created are barebones at this point. What would make this command wholesome is the ability to associate the stacks with a VCS connection and to be able to start that first run of the configuration fetch that way.
List the stacks
Lets run the stacks list
without any arguments on a new terminal.
tf stacks list
╷
│ Error: Missing required flag `-organization-name`
│
│ Please provide the `-organization-name` flag with the name of the target organization.
│ Alternatively, set the ENV VAR 'TF_STACKS_ORGANIZATION_NAME'.
╵
Failure! Could not list stacks
As expected, we do see the need to specify the Organization name.
tabular format
tf stacks list -organization-name ne-devops
Stack Name | Stack ID | Created At | Updated At |
manu-option2 | st-Y2J3wa6yKBSP83Hc | Oct 02, 2025 13:02 | Oct 02, 2025 13:02 |
manu-option1 | st-31HY1439bDRVawex | Oct 02, 2025 13:00 | Oct 02, 2025 13:00 |
app-stack | st-g7uPLPR4pnWyvT7e | Oct 01, 2025 17:44 | Oct 01, 2025 17:44 |
manuchn-stacks-2025 | st-jcgFCdyai89kcn5S | Sep 30, 2025 14:08 | Sep 30, 2025 14:08 |
pet-nulls-stack | st-uyBoA811rPgNzvw3 | Sep 08, 2024 04:41 | Sep 08, 2024 04:41 |
external_module_stack | st-wfXoXu2a56n3GC4Y | Mar 09, 2024 22:00 | Mar 09, 2024 23:09 |
pet-nulls-stack | st-M6kzAWsw7e7Ldep8 | Feb 28, 2024 22:57 | Feb 28, 2024 22:59 |
json format
I was thinking the stacks list
would benefit from a column project
till I ran the -json
format. The data doesn't seem to be present in the output.
tf stacks list -organization-name ne-devops -json
{"format_version":"1.0","organization_name":"ne-devops","project_id":"","stacks":[{"created_at":"2025-10-02T13:02:49Z","id":"st-Y2J3wa6yKBSP83Hc","name":"manu-option2","updated_at":"2025-10-02T13:02:49Z"},{"created_at":"2025-10-02T13:00:22Z","id":"st-31HY1439bDRVawex","name":"manu-option1","updated_at":"2025-10-02T13:00:22Z"},{"created_at":"2025-10-01T17:44:34Z","id":"st-g7uPLPR4pnWyvT7e","name":"app-stack","updated_at":"2025-10-01T17:44:34Z"},{"created_at":"2025-09-30T14:08:37Z","id":"st-jcgFCdyai89kcn5S","name":"manuchn-stacks-2025","updated_at":"2025-09-30T14:08:37Z"},{"created_at":"2024-09-08T04:41:32Z","id":"st-uyBoA811rPgNzvw3","name":"pet-nulls-stack","updated_at":"2024-09-08T04:41:32Z"},{"created_at":"2024-03-09T22:00:44Z","id":"st-wfXoXu2a56n3GC4Y","name":"external_module_stack","updated_at":"2024-03-09T23:09:22Z"},{"created_at":"2024-02-28T22:57:52Z","id":"st-M6kzAWsw7e7Ldep8","name":"pet-nulls-stack","updated_at":"2024-02-28T22:59:54Z"}]}
Filter by project
All the stacks I had listed were part of the Default Project
anyway. So it is not not going to show any difference here.
❯ tf stacks list -organization-name ne-devops -project-name "Default Project"
Stack Name | Stack ID | Created At | Updated At |
manu-option2 | st-Y2J3wa6yKBSP83Hc | Oct 02, 2025 13:02 | Oct 02, 2025 13:02 |
manu-option1 | st-31HY1439bDRVawex | Oct 02, 2025 13:00 | Oct 02, 2025 13:00 |
app-stack | st-g7uPLPR4pnWyvT7e | Oct 01, 2025 17:44 | Oct 01, 2025 17:44 |
manuchn-stacks-2025 | st-jcgFCdyai89kcn5S | Sep 30, 2025 14:08 | Sep 30, 2025 14:08 |
external_module_stack | st-wfXoXu2a56n3GC4Y | Mar 09, 2024 22:00 | Mar 09, 2024 23:09 |
pet-nulls-stack | st-M6kzAWsw7e7Ldep8 | Feb 28, 2024 22:57 | Feb 28, 2024 22:59 |
❯ tf stacks list -organization-name ne-devops -project-name "experiments"
Stack Name | Stack ID | Created At | Updated At |
Tying this back to the HCP Terraform UI; why is Stacks not an entity of its own similar to the workspaces ?
Configuration related
As we are peeling the layers of the Stacks onion, we have till now looked at adding the Stacks frame onto the HCP Terraform organization. But it is missing any sort of configuration items to provision any infrastructure to a target platform.
Note: I have configured a variableset on my HCP Terraform organization with an IAM role which allows any stacks in my org to provision some resources ( S3, VPCs) if they fall under my HCP Terraform organization. I am not going to dive into that as that OIDC based setup hasn't changed from the time of beta.
The commands below are run on a directory which has my HashiConf-Stack repo cloned. So this command is supposed to supplement the stacks create
in my mind. For most of the commands below, a stack is the frame of reference. So you need either a combination of organization-name
, project-name
, stack-name
or stack-id
. I do wonder why we need the project name since we can list the stacks without a project reference in the stacks list
command.
list
I am going to use one of the stack ids from the create operations we did earlier. As expected, there are no configuration items associated with the stack manu-option1
at this point.
> tf stacks configuration list -stack-id st-31HY1439bDRVawex
Sequence Number | Configuration ID |
> tf stacks configuration list -stack-id st-31HY1439bDRVawex --json
{"configurations":null,"format_version":"1.0","stack_id":"st-31HY1439bDRVawex"}
Upload
Running this from a non-stacks directory.
tf stacks configuration upload -stack-id st-31HY1439bDRVawex
╷
│ Error: Missing .terraform-version file
│
│ Terraform Stacks requires a .terraform-version file alongside the root .tfcomponent.hcl files.
╵
╷
│ Error: Failed to open source bundle
│
│ Failed to open source bundle: cannot read manifest: open /Users/manuchn/Documents/2025/TFC/Hashi/experiments/manuchandrasekhar.com/.terraform/modules/terraform-sources.json: no such file or directory. You may need to run `terraform stacks init`.
╵
failed to upload stack configuration
So , you need to initialize the stack , provide a lock configuration and a terraform version. We didn't specify a terraform version when we created the stack using the cli, which I believe doesn't have a version association till a configuration is uploaded.
tf stacks init
Provider "hashicorp/aws" already locked at "5.100.0".
Provider "hashicorp/random" already locked at "3.7.2".
Downloading "hashicorp/aws"@"5.100.0"....
Downloading "hashicorp/random"@"3.7.2"....
Success! Configuration has been initialized and more commands can now be executed.
❯ tf stacks configuration upload -stack-id st-31HY1439bDRVawex
Uploading stack configuration...
Configuration for Stack (id: 'st-31HY1439bDRVawex') was uploaded
Configuration ID: stc-S5Sr3y57SWDLd5G5
Sequence Number: 1
See run at: https://app.terraform.io/app/ne-devops/projects/prj-qbcM8hb5k4EUCGj4/stacks/st-31HY1439bDRVawex/configurations-ga/stc-S5Sr3y57SWDLd5G5
The url which has the configuration or run leads you to the page below if you open the configuration 1
Note: You do not see an apply creating resources as I had
destroy=true
set on the deployments for the first time.
Subsequently a configuration list
renders the below.
tf stacks configuration list -stack-id st-31HY1439bDRVawex
Sequence Number | Configuration ID |
1 | stc-S5Sr3y57SWDLd5G5 |
Note: I have seen some glitches here where the
Configurations
remain blank even when there is an active configuration. This could be some caching issues on my browser.
Let's re-run the configuration upload with the destroy=true
removed in the tfdeploy.hcl
file. I did make a mistake on my configuration and had to run the configuration upload twice. So you will see configuration versions up to 3 in the list when we do those. With the auto-approve on:
Diving into the deployment in HCP Terraform, you can see that the resources have been created in my target AWS account using the configuration upload without a VCS connection.
The state from the Stacks standpoint is slightly different from how you are used to from a workspace or pre-GA.
The command configurations upload
has this option speculative
which can prevent triggering any deployments via this configuration upload. Lets try this again with the option set to true.
tf stacks configuration upload -stack-id st-31HY1439bDRVawex -speculative
Uploading stack configuration...
Speculative Configuration for Stack (id: 'st-31HY1439bDRVawex') was uploaded
Configuration ID: stc-PbHnPRArTLBABzCC
See run at: https://app.terraform.io/app/ne-devops/projects/prj-qbcM8hb5k4EUCGj4/stacks/st-31HY1439bDRVawex/configurations-ga/stc-PbHnPRArTLBABzCC
My issue here is with the phrasing on the option speculative
. You are triggering a deployment run under a configuration null
. I believe this maps to a speculative plan run on workspaces. I think this should be updated to state that explicitly than using the word deployment here.
-speculative: Upload will be speculative and will not trigger any deployments.
Note: I hope they have an option to see this as a json with some download options.
Watch
This is your command line equivalent of live loading on the HCP Terraform UI or the kubectl watch
command.
tf stacks configuration upload -stack-id st-31HY1439bDRVawex
Uploading stack configuration...
Configuration for Stack (id: 'st-31HY1439bDRVawex') was uploaded
Configuration ID: stc-e6dfkfXL3HgDq1U2
Sequence Number: 4
See run at: https://app.terraform.io/app/ne-devops/projects/prj-qbcM8hb5k4EUCGj4/stacks/st-31HY1439bDRVawex/configurations-ga/stc-e6dfkfXL3HgDq1U2
❯ tf stacks configuration watch -configuration-id stc-e6dfkfXL3HgDq1U2
Entering the command launches you to a watch interface which looks to provide you with a real time update on what is happening with the configuration or deployment. Keep in mind that I have the auto-approve set on the deployment groups which is making the response below skip a Pending
state.
[Configuration Sequence Number: 4]
✓ Configuration: 'stc-e6dfkfXL3HgDq1U2' [Completed] [24s]
- Deployment Group: 'test_stack' [Deploying] [33s]
Press q to quit
....
[Configuration Sequence Number: 4]
✓ Configuration: 'stc-e6dfkfXL3HgDq1U2' [Completed] [24s]
✓ Deployment Group: 'test_stack' [Succeeded] [37s]
Press q to quit
There are additional commands in the configuration side including fetch
which requires a VCS connection. I am going to bundle that along the VCS based setup in a different post.
Environment Setup
I could possibly have set these below environment variables than having to add those options in all my commands. But I preferred to know what I am working with , this time around. Maybe in a CI based environment, the environment variable setup would come in handy.
export TF_STACKS_ORGANIZATION_NAME="ne-devops"
export TF_STACKS_PROJECT_NAME="Default Project"
export TF_STACKS_STACK_NAME="app-stack"
Conclusion
With the recent updates , Terraform Stacks provides a unified CLI experience for managing deployments directly against HCP Terraform organizations, offering an alternative to VCS-based deployment workflows. I hope that current implementation gaps will be addressed as platform teams adopt Stacks for managing baseline infrastructure that development teams build upon. As I have always said, Stacks introduces new approaches for provisioning foundational infrastructure across target platforms. However, documented post-GA limitations currently restrict broader adoption in scenarios like AWS OU-based deployments that would typically use StackSets or something similar.
- HCP Terraform supports up to a maximum of 20 deployments for a Stack.
- Deployment groups currently support a single deployment for a Stack.
I planned to write about deployment groups and runs. But with the length of the post, I felt it is better to break this into a few different posts.
Top comments (0)