Current Terragrunt Structure?
Am guessing that the current terragrunt setup is something like this?
├── dev
│ ├── client1
│ │ ├── stack1
│ │ │ └── terragrunt.hcl
│ │ └── stack2
│ │ └── terragrunt.hcl
│ └── client2
│ ├── stack1
│ │ └── terragrunt.hcl
│ └── stack2
│ └── terragrunt.hcl
└── prod
├── client1
│ ├── stack1
│ │ └── terragrunt.hcl
│ └── stack2
│ └── terragrunt.hcl
└── client2
├── stack1
│ └── terragrunt.hcl
└── stack2
└── terragrunt.hcl
Then you deploy one all the stacks for a client like this?
cd dev/client1 && terragrunt apply-all
cd dev/client2 && terragrunt apply-all
cd prod/client1 && terragrunt apply-all
And to deploy to all clients?
cd dev && terragrunt apply-all
cd prod && terragrunt apply-all
Unsure if that’s the structure you have.
Approach #1: Using Terraspace Built-In Defaults
One approach is using different TS_ENV
with the client in the value. Though the built-in defaults may not be a good fit for the above mono-repo structure, it would look something like this:
app/stacks
├── stack1
│ ├── main.tf
│ ├── outputs.tf
│ ├── tfvars
│ │ ├── dev-client1.tfvars
│ │ ├── dev-client2.tfvars
│ │ ├── prod-client1.tfvars
│ │ └── prod-client2.tfvars
│ └── variables.tf
└── stack2
├── main.tf
├── outputs.tf
├── tfvars
│ ├── dev-client1.tfvars
│ ├── dev-client2.tfvars
│ ├── prod-client1.tfvars
│ └── prod-client2.tfvars
└── variables.tf
The commands would be:
TS_ENV=dev-client1 terraspace all up
TS_ENV=dev-client2 terraspace all up
TS_ENV=prod-client1 terraspace all up
That’s one way it could be done. Don’t think it’s great because it mixes multiple client tfvars in the same folder.
Approach #2: Terraspace Custom Layering
Another approach is to define your own custom layering. Terraspace can be customized for different needs. Something like:
config/inits/layering.rb
Terraspace::Layering.module_eval do
def pre_layers
["clients/#{ENV['CLIENT']}/#{@mod.name}"]
end
end
The Terraspace project structure would be:
app
└── stacks
├── stack1
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
└── stack2
├── main.tf
├── outputs.tf
└── variables.tf
config/terraform/tfvars
└── clients
├── client1
│ ├── stack1
│ │ ├── dev.tfvars
│ │ └── prod.tfvars
│ └── stack2
│ ├── dev.tfvars
│ └── prod.tfvars
└── client2
├── stack1
│ ├── dev.tfvars
│ └── prod.tfvars
└── stack2
├── dev.tfvars
└── prod.tfvars
The commands would be:
CLIENT=client1 TS_ENV=dev terraspace build
CLIENT=client2 TS_ENV=dev terraspace build
CLIENT=client1 TS_ENV=prod terraspace build
Click to see example build:
$ CLIENT=client1 terraspace build
Building one stack to build all stacks
Building .terraspace-cache/us-west-2/dev/stacks/stack1
Built in .terraspace-cache/us-west-2/dev/stacks/stack1
$ tree .terraspace-cache
.terraspace-cache
└── us-west-2
└── dev
├── modules
│ └── example
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
└── stacks
├── demo
├── stack1
│ ├── 1-project-clients-client1-stack1-dev.auto.tfvars
│ ├── backend.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── provider.tf
│ └── variables.tf
└── stack2
├── 1-project-clients-client1-stack2-dev.auto.tfvars
├── backend.tf
├── main.tf
├── outputs.tf
├── provider.tf
└── variables.tf
That might be the right amount of trade-offs.
Approach #3: Separate Terraspace Projects
Consider separating repos per client and make the stacks themselves reusable. Then would also consider using the seed/tfvars/stacks/STACK
folder instead of the app/stack/STACK/tfvars
. See: https://terraspace.cloud/docs/tfvars/lookups/ This decouples the tfvars files from the vendor/stacks
folder which would be managed via a Terrafile. Then you’ll be able to:
cd client1-repo && TS_ENV=dev terraspace all up
cd client2-repo && TS_ENV=dev terraspace all up
cd client1-repo && TS_ENV=prod terraspace all up
It comes at a cost of the complexity of multiple repos, though. Some like this and some do not
Deploying All Clients
Terraspace is more env focus and so you cannot deploy to all clients at once. Think that’s better handled as separate wrapper script/command that calls Terraspace multiple times. There are tradeoffs. Some more thoughts here: