Terraspace All: Including and Excluding Stacks

The terraspace all command allows you to deploy multiple stacks simultaneously. You can configure Terraspace to include or exclude specific stacks that terraspace all will deploy.

Important: See Including and Excluding Stacks Inference at the bottom. In general, it’s recommendeded to have terraspace all infer which stacks are allowed be deployed from config.allow.stacks and config.deny.stacks instead of configuring config.all.include_stacks explicitly. Though config.all.include_stacks can be used if you need the control.

Example 1: Simple Include

Let’s say we have 3 stacks:

$ terraspace list
app/stacks/stack1
app/stacks/stack2
app/stacks/stack3

config/app.rb

Terraspace.configure do |config|
  config.all.include_stacks = ["stack1"]
end

This means that terraspace all will only consider stack1. Example:

$ terraspace all up
    terraspace up stack1 # batch 1

You can still deploy stack2 and stack3 directly with terraspace up

terraspace up stack2
terraspace up stack3

Example 2: Simple Exclude

Let’s say we have 3 stacks:

$ terraspace list
app/stacks/stack1
app/stacks/stack2
app/stacks/stack3

config/app.rb

Terraspace.configure do |config|
  config.all.exclude_stacks = ["stack1"]
end

This means that terraspace all will not consider stack1. Example:

$ terraspace all up
    terraspace up stack2 # batch 1
    terraspace up stack3 # batch 1

You can still deploy stack1 directly with terraspace up

terraspace up stack1

Example 3: Exclude Stacks for Specific Environments with Multiple Files

An interesting example is if you want terraspace all to deploy specific stacks only for certain environments.

Let’s say we have 3 stacks:

$ terraspace list
app/stacks/route53
app/stacks/stack1
app/stacks/stack2

Route53 resources are global and may not always make sense to deploy on a per env basis. Here are the terraspace up commands to explain how we only want these stacks to deploy with specific environments.

TS_ENV=global terraspace up route53
TS_ENV=dev    terraspace up stack1
TS_ENV=prod   terraspace up stack1
TS_ENV=dev    terraspace up stack2
TS_ENV=prod   terraspace up stack2

To configure terraspace all so that it will only deploy based on the environment, we can do this:

config/app.rb

Terraspace.configure do |config|
  config.all.exclude_stacks = ["route53"]
end

So we generally exclude the route53 stack from all environments. Then we override the config include it in the global environment like so:

config/envs/global.rb

Terraspace.configure do |config|
  config.all.exclude_stacks = nil # important to override and reset this
  config.all.include_stacks = ["route53"]
end

Running terraspace all results in something like this:

$ TS_ENV=dev terraspace all up
Will run:
    terraspace up stack1 # batch 1
    terraspace up stack2 # batch 1
$ TS_ENV=prod terraspace all up
Will run:
    terraspace up stack1 # batch 1
    terraspace up stack2 # batch 1

And for TS_ENV=global

$ TS_ENV=global terraspace all up
Will run:
    terraspace up route53 # batch 1

Example 4: Exclude Stacks for Specific Environments with One File

Some folks may prefer or feel it’s easier not to have the configuration in multiple files. To use one file instead.

config/app.rb

Terraspace.configure do |config|
  if Terraspace.env == "global"
    config.all.include_stacks = ["route53"]
  else
    config.all.exclude_stacks = ["route53"]
  end
end

This is one of the benefits that come with the way Terraspace handles configurations. You have access to the power of a full programming language, Ruby, when you need it. Of course, with great power comes great responsibility.

Question: Should you use multiple files or one file?

It depends. If the configurations are small, it’s clearer to have them in one file. If the configurations get complex, separating them into multiple files makes sense. It also depends a lot on personal preference.

Example 5: Complete Customization with Object That Implements Call

For even more control over which stacks are included or excluded you can assign an object that implements call and returns an Array. This is an advanced technique. Example:

class Includer
  def call(stack)
    ["route53"] if Terraspace.env == "global"
  end
end
class Excluder
  def call(stack)
    ["route53"] unless Terraspace.env == "global"
  end
end

Terraspace.configure do |config|
  config.all.include_stacks = Includer
  config.all.exclude_stacks = Excluder
end

The Excluder class defines the call method and takes in the stack argument. It’s value is the name of current stack being deployed. The Includer and Excluder classes should return an Array or nil. Returning nil is the same as not setting the option at all.

Including and Excluding Stacks Inference

If you are using config.allow.stacks and config.deny.stacks, then config.all.include_stacks and config.all.exclude_stacks are inferred from those settings. For example:

Let’s say we have 3 stacks:

$ terraspace list
app/stacks/route53
app/stacks/stack1
app/stacks/stack2

If you want to only allow terraspace up to deploy the route53 stack for TS_ENV=global environment. Configure:

config/app.rb

Terraspace.configure do |config|
  if Terraspace.env == "global"
    config.allow.stacks = ["route53"]
  else
    config.deny.stacks = ["route53"]
  end
end

Then the route53 stack will only be allowed to be deployed with TS_ENV=global. If TS_ENV=dev, then the route53 stack will be prevented from deploying. Example:

$ TS_ENV=global terraspace up route53
...
Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:
...
$ TS_ENV=dev terraspace up route53
Building .terraspace-cache/dev/stacks/route53
ERROR: The configs do not allow this.
This stack is not allowed to be used for TS_ENV=dev
Deny stacks: route53

By default, terraspace set the options like this:

config.all.include_stacks = config.allow.stacks
config.all.exclude_stacks = config.deny.stacks

This means:

$ TS_ENV=global terraspace all up
Will run:
    terraspace up route53 # batch 1

And

$ TS_ENV=dev terraspace all up
Will run:
    terraspace up stack1 # batch 1
    terraspace up stack2 # batch 1

The the route53 stack will only be deployed when TS_ENV=global by inference. You only have to set the config.allow.stacks and config.deny.stacks and terraspace all automatically knew what to do.

You can change this default behavior with config.all.consider_allow_deny_stacks = false. You would then need to set config.all.include_stacks and config.all.exclude_stacks explicitly.

More docs: Config Restricting Stacks.

More tools: