Skip to content

Combining Expressions

Negate filter expressions using the ! prefix. Negated expressions are always evaluated after all positive expressions have been evaluated.

Terminal window
# Exclude by name
terragrunt find --filter '!app1'
# Exclude by path
terragrunt find --filter '!./prod/**'
# Exclude by type
terragrunt 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

Use the | operator to refine results from left to right. Results must match all filters in the chain to be included.

Terminal window
# Find all components in ./prod/** that are also units
terragrunt find --filter './prod/** | type=unit'
# Find all components in ./prod/** that are not units
terragrunt find --filter './prod/** | !type=unit'
# You can chain as many filters as you want to further refine the results
terragrunt find --filter './dev/** | type=unit | !name=unit1'
  • Directoryprod
    • Directoryunits
      • Directoryunit1 <— Matched by first filter
        • terragrunt.hcl
      • Directoryunit2 <— Matched by first filter
        • terragrunt.hcl
    • Directorystacks
      • Directorystack1 <— Matched by second filter
        • terragrunt.stack.hcl
      • Directorystack2 <— Matched by second filter
        • terragrunt.stack.hcl
  • Directorydev
    • Directoryunits
      • Directoryunit1
        • terragrunt.hcl
      • Directoryunit2 <— Matched by third filter
        • terragrunt.hcl
    • Directorystacks
      • Directorystack1
        • terragrunt.stack.hcl
      • Directorystack2
        • terragrunt.stack.hcl

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.

Terminal window
# Referencing the file tree above
terragrunt 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.

Terminal window
# Note that we still need to specify 'prod' here in the path.
terragrunt run --working-dir prod --filter '[main...HEAD] | ./prod/units/**'

Specify multiple --filter flags to merge results from multiple filters.

Terminal window
# 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

When a filter query starts with a negation (!), the result is applied after all positive filters have been applied.

This means that if you have a filter query like this:

Terminal window
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.

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.

Terminal window
$ terragrunt find --filter '[main...HEAD]' --filter ./live/foo
live/foo
live/foo

The 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.

Terminal window
terragrunt find --filter '[main...HEAD]' | awk '{printf "{%s}\n", $0}' > /tmp/diffs.txt
terragrunt run --all --filters-file /tmp/diffs.txt -- plan