Combining Expressions
Negated Expressions
Section titled “Negated Expressions”Negate filter expressions using the ! prefix. A query that does nothing but negate is always evaluated after every other filter has been evaluated.
# Exclude by nameterragrunt find --filter '!app1'
# Exclude by pathterragrunt find --filter '!./prod/**'
# Exclude by typeterragrunt find --filter '!type=stack'Directoryenvs
Directoryprod
Directoryapps
Directoryapp1 <— Excluded by both the first and second filter
- terragrunt.hcl
Directoryapp2 <— Matched by all filters except the second filter
- terragrunt.hcl
Directorystacks
Directorystack1 <— Excluded by both the second and third filter
- terragrunt.stack.hcl
Directorystage
Directoryapps
Directoryapp1 <— Matched by all filters except the first filter
- terragrunt.hcl
Directoryapp2 <— Matched by all filters
- terragrunt.hcl
Directorystacks
Directorystack1 <— Matched by all filters except the third filter
- terragrunt.stack.hcl
Intersection Expressions
Section titled “Intersection Expressions”Use the | operator to refine results from left to right. Results must match all filters in the chain to be included.
# Find all components in ./prod/** that are also unitsterragrunt find --filter './prod/** | type=unit'
# Find all components in ./prod/** that are not unitsterragrunt find --filter './prod/** | !type=unit'
# You can chain as many filters as you want to further refine the resultsterragrunt find --filter './dev/** | type=unit | !name=unit1'Directoryprod
Directoryunits
Directoryunit1 <— Matched by the first filter
- terragrunt.hcl
Directoryunit2 <— Matched by the first filter
- terragrunt.hcl
Directorystacks
Directorystack1 <— Matched by the second filter
- terragrunt.stack.hcl
Directorystack2 <— Matched by the second filter
- terragrunt.stack.hcl
Directorydev
Directoryunits
Directoryunit1
- terragrunt.hcl
Directoryunit2 <— Matched by the third filter
- terragrunt.hcl
Directorystacks
Directorystack1
- terragrunt.stack.hcl
Directorystack2
- terragrunt.stack.hcl
Path resolution in intersection chains
Section titled “Path resolution in intersection chains”In an intersection chain (A | B | C), the left-most expression determines which components flow through the rest of the chain. Each component carries its own discovery context, including a working directory where the component was discovered.
Relative path expressions (like ./apps/*) in any part of the chain resolve against the working directory of the component’s discovery context — not the user’s current working directory. These two are the same when discovering components in the current working directory.
# Referencing the file tree aboveterragrunt run --working-dir prod --filter './units/** | unit1'When using Git expressions, components are discovered in temporary Git worktrees (see How it works for more details. As such, relative paths resolve relative to the root of the Git worktree where the component was discovered.
# Note that we still need to specify 'prod' here in the path.terragrunt run --working-dir prod --filter '[main...HEAD] | ./prod/units/**'Union Expressions
Section titled “Union Expressions”Specify multiple --filter flags to merge results from multiple filters.
# Find components named 'unit1' and 'stack1'terragrunt find --filter unit1 --filter stack1
# Find components in ./envs/prod/* and ./envs/stage/*terragrunt find --filter './envs/prod/*' --filter './envs/stage/*'
# Find components named 'stack2' _except_ those in ./envs/prod/* and ./envs/stage/*terragrunt find --filter stack2 --filter '!./envs/prod/**' --filter '!./envs/stage/**'Directoryenvs
Directoryprod
Directoryunit1 <— Matched by the first filter and the second filter
- terragrunt.hcl
Directoryunit2 <— Matched by the second filter
- terragrunt.hcl
Directorystack1 <— Matched by the first filter and the second filter
- terragrunt.stack.hcl
Directorystack2 <— Matched by the second filter
- terragrunt.stack.hcl
Directorystage
Directoryunit1 <— Matched by the first filter and the second filter
- terragrunt.hcl
Directoryunit2 <— Matched by the second filter
- terragrunt.hcl
Directorystack1 <— Matched by the first filter and the second filter
- terragrunt.stack.hcl
Directorystack2 <— Matched by the second filter
- terragrunt.stack.hcl
Directorydev
Directoryunit1 <— Matched by the first filter
- terragrunt.hcl
Directoryunit2
- terragrunt.hcl
Directorystack1 <— Matched by the first filter
- terragrunt.stack.hcl
Directorystack2 <— Matched by the third filter
- terragrunt.stack.hcl
Unions of negated filters
Section titled “Unions of negated filters”When every expression in a filter query is negated, the query is applied after all other filters, removing from their combined results.
This means that if you have a filter query like this:
terragrunt find --filter '!type=unit' --filter 'name=unit1'The result will be the components that are not units and are named unit1.
This means that you should be able to expect negative filters to take effect regardless of how other positive filters may result in the addition of results.
A query that mixes a negation with a positive expression, like '!./prod/** | type=stack', is not one of these. The | operator refines from left to right, so the query selects the stacks outside ./prod/**, and it unions with the other filters the same way any other query does.
# Stacks outside ./prod/**, plus everything named 'unit1'terragrunt find --filter '!./prod/** | type=stack' --filter 'name=unit1'Unions with Git expressions
Section titled “Unions with Git expressions”Union deduplication in filter results is based on the absolute path of each discovered component. When combining Git expressions with non-Git expressions in a union, it might seem like the same unit has appeared twice. e.g.
$ terragrunt find --filter '[main...HEAD]' --filter ./live/foolive/foolive/fooThe reason for this is usually that a unit with the same name has been discovered twice: once discovered from your current working directory and once from a git worktree. From Terragrunt’s perspective, these are two different units and can be operated on independently.
If your intent is not to use Terragrunt in this way (to operate on units in temporary Git worktrees and units in your current working directory independently), you can use the find command instead to perform the logic of discovering components that have changed between Git references, then separately perform a run against units in your current working directory.
terragrunt find --filter '[main...HEAD]' | awk '{printf "{%s}\n", $0}' > /tmp/diffs.txtterragrunt run --all --filters-file /tmp/diffs.txt -- plan