Repository: gruntwork-io/terragrunt

Issue:
Add support for `--includes` in `find --json`
# Enhancement

To support the need to quickly identify units that include particular configuration, add support for the `--includes` flag to the `find` command, and enrich the JSON output of the results with an array of includes. Users can use this value to programmatically discover units that include a particular configuration.

For example, if the command `terragrunt find --format=json` returns the following:

```bash
$ terragrunt find --format=json | jq '.[:3]'
[
  {
    "type": "stack",
    "path": "basic"
  },
  {
    "type": "unit",
    "path": "basic/units/chick"
  },
  {
    "type": "unit",
    "path": "basic/units/chicken"
  }
]
```

Have it return the following if `chick` and `chicken` both include `root.hcl` at the root of the project:

```bash
$ terragrunt find --format=json
[
  {
    "type": "stack",
    "path": "basic"
  },
  {
    "type": "unit",
    "path": "basic/units/chick",
    "includes": {
      "root": "root.hcl"
    }
  },
  {
    "type": "unit",
    "path": "basic/units/chicken",
    "includes": {
      "root": "root.hcl"
    }
  }
]
```

This design might need tweaking, based on discovery during implementation. We'll need to play with it to see if this meets the needs of users.

After initial implementation, let's cut a pre-release and see how users feel about using the feature.

---

# Original bug report

## Describe the bug

The --units-that-include (--terragrunt-modules-that-include) flag returns false positive results. This behavior first appeared in  v0.73.2 and seems to be in all versions since then (I didn't test all of them though).

## Steps To Reproduce

The simplest reproducer I came up with is to create a structure like this.

```
.
├── bar
│   ├── main.tf
│   └── terragrunt.hcl
├── cloud.hcl
└── foo
    ├── main.tf
    └── terragrunt.hcl
```

All files can be empty with just single include in one of the terragrunt.hcl (bar/terragrunt.hcl in my case)

```hcl
include "cloud" {
  path = find_in_parent_folders("cloud.hcl")
}
```

And run `terragrunt --units-that-include=cloud.hcl output-module-groups`

## Expected behavior

Just a single unit should be returned.

## Nice to haves

- [ ] Terminal output

```
❯ terragrunt --units-that-include=cloud.hcl output-module-groups
[INFO] Getting version from tgenv-version-name
[INFO] TGENV_VERSION is 0.77.6
11:20:49.196 WARN   The global `--units-that-include` flag has moved to the `run` command and will not be accessible as a global flag in a future version of Terragrunt. Use `run --units-that-include=cloud.hcl` instead.
11:20:49.197 INFO   Terragrunt Cache server is listening on 127.0.0.1:35207
11:20:49.197 INFO   Start Terragrunt Cache server
{
  "Group 1": [
    "/home/dsecik/aws/bar",
    "/home/dsecik/aws/foo"
  ]
}
11:20:49.199 INFO   Shutting down Terragrunt Cache server...
11:20:49.199 INFO   Terragrunt Cache server stopped
```
```
❯ terragrunt --units-that-include=cloud.hcl output-module-groups
[INFO] Getting version from tgenv-version-name
[INFO] TGENV_VERSION is 0.73.1
11:21:06.560 WARN   The `--units-that-include` global flag is moved to `run` command and will be removed from the global flags in a future version. Use `--run --units-that-include=cloud.hcl` instead.
11:21:06.561 INFO   Terragrunt Cache server is listening on 127.0.0.1:44689
11:21:06.562 INFO   Start Terragrunt Cache server
{
  "Group 1": [
    "/home/dsecik/aws/bar"
  ]
}
11:21:06.563 INFO   Shutting down Terragrunt Cache server...
11:21:06.563 INFO   Terragrunt Cache server stopped
```

## Versions

- Terragrunt version: v0.77.6
- OpenTofu/Terraform version: v1.11.3
- Environment details (Ubuntu 20.04, Windows 10, etc.): Fedora 41

## Additional context

N/A


Hints from issue comments:
@yhakbar: Hey @dsecik ,

The `output-module-groups` command doesn't support the `units-that-include` flag. It just doesn't throw an error when unsupported flags are used, which it probably should.

The command will be deprecated as part of the [CLI Redesign](https://github.com/gruntwork-io/terragrunt/issues/3445) in favor of integrations with the [find](https://terragrunt.gruntwork.io/docs/reference/cli-options/#find) and [list](https://terragrunt.gruntwork.io/docs/reference/cli-options/#list) commands, which will have tooling to make it so that users can filter units that include particular configuration.

I'll leave this issue open to confirm that the `find` and `list` commands have replaced this desired behavior.@dsecik: Hi @yhakbar,

Thanks for your reply. What exactly you mean by _The output-module-groups command doesn't support the units-that-include flag._ ? It is even explicitly mentioned as useful  combination in [TG cli docs](https://terragrunt.gruntwork.io/docs/reference/cli-options/#output-module-groups) and it worked fine for a long time for us.@yhakbar: Oh geez. I should have done more digging. Thanks for pointing that out, @dsecik .

I assumed that the flag was never integrated into it. We'll get this fixed.@igor-raits: @yhakbar do you know some ETA for the fix? it is quite painful to run whole-repo pipelines when only 1-2 folders should have run instead. Thank you!@yhakbar: No, unfortunately, @ignatenkobrain .

We're currently focusing on getting stacks and the CLI redesign finished up, so it might take us a while to get to this.

If you would like resolution of this issue sped up, please perform more investigation, and potentially open a pull request to resolve the bug.@dsecik: Hi @yhakbar I noticed the CLI redesign is done and the migration guide suggest to replace `output-module-groups` by new `find`/`list` commands. I quickly checked the options of both but didn't find a way how to use it to find all units(.hcl files) that include some hcl. Am I missing something or the alternative to this is just not implemented (yet)?@yhakbar: It's just not implemented yet, @dsecik .

I can't give you a timeline, but we'll make sure to add it.@yhakbar: I've updated the title and description of the ticket to indicate how this will be handled, when it is.

Instead of supporting `--units-that-include` for the `find`/`list` commands, we'll look to support enriching the JSON output, like we do with dependencies, etc.

This will avoid introducing functionality that's potentially incompatible with future updates, like:
https://github.com/gruntwork-io/terragrunt/issues/4060@dsecik: FTR while debugging other parts of our CI/CD pipelines we figured out the problem is wrong flag positioning. Since v0.73.2 flags before actual command are ignored (while accepted in prior versions). Considering the `output-module-groups` command is deprecated, adding support to identify units that include particular configuration to `find` command is still valid.

```
❯ terragrunt output-module-groups
[INFO] Getting version from tgenv-version-name
[INFO] TGENV_VERSION is 0.78.1
13:20:18.626 WARN   The `output-module-groups` command is deprecated and will be removed in a future version of Terragrunt. Use `terragrunt find --json` instead.
13:20:18.627 INFO   Terragrunt Cache server is listening on 127.0.0.1:37199
13:20:18.627 INFO   Start Terragrunt Cache server
{
  "Group 1": [
    "/home/dsecik/aws/bar",
    "/home/dsecik/aws/foo"
  ]
}
13:20:18.629 INFO   Shutting down Terragrunt Cache server...
13:20:18.629 INFO   Terragrunt Cache server stopped

 
❯ terragrunt --units-that-include=cloud.hcl output-module-groups
[INFO] Getting version from tgenv-version-name
[INFO] TGENV_VERSION is 0.78.1
13:20:25.393 WARN   The `output-module-groups` command is deprecated and will be removed in a future version of Terragrunt. Use `terragrunt find --json` instead.
13:20:25.394 INFO   Terragrunt Cache server is listening on 127.0.0.1:35909
13:20:25.394 INFO   Start Terragrunt Cache server
{
  "Group 1": [
    "/home/dsecik/aws/bar",
    "/home/dsecik/aws/foo"
  ]
}
13:20:25.395 INFO   Shutting down Terragrunt Cache server...
13:20:25.395 INFO   Terragrunt Cache server stopped


❯ terragrunt output-module-groups --units-that-include=cloud.hcl
[INFO] Getting version from tgenv-version-name
[INFO] TGENV_VERSION is 0.78.1
13:20:28.726 WARN   The `output-module-groups` command is deprecated and will be removed in a future version of Terragrunt. Use `terragrunt find --json` instead.
13:20:28.727 INFO   Terragrunt Cache server is listening on 127.0.0.1:46235
13:20:28.727 INFO   Start Terragrunt Cache server
{
  "Group 1": [
    "/home/dsecik/aws/foo"
  ]
}
13:20:28.728 INFO   Shutting down Terragrunt Cache server...
13:20:28.728 INFO   Terragrunt Cache server stopped

~/aws
```@yhakbar: Added in [v0.80.2](https://github.com/gruntwork-io/terragrunt/releases/tag/v0.80.2)

Solution patch:
diff --git a/cli/commands/find/cli.go b/cli/commands/find/cli.go
index 2d25d558..7c126ac5 100644
--- a/cli/commands/find/cli.go
+++ b/cli/commands/find/cli.go
@@ -23,6 +23,7 @@ const (
 	Dependencies   = "dependencies"
 	External       = "external"
 	Exclude        = "exclude"
+	Include        = "include"
 
 	QueueConstructAsFlagName  = "queue-construct-as"
 	QueueConstructAsFlagAlias = "as"
@@ -70,6 +71,12 @@ func NewFlags(opts *Options, prefix flags.Prefix) cli.Flags {
 			Destination: &opts.Exclude,
 			Usage:       "Display exclude configurations in the results (only when using --format=json).",
 		}),
+		flags.NewFlag(&cli.BoolFlag{
+			Name:        Include,
+			EnvVars:     tgPrefix.EnvVars(Include),
+			Destination: &opts.Include,
+			Usage:       "Display include configurations in the results (only when using --format=json).",
+		}),
 		flags.NewFlag(&cli.BoolFlag{
 			Name:        External,
 			EnvVars:     tgPrefix.EnvVars(External),
diff --git a/cli/commands/find/find.go b/cli/commands/find/find.go
index c855a1e0..786ad552 100644
--- a/cli/commands/find/find.go
+++ b/cli/commands/find/find.go
@@ -7,6 +7,7 @@ import (
 	"path/filepath"
 
 	"github.com/gruntwork-io/terragrunt/telemetry"
+	"github.com/gruntwork-io/terragrunt/util"
 
 	"github.com/gruntwork-io/terragrunt/config"
 	"github.com/gruntwork-io/terragrunt/internal/discovery"
@@ -37,6 +38,10 @@ func Run(ctx context.Context, opts *Options) error {
 		d = d.WithParseExclude()
 	}
 
+	if opts.Include {
+		d = d.WithParseInclude()
+	}
+
 	if opts.QueueConstructAs != "" {
 		d = d.WithParseExclude()
 		d = d.WithDiscoveryContext(&discovery.DiscoveryContext{
@@ -124,6 +129,7 @@ type FoundConfig struct {
 	Path string               `json:"path"`
 
 	Exclude *config.ExcludeConfig `json:"exclude,omitempty"`
+	Include map[string]string     `json:"include,omitempty"`
 
 	Dependencies []string `json:"dependencies,omitempty"`
 }
@@ -161,6 +167,16 @@ func discoveredToFound(configs discovery.DiscoveredConfigs, opts *Options) (Foun
 			foundCfg.Exclude = config.Parsed.Exclude.Clone()
 		}
 
+		if opts.Include && config.Parsed != nil && config.Parsed.ProcessedIncludes != nil {
+			foundCfg.Include = map[string]string{}
+			for _, v := range config.Parsed.ProcessedIncludes {
+				foundCfg.Include[v.Name], err = util.GetPathRelativeTo(v.Path, opts.RootWorkingDir)
+				if err != nil {
+					errs = append(errs, errors.New(err))
+				}
+			}
+		}
+
 		if !opts.Dependencies || len(config.Dependencies) == 0 {
 			foundCfgs = append(foundCfgs, foundCfg)
 
diff --git a/cli/commands/find/options.go b/cli/commands/find/options.go
index c8181a24..cbb7c9a1 100644
--- a/cli/commands/find/options.go
+++ b/cli/commands/find/options.go
@@ -53,6 +53,9 @@ type Options struct {
 	// Exclude determines if exclude configurations should be included in the output.
 	Exclude bool
 
+	// Include determines if Include configurations should be included in the output.
+	Include bool
+
 	// External determines if external dependencies should be included in the output.
 	External bool
 }
diff --git a/internal/discovery/discovery.go b/internal/discovery/discovery.go
index dac204d8..4ac6a7c9 100644
--- a/internal/discovery/discovery.go
+++ b/internal/discovery/discovery.go
@@ -91,6 +91,9 @@ type Discovery struct {
 	// parseExclude determines whether to parse exclude configurations.
 	parseExclude bool
 
+	// parseInclude determines whether to parse include configurations.
+	parseInclude bool
+
 	// discoverExternalDependencies determines whether to discover external dependencies.
 	discoverExternalDependencies bool
 
@@ -151,6 +154,15 @@ func (d *Discovery) WithParseExclude() *Discovery {
 	return d
 }
 
+// WithParseInclude sets the ParseExclude flag to true.
+func (d *Discovery) WithParseInclude() *Discovery {
+	d.parseInclude = true
+
+	d.requiresParse = true
+
+	return d
+}
+
 // WithMaxDependencyDepth sets the MaxDependencyDepth flag to the given depth.
 func (d *Discovery) WithMaxDependencyDepth(depth int) *Discovery {
 	d.maxDependencyDepth = depth



Test patch:
diff --git a/test/benchmarks/.gitignore b/test/benchmarks/.gitignore
new file mode 100644
index 00000000..9ed3b07c
--- /dev/null
+++ b/test/benchmarks/.gitignore
@@ -0,0 +1 @@
+*.test
diff --git a/test/benchmarks/helpers/helpers.go b/test/benchmarks/helpers/helpers.go
new file mode 100644
index 00000000..4135d4f0
--- /dev/null
+++ b/test/benchmarks/helpers/helpers.go
@@ -0,0 +1,106 @@
+// Package helpers provides helper functions for the integration benchmarks.
+package helpers
+
+import (
+	"context"
+	"io"
+	"os"
+	"path/filepath"
+	"strconv"
+	"testing"
+	"time"
+
+	"github.com/gruntwork-io/terragrunt/cli"
+	"github.com/gruntwork-io/terragrunt/options"
+	"github.com/gruntwork-io/terragrunt/pkg/log"
+	"github.com/stretchr/testify/require"
+)
+
+const (
+	// DefaultDirPermissions specifies the default file mode for creating directories.
+	// rwxr-xr-x (owner can read, write, execute; group and others can read, execute)
+	DefaultDirPermissions = 0755
+	// DefaultFilePermissions specifies the default file mode for creating files.
+	// rw-r--r-- (owner can read, write; group and others can read)
+	DefaultFilePermissions = 0644
+)
+
+// RunTerragruntCommand runs a Terragrunt command and logs the output to io.Discard.
+func RunTerragruntCommand(b *testing.B, args ...string) {
+	b.Helper()
+
+	writer := io.Discard
+	errwriter := io.Discard
+
+	opts := options.NewTerragruntOptionsWithWriters(writer, errwriter)
+	app := cli.NewApp(opts) //nolint:contextcheck
+
+	ctx := log.ContextWithLogger(context.Background(), opts.Logger)
+
+	err := app.RunContext(ctx, args)
+	require.NoError(b, err)
+}
+
+// GenerateNUnits generates n units in the given temporary directory.
+func GenerateNUnits(b *testing.B, dir string, n int, tgConfig string, tfConfig string) {
+	b.Helper()
+
+	for i := range n {
+		unitDir := filepath.Join(dir, "unit-"+strconv.Itoa(i))
+		require.NoError(b, os.MkdirAll(unitDir, DefaultDirPermissions))
+
+		// Create an empty `terragrunt.hcl` file
+		unitTerragruntConfigPath := filepath.Join(unitDir, "terragrunt.hcl")
+		require.NoError(b, os.WriteFile(unitTerragruntConfigPath, []byte(tgConfig), DefaultFilePermissions))
+
+		// Create an empty `main.tf` file
+		unitMainTfPath := filepath.Join(unitDir, "main.tf")
+		require.NoError(b, os.WriteFile(unitMainTfPath, []byte(tfConfig), DefaultFilePermissions))
+	}
+}
+
+// GenerateEmptyUnits generates n empty units in the given temporary directory.
+func GenerateEmptyUnits(b *testing.B, dir string, n int) {
+	b.Helper()
+
+	emptyRootConfig := ``
+	includeRootConfig := `include "root" {
+		path = find_in_parent_folders("root.hcl")
+}
+`
+	emptyMainTf := ``
+
+	rootTerragruntConfigPath := filepath.Join(dir, "root.hcl")
+
+	// Create an empty `root.hcl` file
+	require.NoError(b, os.WriteFile(rootTerragruntConfigPath, []byte(emptyRootConfig), DefaultFilePermissions))
+
+	// Generate n units
+	GenerateNUnits(b, dir, n, includeRootConfig, emptyMainTf)
+}
+
+func Init(b *testing.B, dir string) {
+	b.Helper()
+
+	// Measure plan time
+	planStart := time.Now()
+
+	RunTerragruntCommand(b, "terragrunt", "run", "--all", "init", "--non-interactive", "--working-dir", dir)
+
+	planDuration := time.Since(planStart)
+
+	b.ReportMetric(float64(planDuration.Seconds()), "init_s/op")
+}
+
+func Plan(b *testing.B, dir string) {
+	b.Helper()
+
+	// Measure plan time
+	planStart := time.Now()
+
+	RunTerragruntCommand(b, "terragrunt", "run", "--all", "plan", "--non-interactive", "--working-dir", dir)
+
+	planDuration := time.Since(planStart)
+
+	b.ReportMetric(float64(planDuration.Seconds()), "plan_s/op")
+}
diff --git a/test/benchmarks/integration_bench_test.go b/test/benchmarks/integration_bench_test.go
new file mode 100644
index 00000000..c5b61749
--- /dev/null
+++ b/test/benchmarks/integration_bench_test.go
@@ -0,0 +1,176 @@
+package test_test
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+
+	"github.com/gruntwork-io/terragrunt/test/benchmarks/helpers"
+	"github.com/stretchr/testify/require"
+)
+
+func BenchmarkEmptyTerragruntInit(b *testing.B) {
+	emptyMainTf := ``
+
+	emptyRootConfig := ``
+	includeRootConfig := `include "root" {
+		path = find_in_parent_folders("root.hcl")
+}
+
+terraform {
+	source = "."
+}
+`
+
+	// Create a temporary directory for the test
+	tmpDir := b.TempDir()
+	rootTerragruntConfigPath := filepath.Join(tmpDir, "root.hcl")
+	// Create an empty `root.hcl` file
+	require.NoError(b, os.WriteFile(rootTerragruntConfigPath, []byte(emptyRootConfig), helpers.DefaultFilePermissions))
+
+	// Create 1 units
+	helpers.GenerateNUnits(b, tmpDir, 1, includeRootConfig, emptyMainTf)
+
+	b.Run("1 units", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+			helpers.Init(b, tmpDir)
+		}
+	})
+}
+
+func BenchmarkTwoEmptyTerragruntInits(b *testing.B) {
+	emptyMainTf := ``
+
+	emptyRootConfig := ``
+	includeRootConfig := `include "root" {
+		path = find_in_parent_folders("root.hcl")
+}
+
+terraform {
+	source = "."
+}
+`
+
+	tmpDir := b.TempDir()
+
+	rootTerragruntConfigPath := filepath.Join(tmpDir, "root.hcl")
+	require.NoError(b, os.WriteFile(rootTerragruntConfigPath, []byte(emptyRootConfig), helpers.DefaultFilePermissions))
+
+	helpers.GenerateNUnits(b, tmpDir, 2, includeRootConfig, emptyMainTf)
+
+	b.Run("2 units", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+			helpers.Init(b, tmpDir)
+		}
+	})
+}
+
+func BenchmarkManyEmptyTerragruntInits(b *testing.B) {
+	emptyMainTf := ``
+
+	emptyRootConfig := ``
+	includeRootConfig := `include "root" {
+		path = find_in_parent_folders("root.hcl")
+}
+
+terraform {
+	source = "."
+}
+`
+
+	tmpDir := b.TempDir()
+
+	rootTerragruntConfigPath := filepath.Join(tmpDir, "root.hcl")
+	require.NoError(b, os.WriteFile(rootTerragruntConfigPath, []byte(emptyRootConfig), helpers.DefaultFilePermissions))
+
+	helpers.GenerateNUnits(b, tmpDir, 1000, includeRootConfig, emptyMainTf)
+
+	b.Run("1000 units", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+			helpers.Init(b, tmpDir)
+		}
+	})
+}
+
+func BenchmarkEmptyTerragruntPlan(b *testing.B) {
+	emptyMainTf := ``
+
+	emptyRootConfig := ``
+	includeRootConfig := `include "root" {
+		path = find_in_parent_folders("root.hcl")
+}
+
+terraform {
+	source = "."
+}
+`
+
+	// Create a temporary directory for the test
+	tmpDir := b.TempDir()
+	rootTerragruntConfigPath := filepath.Join(tmpDir, "root.hcl")
+	// Create an empty `root.hcl` file
+	require.NoError(b, os.WriteFile(rootTerragruntConfigPath, []byte(emptyRootConfig), helpers.DefaultFilePermissions))
+
+	// Create 1 units
+	helpers.GenerateNUnits(b, tmpDir, 1, includeRootConfig, emptyMainTf)
+
+	b.Run("1 units", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+
+			helpers.Plan(b, tmpDir)
+		}
+	})
+}
+
+func BenchmarkTwoEmptyTerragruntPlans(b *testing.B) {
+	emptyMainTf := ``
+
+	emptyRootConfig := ``
+	includeRootConfig := `include "root" {
+		path = find_in_parent_folders("root.hcl")
+	}
+
+	terraform {
+		source = "."
+	}
+`
+
+	tmpDir := b.TempDir()
+
+	rootTerragruntConfigPath := filepath.Join(tmpDir, "root.hcl")
+	require.NoError(b, os.WriteFile(rootTerragruntConfigPath, []byte(emptyRootConfig), helpers.DefaultFilePermissions))
+
+	helpers.GenerateNUnits(b, tmpDir, 2, includeRootConfig, emptyMainTf)
+
+	b.Run("2 units", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+			helpers.Plan(b, tmpDir)
+		}
+	})
+}
+
+func BenchmarkManyEmptyTerragruntPlans(b *testing.B) {
+	emptyMainTf := ``
+
+	emptyRootConfig := ``
+	includeRootConfig := `include "root" {
+		path = find_in_parent_folders("root.hcl")
+	}
+
+	terraform {
+		source = "."
+	}
+`
+
+	tmpDir := b.TempDir()
+	rootTerragruntConfigPath := filepath.Join(tmpDir, "root.hcl")
+	require.NoError(b, os.WriteFile(rootTerragruntConfigPath, []byte(emptyRootConfig), helpers.DefaultFilePermissions))
+
+	helpers.GenerateNUnits(b, tmpDir, 1000, includeRootConfig, emptyMainTf)
+
+	b.Run("1000 units", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+			helpers.Plan(b, tmpDir)
+		}
+	})
+}
diff --git a/test/fixtures/find/include/bar/main.tf b/test/fixtures/find/include/bar/main.tf
new file mode 100644
index 00000000..e69de29b
diff --git a/test/fixtures/find/include/bar/terragrunt.hcl b/test/fixtures/find/include/bar/terragrunt.hcl
new file mode 100644
index 00000000..0bd9a61d
--- /dev/null
+++ b/test/fixtures/find/include/bar/terragrunt.hcl
@@ -0,0 +1,3 @@
+include "cloud" {
+  path = find_in_parent_folders("cloud.hcl")
+}
\ No newline at end of file
diff --git a/test/fixtures/find/include/cloud.hcl b/test/fixtures/find/include/cloud.hcl
new file mode 100644
index 00000000..e69de29b
diff --git a/test/fixtures/find/include/foo/main.tf b/test/fixtures/find/include/foo/main.tf
new file mode 100644
index 00000000..e69de29b
diff --git a/test/fixtures/find/include/foo/terragrunt.hcl b/test/fixtures/find/include/foo/terragrunt.hcl
new file mode 100644
index 00000000..e69de29b
diff --git a/test/integration_find_test.go b/test/integration_find_test.go
index 41682bea..bf528563 100644
--- a/test/integration_find_test.go
+++ b/test/integration_find_test.go
@@ -19,6 +19,7 @@ const (
 	testFixtureFindDAG               = "fixtures/find/dag"
 	testFixtureFindInternalVExternal = "fixtures/find/internal-v-external"
 	testFixtureFindExclude           = "fixtures/exclude/basic"
+	testFixtureFindInclude           = "fixtures/find/include"
 )
 
 func TestFindBasic(t *testing.T) {
@@ -142,6 +143,20 @@ func TestFindExternalDependencies(t *testing.T) {
 	assert.Equal(t, "a-dependent\nb-dependency\n", stdout)
 }
 
+func TestFindInclude(t *testing.T) {
+	t.Parallel()
+
+	helpers.CleanupTerraformFolder(t, testFixtureFindInclude)
+
+	workdir := testFixtureFindInclude
+
+	stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, "terragrunt find --no-color --working-dir "+workdir+" --include --json")
+	require.NoError(t, err)
+
+	assert.Empty(t, stderr)
+	assert.JSONEq(t, `[{"type":"unit","path":"bar","include":{"cloud":"cloud.hcl"}},{"type":"unit","path":"foo"}]`, stdout)
+}
+
 func TestFindExclude(t *testing.T) {
 	t.Parallel()