Repository: gruntwork-io/terragrunt

Issue:
--tf-path tries to make you use cli-redesign
## Describe the bug

When using the `--tf-path` flag, it warns you to not use it globally, but to use it with the `run` command. Trying to use it with the run command throws an error that the cli-redesign experiment must be enabled.

## Steps To Reproduce

```
❯ terragrunt version
terragrunt version v0.76.8
❯ terragrunt --tf-path=terraform get
WARN[0000] The global `--tf-path` flag has moved to the `run` command and will not be accessible as a global flag in a future version of Terragrunt. Use `run --tf-path=terraform` instead.
❯ terragrunt run --tf-path=terraform get
ERRO[0000] requires that the cli-redesign experiment is enabled. e.g. --experiment cli-redesign
```

## Expected behavior

If you tell me to use an option, I expect that option to be supported.

If you tell me to use an option a certain way and then tell me that I can only use it that way if I use an experimental feature set, that is not acceptable. Especially when I know it has bugs that will break my configuration (https://github.com/gruntwork-io/terragrunt/issues/4064)


Discussion:

Solution patch:
diff --git a/cli/commands/commands.go b/cli/commands/commands.go
index 96ee6931..54530e3b 100644
--- a/cli/commands/commands.go
+++ b/cli/commands/commands.go
@@ -3,6 +3,7 @@ package commands
 
 import (
 	"github.com/gruntwork-io/terragrunt/cli/commands/backend"
+	"github.com/gruntwork-io/terragrunt/cli/commands/dag"
 	"github.com/gruntwork-io/terragrunt/cli/commands/find"
 	"github.com/gruntwork-io/terragrunt/cli/commands/info"
 	"github.com/gruntwork-io/terragrunt/cli/commands/list"
@@ -88,6 +89,7 @@ func New(opts *options.TerragruntOptions) cli.Commands {
 		hclvalidate.NewCommand(opts),        // hclvalidate
 		hclfmt.NewCommand(opts),             // hclfmt
 		info.NewCommand(opts),               // info
+		dag.NewCommand(opts),                // dag
 		terragruntinfo.NewCommand(opts),     // terragrunt-info
 		renderjson.NewCommand(opts),         // render-json
 		helpCmd.NewCommand(opts),            // help (hidden)
diff --git a/cli/commands/dag/cli.go b/cli/commands/dag/cli.go
new file mode 100644
index 00000000..8351d5ed
--- /dev/null
+++ b/cli/commands/dag/cli.go
@@ -0,0 +1,28 @@
+// Package dag implements the dag command to interact with the Directed Acyclic Graph (DAG).
+// It provides functionality to visualize and analyze dependencies between Terragrunt configurations.
+package dag
+
+import (
+	"github.com/gruntwork-io/terragrunt/cli/commands/dag/graph"
+	"github.com/gruntwork-io/terragrunt/cli/flags"
+	"github.com/gruntwork-io/terragrunt/internal/cli"
+	"github.com/gruntwork-io/terragrunt/options"
+)
+
+const (
+	CommandName = "dag"
+)
+
+func NewCommand(opts *options.TerragruntOptions) *cli.Command {
+	prefix := flags.Prefix{CommandName}
+
+	return &cli.Command{
+		Name:  CommandName,
+		Usage: "Interact with the Directed Acyclic Graph (DAG).",
+		Subcommands: cli.Commands{
+			graph.NewCommand(opts, prefix),
+		},
+		ErrorOnUndefinedFlag: true,
+		Action:               cli.ShowCommandHelp,
+	}
+}
diff --git a/cli/commands/dag/graph/command.go b/cli/commands/dag/graph/command.go
new file mode 100644
index 00000000..a78d1443
--- /dev/null
+++ b/cli/commands/dag/graph/command.go
@@ -0,0 +1,44 @@
+// Package graph implements the terragrunt dag graph command which generates a visual
+// representation of the Terragrunt dependency graph in DOT language format.
+package graph
+
+import (
+	"github.com/gruntwork-io/terragrunt/cli/commands/common/graph"
+	"github.com/gruntwork-io/terragrunt/cli/commands/common/runall"
+	"github.com/gruntwork-io/terragrunt/cli/flags"
+	"github.com/gruntwork-io/terragrunt/configstack"
+	"github.com/gruntwork-io/terragrunt/internal/cli"
+	"github.com/gruntwork-io/terragrunt/options"
+)
+
+const (
+	CommandName = "graph"
+)
+
+func NewCommand(opts *options.TerragruntOptions, _ flags.Prefix) *cli.Command {
+	cmd := &cli.Command{
+		Name:                 CommandName,
+		Usage:                "Graph the Directed Acyclic Graph (DAG) in DOT language.",
+		UsageText:            "terragrunt dag graph",
+		ErrorOnUndefinedFlag: true,
+		Action: func(ctx *cli.Context) error {
+			return Run(ctx, opts)
+		},
+	}
+
+	cmd = runall.WrapCommand(opts, cmd)
+	cmd = graph.WrapCommand(opts, cmd)
+
+	return cmd
+}
+
+func Run(ctx *cli.Context, opts *options.TerragruntOptions) error {
+	stack, err := configstack.FindStackInSubfolders(ctx, opts)
+	if err != nil {
+		return err
+	}
+
+	stack.Graph(opts)
+
+	return nil
+}
diff --git a/cli/commands/find/cli.go b/cli/commands/find/cli.go
index 1fdcb6ae..6ed93965 100644
--- a/cli/commands/find/cli.go
+++ b/cli/commands/find/cli.go
@@ -24,6 +24,10 @@ const (
 	HiddenFlagName = "hidden"
 	Dependencies   = "dependencies"
 	External       = "external"
+	Exclude        = "exclude"
+
+	QueueConstructAsFlagName  = "queue-construct-as"
+	QueueConstructAsFlagAlias = "as"
 )
 
 func NewFlags(opts *Options, prefix flags.Prefix) cli.Flags {
@@ -62,12 +66,25 @@ func NewFlags(opts *Options, prefix flags.Prefix) cli.Flags {
 			Destination: &opts.Dependencies,
 			Usage:       "Include dependencies in the results (only when using --format=json).",
 		}),
+		flags.NewFlag(&cli.BoolFlag{
+			Name:        Exclude,
+			EnvVars:     tgPrefix.EnvVars(Exclude),
+			Destination: &opts.Exclude,
+			Usage:       "Display exclude configurations in the results (only when using --format=json).",
+		}),
 		flags.NewFlag(&cli.BoolFlag{
 			Name:        External,
 			EnvVars:     tgPrefix.EnvVars(External),
 			Destination: &opts.External,
 			Usage:       "Discover external dependencies from initial results, and add them to top-level results.",
 		}),
+		flags.NewFlag(&cli.GenericFlag[string]{
+			Name:        QueueConstructAsFlagName,
+			EnvVars:     tgPrefix.EnvVars(QueueConstructAsFlagName),
+			Destination: &opts.QueueConstructAs,
+			Usage:       "Construct the queue as if a specific command was run.",
+			Aliases:     []string{QueueConstructAsFlagAlias},
+		}),
 	}
 }
 
@@ -93,6 +110,12 @@ func NewCommand(opts *options.TerragruntOptions) *cli.Command {
 				cmdOpts.Mode = ModeDAG
 			}
 
+			// Requesting a specific command to be used for queue construction
+			// implies DAG mode.
+			if cmdOpts.QueueConstructAs != "" {
+				cmdOpts.Mode = ModeDAG
+			}
+
 			if err := cmdOpts.Validate(); err != nil {
 				return cli.NewExitError(err, cli.ExitCodeGeneralError)
 			}
diff --git a/cli/commands/find/find.go b/cli/commands/find/find.go
index 4192f425..b1662518 100644
--- a/cli/commands/find/find.go
+++ b/cli/commands/find/find.go
@@ -6,6 +6,7 @@ import (
 	"os"
 	"path/filepath"
 
+	"github.com/gruntwork-io/terragrunt/config"
 	"github.com/gruntwork-io/terragrunt/internal/discovery"
 	"github.com/gruntwork-io/terragrunt/internal/errors"
 	"github.com/gruntwork-io/terragrunt/internal/queue"
@@ -30,9 +31,20 @@ func Run(ctx context.Context, opts *Options) error {
 		d = d.WithDiscoverExternalDependencies()
 	}
 
+	if opts.Exclude {
+		d = d.WithParseExclude()
+	}
+
+	if opts.QueueConstructAs != "" {
+		d = d.WithParseExclude()
+		d = d.WithDiscoveryContext(&discovery.DiscoveryContext{
+			Cmd: opts.QueueConstructAs,
+		})
+	}
+
 	cfgs, err := d.Discover(ctx, opts.TerragruntOptions)
 	if err != nil {
-		opts.Logger.Warnf("Error discovering configurations:\n%s", err)
+		opts.Logger.Debugf("Errors encountered while discovering configurations:\n%s", err)
 	}
 
 	switch opts.Mode {
@@ -44,7 +56,7 @@ func Run(ctx context.Context, opts *Options) error {
 			return errors.New(err)
 		}
 
-		cfgs = q.Entries()
+		cfgs = q.Configs()
 	default:
 		// This should never happen, because of validation in the command.
 		// If it happens, we want to throw so we can fix the validation.
@@ -74,6 +86,8 @@ type FoundConfig struct {
 	Type discovery.ConfigType `json:"type"`
 	Path string               `json:"path"`
 
+	Exclude *config.ExcludeConfig `json:"exclude,omitempty"`
+
 	Dependencies []string `json:"dependencies,omitempty"`
 }
 
@@ -86,6 +100,14 @@ func discoveredToFound(configs discovery.DiscoveredConfigs, opts *Options) (Foun
 			continue
 		}
 
+		if opts.QueueConstructAs != "" {
+			if config.Parsed != nil && config.Parsed.Exclude != nil {
+				if config.Parsed.Exclude.IsActionListed(opts.QueueConstructAs) {
+					continue
+				}
+			}
+		}
+
 		relPath, err := filepath.Rel(opts.WorkingDir, config.Path)
 		if err != nil {
 			errs = append(errs, errors.New(err))
@@ -98,6 +120,10 @@ func discoveredToFound(configs discovery.DiscoveredConfigs, opts *Options) (Foun
 			Path: relPath,
 		}
 
+		if opts.Exclude && config.Parsed != nil && config.Parsed.Exclude != nil {
+			foundCfg.Exclude = config.Parsed.Exclude.Clone()
+		}
+
 		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 02f935bc..c8181a24 100644
--- a/cli/commands/find/options.go
+++ b/cli/commands/find/options.go
@@ -34,6 +34,9 @@ type Options struct {
 	// Mode determines the mode of the find command.
 	Mode string
 
+	// QueueConstructAs constructs the queue as if a particular command was run.
+	QueueConstructAs string
+
 	// JSON determines if the output should be in JSON format.
 	// Alias for --format=json.
 	JSON bool
@@ -47,6 +50,9 @@ type Options struct {
 	// Dependencies determines if dependencies should be included in the output.
 	Dependencies bool
 
+	// Exclude determines if exclude configurations should be included in the output.
+	Exclude bool
+
 	// External determines if external dependencies should be included in the output.
 	External bool
 }
diff --git a/cli/commands/graph-dependencies/cli.go b/cli/commands/graph-dependencies/cli.go
index 5c3def64..fbeb7fad 100644
--- a/cli/commands/graph-dependencies/cli.go
+++ b/cli/commands/graph-dependencies/cli.go
@@ -1,4 +1,6 @@
 // Package graphdependencies provides the command to print the terragrunt dependency graph to stdout.
+// Deprecated: This package is deprecated and will be removed in a future version.
+// Use the 'terragrunt dag graph' command instead.
 package graphdependencies
 
 import (
diff --git a/cli/commands/list/cli.go b/cli/commands/list/cli.go
index 96e40c06..bf7faf1b 100644
--- a/cli/commands/list/cli.go
+++ b/cli/commands/list/cli.go
@@ -27,6 +27,9 @@ const (
 	ExternalFlagName     = "external"
 
 	DAGFlagName = "dag"
+
+	QueueConstructAsFlagName  = "queue-construct-as"
+	QueueConstructAsFlagAlias = "as"
 )
 
 func NewFlags(opts *Options, prefix flags.Prefix) cli.Flags {
@@ -78,6 +81,13 @@ func NewFlags(opts *Options, prefix flags.Prefix) cli.Flags {
 			Destination: &opts.DAG,
 			Usage:       "Use DAG mode to sort and group output.",
 		}),
+		flags.NewFlag(&cli.GenericFlag[string]{
+			Name:        QueueConstructAsFlagName,
+			EnvVars:     tgPrefix.EnvVars(QueueConstructAsFlagName),
+			Destination: &opts.QueueConstructAs,
+			Usage:       "Construct the queue as if a specific command was run.",
+			Aliases:     []string{QueueConstructAsFlagAlias},
+		}),
 	}
 }
 
@@ -107,6 +117,12 @@ func NewCommand(opts *options.TerragruntOptions) *cli.Command {
 				cmdOpts.Mode = ModeDAG
 			}
 
+			// Requesting a specific command to be used for queue construction
+			// implies DAG mode.
+			if cmdOpts.QueueConstructAs != "" {
+				cmdOpts.Mode = ModeDAG
+			}
+
 			if err := cmdOpts.Validate(); err != nil {
 				return cli.NewExitError(err, cli.ExitCodeGeneralError)
 			}
diff --git a/cli/commands/list/list.go b/cli/commands/list/list.go
index 99bc872e..3d7a38da 100644
--- a/cli/commands/list/list.go
+++ b/cli/commands/list/list.go
@@ -34,9 +34,17 @@ func Run(ctx context.Context, opts *Options) error {
 		d = d.WithDiscoverExternalDependencies()
 	}
 
+	if opts.QueueConstructAs != "" {
+		d = d.WithParseExclude()
+		d = d.WithDiscoverDependencies()
+		d = d.WithDiscoveryContext(&discovery.DiscoveryContext{
+			Cmd: opts.QueueConstructAs,
+		})
+	}
+
 	cfgs, err := d.Discover(ctx, opts.TerragruntOptions)
 	if err != nil {
-		return errors.New(err)
+		opts.Logger.Debugf("Errors encountered while discovering configurations:\n%s", err)
 	}
 
 	switch opts.Mode {
@@ -48,7 +56,7 @@ func Run(ctx context.Context, opts *Options) error {
 			return errors.New(err)
 		}
 
-		cfgs = q.Entries()
+		cfgs = q.Configs()
 	default:
 		// This should never happen, because of validation in the command.
 		// If it happens, we want to throw so we can fix the validation.
@@ -131,6 +139,14 @@ func discoveredToListed(configs discovery.DiscoveredConfigs, opts *Options) (Lis
 			continue
 		}
 
+		if opts.QueueConstructAs != "" {
+			if config.Parsed != nil && config.Parsed.Exclude != nil {
+				if config.Parsed.Exclude.IsActionListed(opts.QueueConstructAs) {
+					continue
+				}
+			}
+		}
+
 		relPath, err := filepath.Rel(opts.WorkingDir, config.Path)
 		if err != nil {
 			errs = append(errs, errors.New(err))
diff --git a/cli/commands/list/options.go b/cli/commands/list/options.go
index d8b100f3..3533bb05 100644
--- a/cli/commands/list/options.go
+++ b/cli/commands/list/options.go
@@ -37,6 +37,9 @@ type Options struct {
 	// Mode determines the mode of the list command.
 	Mode string
 
+	// QueueConstructAs constructs the queue as if a particular command was run.
+	QueueConstructAs string
+
 	// Hidden determines whether to detect hidden directories.
 	Hidden bool
 
diff --git a/cli/flags/flag.go b/cli/flags/flag.go
index d23f8cbc..aff88210 100644
--- a/cli/flags/flag.go
+++ b/cli/flags/flag.go
@@ -13,9 +13,14 @@ import (
 
 var _ = cli.Flag(new(Flag))
 
+// EvaluateWrapperFunc represents a function that is used to wrap the `Evaluate(ctx context.Context) error` strict control method.
+// Which can be passed as an option `WithEvaluateWrapper` to `NewFlag(...)` to control the behavior of strict control evaluation.
+type EvaluateWrapperFunc func(ctx context.Context, evalFn func(ctx context.Context) error) error
+
 // Flag is a wrapper for `cli.Flag` that avoids displaying deprecated flags in help, but registers their flag names and environment variables.
 type Flag struct {
 	cli.Flag
+	evaluateWrapper EvaluateWrapperFunc
 	deprecatedFlags DeprecatedFlags
 }
 
@@ -23,6 +28,9 @@ type Flag struct {
 func NewFlag(new cli.Flag, opts ...Option) *Flag {
 	flag := &Flag{
 		Flag: new,
+		evaluateWrapper: func(ctx context.Context, evalFn func(ctx context.Context) error) error {
+			return evalFn(ctx)
+		},
 	}
 
 	for _, opt := range opts {
@@ -32,8 +40,8 @@ func NewFlag(new cli.Flag, opts ...Option) *Flag {
 	return flag
 }
 
-func NewMovedFlag(deprecatedFlag cli.Flag, newCommandName string, regControlsFn RegisterStrictControlsFunc) *Flag {
-	return NewFlag(nil, WithDeprecatedMovedFlag(deprecatedFlag, newCommandName, regControlsFn))
+func NewMovedFlag(deprecatedFlag cli.Flag, newCommandName string, regControlsFn RegisterStrictControlsFunc, opts ...Option) *Flag {
+	return NewFlag(nil, append(opts, WithDeprecatedMovedFlag(deprecatedFlag, newCommandName, regControlsFn))...)
 }
 
 // TakesValue implements `github.com/urfave/cli.DocGenerationFlag` required to generate help.
@@ -120,7 +128,7 @@ func (newFlag *Flag) Apply(set *flag.FlagSet) error {
 // RunAction implements `cli.Flag` interface.
 func (newFlag *Flag) RunAction(ctx *cli.Context) error {
 	for _, deprecated := range newFlag.deprecatedFlags {
-		if err := deprecated.Evaluate(ctx); err != nil {
+		if err := newFlag.evaluateWrapper(ctx, deprecated.Evaluate); err != nil {
 			return err
 		}
 
@@ -136,7 +144,7 @@ func (newFlag *Flag) RunAction(ctx *cli.Context) error {
 	if deprecated, ok := newFlag.Flag.(interface {
 		Evaluate(ctx context.Context) error
 	}); ok {
-		if err := deprecated.Evaluate(ctx); err != nil {
+		if err := newFlag.evaluateWrapper(ctx, deprecated.Evaluate); err != nil {
 			return err
 		}
 	}
diff --git a/cli/flags/flag_opts.go b/cli/flags/flag_opts.go
index 4cebcfe2..d06ace4f 100644
--- a/cli/flags/flag_opts.go
+++ b/cli/flags/flag_opts.go
@@ -151,3 +151,10 @@ func WithDeprecatedNamesEnvVars(flagNames, envVars []string, regControlsFn Regis
 		newFlag.deprecatedFlags = append(newFlag.deprecatedFlags, deprecatedFlag)
 	}
 }
+
+// WithEvaluateWrapper returns an Option that wraps the strict control `Evaluate(ctx context.Context)` function.
+func WithEvaluateWrapper(fn EvaluateWrapperFunc) Option {
+	return func(newFlag *Flag) {
+		newFlag.evaluateWrapper = fn
+	}
+}
diff --git a/cli/flags/global/flags.go b/cli/flags/global/flags.go
index 9f34d5a2..731902c6 100644
--- a/cli/flags/global/flags.go
+++ b/cli/flags/global/flags.go
@@ -2,6 +2,7 @@
 package global
 
 import (
+	"context"
 	"fmt"
 
 	"slices"
@@ -9,9 +10,11 @@ import (
 	"github.com/gruntwork-io/go-commons/collections"
 	"github.com/gruntwork-io/terragrunt/cli/commands"
 	"github.com/gruntwork-io/terragrunt/cli/commands/help"
+	"github.com/gruntwork-io/terragrunt/cli/commands/run"
 	"github.com/gruntwork-io/terragrunt/cli/commands/version"
 	"github.com/gruntwork-io/terragrunt/cli/flags"
 	"github.com/gruntwork-io/terragrunt/internal/cli"
+	"github.com/gruntwork-io/terragrunt/internal/experiment"
 	"github.com/gruntwork-io/terragrunt/internal/strict"
 	"github.com/gruntwork-io/terragrunt/internal/strict/controls"
 	"github.com/gruntwork-io/terragrunt/options"
@@ -80,6 +83,11 @@ const (
 	DeprecatedTfLogJSONFlagName            = "tf-logs-to-json"
 )
 
+// experimentalCommands is a list of experimental commands for which the deprecated messages about moved global flags should not be displayed unless the `cli-redesign` experiment is enabled.
+var experimentalCommands = []string{
+	run.CommandName,
+}
+
 // NewFlagsWithDeprecatedMovedFlags returns global flags along with flags that have been moved to other commands and hidden from CLI help.
 func NewFlagsWithDeprecatedMovedFlags(opts *options.TerragruntOptions) cli.Flags {
 	globalFlags := NewFlags(opts, nil)
@@ -105,8 +113,22 @@ func NewFlagsWithDeprecatedMovedFlags(opts *options.TerragruntOptions) cli.Flags
 				continue
 			}
 
+			// Disable strcit control evaluation of moves global flags for the experimental `run` command if the `cli-redesign` experiment is not enabled.
+			evaluateWrapper := func(ctx context.Context, evalFn func(ctx context.Context) error) error {
+				if opts.Experiments.Evaluate(experiment.CLIRedesign) && slices.Contains(experimentalCommands, cmd.Name) {
+					return evalFn(ctx)
+				}
+
+				return nil
+			}
+
 			seen = append(seen, flagName)
-			globalFlags = append(globalFlags, flags.NewMovedFlag(flag, cmd.Name, flags.StrictControlsByMovedGlobalFlags(opts.StrictControls, cmd.Name)))
+			globalFlags = append(globalFlags, flags.NewMovedFlag(
+				flag,
+				cmd.Name,
+				flags.StrictControlsByMovedGlobalFlags(opts.StrictControls, cmd.Name),
+				flags.WithEvaluateWrapper(evaluateWrapper),
+			))
 		}
 	}
 
diff --git a/config/config_as_cty.go b/config/config_as_cty.go
index 586eea43..0a2e7ca2 100644
--- a/config/config_as_cty.go
+++ b/config/config_as_cty.go
@@ -792,6 +792,10 @@ func stackToCty(stack *Stack) (cty.Value, error) {
 		output["no_dot_terragrunt_stack"] = goboolToCty(*stack.NoStack)
 	}
 
+	if stack.NoValidation != nil {
+		output["no_validation"] = goboolToCty(*stack.NoValidation)
+	}
+
 	return convertValuesMapToCtyVal(output)
 }
 
@@ -817,6 +821,10 @@ func unitToCty(unit *Unit) (cty.Value, error) {
 		output["no_dot_terragrunt_stack"] = goboolToCty(*unit.NoStack)
 	}
 
+	if unit.NoValidation != nil {
+		output["no_validation"] = goboolToCty(*unit.NoValidation)
+	}
+
 	return convertValuesMapToCtyVal(output)
 }
 
diff --git a/config/stack.go b/config/stack.go
index 50fcce2d..67505605 100644
--- a/config/stack.go
+++ b/config/stack.go
@@ -53,20 +53,22 @@ type StackConfig struct {
 
 // Unit represent unit from stack file.
 type Unit struct {
-	NoStack *bool      `hcl:"no_dot_terragrunt_stack,attr"`
-	Values  *cty.Value `hcl:"values,attr"`
-	Name    string     `hcl:",label"`
-	Source  string     `hcl:"source,attr"`
-	Path    string     `hcl:"path,attr"`
+	NoStack      *bool      `hcl:"no_dot_terragrunt_stack,attr"`
+	NoValidation *bool      `hcl:"no_validation,attr"`
+	Values       *cty.Value `hcl:"values,attr"`
+	Name         string     `hcl:",label"`
+	Source       string     `hcl:"source,attr"`
+	Path         string     `hcl:"path,attr"`
 }
 
 // Stack represents the stack block in the configuration.
 type Stack struct {
-	NoStack *bool      `hcl:"no_dot_terragrunt_stack,attr"`
-	Values  *cty.Value `hcl:"values,attr"`
-	Name    string     `hcl:",label"`
-	Source  string     `hcl:"source,attr"`
-	Path    string     `hcl:"path,attr"`
+	NoStack      *bool      `hcl:"no_dot_terragrunt_stack,attr"`
+	NoValidation *bool      `hcl:"no_validation,attr"`
+	Values       *cty.Value `hcl:"values,attr"`
+	Name         string     `hcl:",label"`
+	Source       string     `hcl:"source,attr"`
+	Path         string     `hcl:"path,attr"`
 }
 
 // GenerateStacks generates the stack files.
@@ -375,14 +377,15 @@ func generateUnits(ctx context.Context, opts *options.TerragruntOptions, pool *w
 
 		pool.Submit(func() error {
 			item := componentToProcess{
-				sourceDir: sourceDir,
-				targetDir: targetDir,
-				name:      unitCopy.Name,
-				path:      unitCopy.Path,
-				source:    unitCopy.Source,
-				values:    unitCopy.Values,
-				noStack:   unitCopy.NoStack != nil && *unitCopy.NoStack,
-				kind:      unitKind,
+				sourceDir:    sourceDir,
+				targetDir:    targetDir,
+				name:         unitCopy.Name,
+				path:         unitCopy.Path,
+				source:       unitCopy.Source,
+				values:       unitCopy.Values,
+				noStack:      unitCopy.NoStack != nil && *unitCopy.NoStack,
+				noValidation: unitCopy.NoValidation != nil && *unitCopy.NoValidation,
+				kind:         unitKind,
 			}
 
 			opts.Logger.Infof("Processing unit %s", unitCopy.Name)
@@ -408,14 +411,15 @@ func generateStacks(ctx context.Context, opts *options.TerragruntOptions, pool *
 
 		pool.Submit(func() error {
 			item := componentToProcess{
-				sourceDir: sourceDir,
-				targetDir: targetDir,
-				name:      stackCopy.Name,
-				path:      stackCopy.Path,
-				source:    stackCopy.Source,
-				noStack:   stackCopy.NoStack != nil && *stackCopy.NoStack,
-				values:    stackCopy.Values,
-				kind:      stackKind,
+				sourceDir:    sourceDir,
+				targetDir:    targetDir,
+				name:         stackCopy.Name,
+				path:         stackCopy.Path,
+				source:       stackCopy.Source,
+				noStack:      stackCopy.NoStack != nil && *stackCopy.NoStack,
+				noValidation: stackCopy.NoValidation != nil && *stackCopy.NoValidation,
+				values:       stackCopy.Values,
+				kind:         stackKind,
 			}
 
 			opts.Logger.Infof("Processing stack %s", stackCopy.Name)
@@ -444,14 +448,15 @@ const (
 // It contains information about the source and target directories, the name and path of the item, the source URL or path,
 // and any associated values that need to be processed.
 type componentToProcess struct {
-	values    *cty.Value
-	sourceDir string
-	targetDir string
-	name      string
-	path      string
-	source    string
-	noStack   bool
-	kind      componentKind
+	values       *cty.Value
+	sourceDir    string
+	targetDir    string
+	name         string
+	path         string
+	source       string
+	noStack      bool
+	noValidation bool
+	kind         componentKind
 }
 
 // processComponent copies files from the source directory to the target destination and generates a corresponding values file.
@@ -519,7 +524,21 @@ func processComponent(ctx context.Context, opts *options.TerragruntOptions, cmp
 		)
 	}
 
-	if !cmp.noStack {
+	skipValidation := false
+
+	if cmp.noStack {
+		opts.Logger.Debugf("Skipping validation for %s %s due to no_stack flag", kindStr, cmp.name)
+
+		skipValidation = true
+	}
+
+	if cmp.noValidation {
+		opts.Logger.Debugf("Skipping validation for %s %s due to no_validation flag", kindStr, cmp.name)
+
+		skipValidation = true
+	}
+
+	if !skipValidation {
 		// validate what was copied to the destination, don't do validation for special noStack components
 		expectedFile := DefaultTerragruntConfigPath
 
diff --git a/docs-starlight/src/content/docs/04-reference/01-hcl/01-overview.mdx b/docs-starlight/src/content/docs/04-reference/01-hcl/01-overview.mdx
index 1086c20b..7add3079 100644
--- a/docs-starlight/src/content/docs/04-reference/01-hcl/01-overview.mdx
+++ b/docs-starlight/src/content/docs/04-reference/01-hcl/01-overview.mdx
@@ -10,7 +10,7 @@ import { FileTree } from '@astrojs/starlight/components';
 
 Terragrunt configuration is defined in [HCL](https://github.com/hashicorp/hcl) files. This uses the same HCL syntax as OpenTofu/Terraform itself.
 
-Here’s an example:
+Here's an example:
 
 ```hcl
 # terragrunt.hcl
@@ -68,7 +68,7 @@ Currently terragrunt parses the config in the following order:
 
 8. A merge operation between the config referenced by `include` and the current config.
 
-Blocks that are parsed earlier in the process will be made available for use in the parsing of later blocks. Similarly, you cannot use blocks that are parsed later earlier in the process (e.g you can’t reference `dependency` in `locals`, `include`, or `dependencies` blocks).
+Blocks that are parsed earlier in the process will be made available for use in the parsing of later blocks. Similarly, you cannot use blocks that are parsed later earlier in the process (e.g you can't reference `dependency` in `locals`, `include`, or `dependencies` blocks).
 
 Note that the parsing order is slightly different when using the `--all` flag of the [`run`](/docs/reference/cli/commands/run) command. When using the `--all` flag, Terragrunt parses the configuration twice. In the first pass, it follows the following parsing order:
 
@@ -84,17 +84,19 @@ Note that the parsing order is slightly different when using the `--all` flag of
 
 The results of this pass are then used to build the dependency graph of the units in the stack. Once the graph is constructed, Terragrunt will loop through the units and run the specified command. It will then revert to the single configuration parsing order specified above for each unit as it runs the command.
 
-This allows Terragrunt to avoid resolving `dependency` on units that haven’t been applied yet when doing a clean deployment from scratch with `run-all apply`.
+This allows Terragrunt to avoid resolving `dependency` on units that haven't been applied yet when doing a clean deployment from scratch with `run-all apply`.
 
 ## Stacks
 
 When multiple units, each with their own `terragrunt.hcl` file exist in child directories of a single parent directory, that parent directory becomes a [stack](/docs/getting-started/terminology#stack).
 
-To make it easier to generate configurations like this, Terragrunt has special tooling in the form of `terragrunt.stack.hcl` files.
+To make it easier to generate configurations like this, Terragrunt has special tooling in the form of `terragrunt.stack.hcl` files. `terragrunt.stack.hcl` files support all the same HCL functions as `terragrunt.hcl` files, however, they don't support any top-level attributes, and the configuration blocks they support are limited to the following:
 
-These special configurations are used by the [stack generate](/docs/reference/cli/commands/stack/generate) (and all the other `stack` prefixed) commands to generate units programmatically.
+- [unit](/docs/reference/hcl/blocks#unit)
+- [stack](/docs/reference/hcl/blocks#stack)
+- [locals](/docs/reference/hcl/blocks#locals)
 
-Stacks are still an [experiment](/docs/reference/experiments#stacks). Expect more documentation on stacks by the time they are generally available.
+These special configurations are used by the [stack generate command](/docs/reference/cli/commands/stack/generate) (and all the other `stack` prefixed commands) to generate units programmatically, on demand. The units they generate are valid unit configurations, and can be read and used as if they were manually authored.
 
 ## Included Configurations
 
@@ -141,7 +143,7 @@ If you run `terragrunt hclfmt` at the `root`, this will update:
 
 You can set `--diff` option. `terragrunt hclfmt --diff` will output the diff in a unified format which can be redirected to your favourite diff tool. `diff` utility must be presented in PATH.
 
-Additionally, there’s a flag `--check`. `terragrunt hclfmt --check` will only verify if the files are correctly formatted **without rewriting** them. The command will return exit status 1 if any matching files are improperly formatted, or 0 if all matching `.hcl` files are correctly formatted.
+Additionally, there's a flag `--check`. `terragrunt hclfmt --check` will only verify if the files are correctly formatted **without rewriting** them. The command will return exit status 1 if any matching files are improperly formatted, or 0 if all matching `.hcl` files are correctly formatted.
 
 You can exclude directories from the formatting process by using the `--hclfmt-exclude-dir` flag. For example, `terragrunt hclfmt --hclfmt-exclude-dir=qa/services`.
 
diff --git a/docs-starlight/src/content/docs/04-reference/01-hcl/02-blocks.mdx b/docs-starlight/src/content/docs/04-reference/01-hcl/02-blocks.mdx
index a6a77c3b..966e8f32 100644
--- a/docs-starlight/src/content/docs/04-reference/01-hcl/02-blocks.mdx
+++ b/docs-starlight/src/content/docs/04-reference/01-hcl/02-blocks.mdx
@@ -1674,6 +1674,7 @@ The `unit` block supports the following arguments:
 - `path` (attribute): The relative path where this unit should be deployed within the stack directory (`.terragrunt-stack`). Also take note of the `no_dot_terragrunt_stack` attribute below, which can impact this.
 - `values` (attribute, optional): A map of values that will be passed to the unit as inputs.
 - `no_dot_terragrunt_stack` (attribute, optional): A boolean flag (`true` or `false`). When set to `true`, the unit **will not** be placed inside the `.terragrunt-stack` directory but will instead be generated in the same directory where `terragrunt.stack.hcl` is located. This allows for a **soft adoption** of stacks, making it easier for users to start using `terragrunt.stack.hcl` without modifying existing directory structures, or performing state migrations.
+- `no_validation` (attribute, optional): A boolean flag (`true` or `false`) that controls whether Terragrunt should validate the unit's configuration. When set to `true`, Terragrunt will skip validation checks for this unit.
 
 Example:
 
@@ -1790,6 +1791,7 @@ The `stack` block supports the following arguments:
 - `path` (attribute): The relative path within `.terragrunt-stack` where this stack should be generated.If an absolute path is provided here, Terragrunt will generate the stack in that location, instead of generating it in a path relative to the `.terragrunt-stack` directory. Also take note of the `no_dot_terragrunt_stack` attribute below, which can impact this.
 - `values` (attribute, optional): A map of custom values that can be passed to the stack. These values can be referenced within the stack's configuration files, allowing for customization without modifying the stack source.
 - `no_dot_terragrunt_stack` (attribute, optional): A boolean flag (`true` or `false`). When set to `true`, the stack **will not** be placed inside the `.terragrunt-stack` directory but will instead be generated in the same directory where `terragrunt.stack.hcl` is located. This allows for a **soft adoption** of stacks, making it easier for users to start using `terragrunt.stack.hcl` without modifying existing directory structures, or performing state migrations.
+- `no_validation` (attribute, optional): A boolean flag (`true` or `false`) that controls whether Terragrunt should validate the stack's configuration. When set to `true`, Terragrunt will skip validation checks for this stack.
 
 Example:
 
diff --git a/docs-starlight/src/content/docs/04-reference/04-experiments.md b/docs-starlight/src/content/docs/04-reference/04-experiments.md
index 921e85f8..0045bceb 100644
--- a/docs-starlight/src/content/docs/04-reference/04-experiments.md
+++ b/docs-starlight/src/content/docs/04-reference/04-experiments.md
@@ -154,9 +154,20 @@ To transition `cli-redesign` features to a stable release, the following must be
 - [x] Add OpenTofu commands as explicit shortcuts in the CLI instead of forwarding all unknown commands to OpenTofu/Terraform.
 - [ ] Add support for the `backend` command.
 - [ ] Add support for the `render` command.
-- [ ] Add support for the `info` command.
+- [x] Add support for the `info` command.
 - [ ] Add support for the `dag` command.
-ith `--sort=dag` flag on all the fixtures in the `test/fixtures` directory.
+- [ ] Add support for the `find` command.
+  - [x] Add support for `find` without flags.
+  - [x] Add support for `find` with colorful output.
+  - [x] Add support for `find` with `--format=json` flag.
+  - [x] Add support for `find` with stdout redirection detection.
+  - [x] Add support for `find` with `--hidden` flag.
+  - [x] Add support for `find` with `--sort=alpha` flag.
+  - [x] Add support for `find` with `--sort=dag` flag.
+  - [x] Add support for `find` with the `exclude` block used to exclude units from the search.
+  - [ ] Add integration with `symlinks` experiment to support finding units/stacks via symlinks.
+  - [x] Add handling of broken configurations or configurations requiring authentication.
+  - [x] Add integration test for `find` with `--sort=dag` flag on all the fixtures in the `test/fixtures` directory.
 - [ ] Add support for the `list` command.
   - [x] Add support for `list` without flags.
   - [x] Add support for `list` with colorful output.
@@ -168,10 +179,10 @@ ith `--sort=dag` flag on all the fixtures in the `test/fixtures` directory.
   - [x] Add support for `list` with `--sort=dag` flag.
   - [x] Add support for `list` with `--group-by=fs` flag.
   - [x] Add support for `list` with `--group-by=dag` flag.
-  - [ ] Add support for `list` with the `exclude` block used to exclude units from the search.
+  - [x] Add support for `list` with the `exclude` block used to exclude units from the search.
   - [ ] Add integration with `symlinks` experiment to support listing units/stacks via symlinks.
-  - [ ] Add handling of broken configurations or configurations requiring authentication.
-  - [ ] Add integration test for `list` with `--sort=dag` flag on all the fixtures in the `test/fixtures` directory.
+  - [x] Add handling of broken configurations or configurations requiring authentication.
+  - [x] Add integration test for `list` with `--sort=dag` flag on all the fixtures in the `test/fixtures` directory.
 
 ### `cas`
 
diff --git a/docs-starlight/src/content/docs/06-migrate/04-terragrunt-stacks.mdx b/docs-starlight/src/content/docs/06-migrate/04-terragrunt-stacks.mdx
new file mode 100644
index 00000000..580fe7d4
--- /dev/null
+++ b/docs-starlight/src/content/docs/06-migrate/04-terragrunt-stacks.mdx
@@ -0,0 +1,329 @@
+---
+title: Terragrunt Stacks
+description: Migration guide to migrate to Terragrunt Stacks
+slug: docs/migrate/terragrunt-stacks
+sidebar:
+  order: 4
+---
+
+import { Aside, FileTree } from '@astrojs/starlight/components'
+
+<Aside type="tip">
+
+This guide assumes that Terragrunt Stacks have already graduated from the [experimental stage](https://terragrunt.gruntwork.io/docs/reference/experiments/#stacks), and was written when Terragrunt Stacks were very close to being marked as generally available. If they are not yet generally available, you will need to use the `--experiment stacks` flag when running Terragrunt commands that use Terragrunt Stacks functionality.
+
+</Aside>
+
+## Migrating from the `terragrunt-infrastructure-live-example` repository
+
+If you have an existing repository that was started using the [terragrunt-infrastructure-live-example](https://github.com/gruntwork-io/terragrunt-infrastructure-live-example) repository as a starting point, follow the steps below to migrate your existing configurations to take advantage of the patterns available using Terragrunt Stacks.
+
+### Step 1: Assess your current infrastructure
+
+Before you get started adjusting any of your existing configurations, it's important to understand the current state of your infrastructure.
+
+How much of it do you regularly update? Does any of it result in frustration or difficulty? Why?
+
+Determine whether it's a good time to be migrating your infrastructure to new patterns, and if so, how much of it you're willing to migrate. If you are happy, and successful with your current patterns, you may not need to migrate any existing configuration, and that's great! Consider this a best practice that you can adopt when you start to introduce new infrastructure, and that you may want to adjust your existing infrastructure configurations over time to take advantage of new patterns.
+
+The advantages of using the new paradigm with Terragrunt Stacks are:
+
+- You can more easily manage your infrastructure at scale.
+- You can more easily manage your infrastructure in different environments.
+- You can more easily manage your infrastructure across multiple accounts and regions.
+- You can more easily manage your infrastructure across multiple teams and organizations.
+
+We, at Gruntwork, generally consider this paradigm to be the best practice for managing Infrastructure as Code (IaC) at scale, which is why we've created this migration guide to help you transition to it.
+
+If you get overwhelmed at any point, read the [Support docs](/docs/community/support/) to learn how you can get help.
+
+### Step 2: Update your Terragrunt version
+
+Now that you've determined that you want to migrate some or all of your infrastructure to new patterns, the next step is to ensure that you have a version of Terragrunt that supports the `terragrunt.stack.hcl` file.
+
+You can do this by updating the version of Terragrunt you use to the latest available version. If you would like more information on how to update your Terragrunt version, see the [Installation](/docs/getting-started/install/) guide.
+
+### Step 3: Add `.terragrunt-stack` directories to your repository `.gitignore` file
+
+Now that you're adopting Terragrunt Stacks, you'll want to add the `.terragrunt-stack` directories to your repository `.gitignore` file.
+
+```bash
+echo ".terragrunt-stack" >> .gitignore
+git add .gitignore
+git commit -m "Add .terragrunt-stack to .gitignore"
+```
+
+This will prevent you from accidentally committing `.terragrunt-stack` directories to your repository, which is good because you can always regenerate them on demand using the `terragrunt stack generate` command.
+
+All other `terragrunt stack` commands also automatically generate the `.terragrunt-stack` directory on demand, so you can safely ignore it.
+
+### Step 4: Re-define existing infrastructure using `terragrunt.stack.hcl` files
+
+The infrastructure that you already have can be re-defined using `terragrunt.stack.hcl` files, reducing the amount of code that you need to maintain in your repository.
+
+To do this, you'll need to:
+
+{/*
+
+    We have a repository here: [infrastructure-catalog](https://github.com/gruntwork-io/terragrunt-infrastructure-catalog-example), but we have not yet published it, so we'll just be vague about this for now.
+
+    Units example link: [units](https://github.com/gruntwork-io/terragrunt-infrastructure-catalog-example/tree/main/units)
+    Usage example link: [terragrunt.stack.hcl examples](https://github.com/gruntwork-io/terragrunt-infrastructure-catalog-example/tree/main/examples/terragrunt/stacks)
+
+*/}
+
+1. Create an `infrastructure-catalog` repository if you don't already have one to store your infrastructure configurations.
+2. Define the units that you want to reproduce from your `infrastructure-catalog` repository in your `infrastructure-live` repository via `terragrunt.stack.hcl` files.
+3. Find a collection of units that you want to abstract into a stack, and define a `terragrunt.stack.hcl` file for them.
+
+   For example, say you have a collection of units like this, that you want to abstract into a stack:
+
+   <FileTree>
+
+   - non-prod
+     - us-east-1
+       - stateful-ec2-asg-service
+         - service
+            - terragrunt.hcl
+         - db
+            - terragrunt.hcl
+         - sgs
+            - asg
+                - terragrunt.hcl
+
+   </FileTree>
+
+   This collection of units can be abstracted into a single stack by creating a `terragrunt.stack.hcl` file in the `stateful-ec2-asg-service` directory that references each unit configuration, as defined in your `infrastructure-catalog` repository (in this example, the `infrastructure-catalog` repository is hosted at `git@github.com:acme/infrastructure-catalog.git`):
+
+   ```hcl
+    ## non-prod/us-east-1/stateful-ec2-asg-service/terragrunt.stack.hcl
+
+   unit "service" {
+     source = "git::git@github.com:acme/infrastructure-catalog.git//units/ec2-asg-stateful-service"
+     path   = "service"
+
+     no_dot_terragrunt_stack = true
+
+     ## Add any additional configuration for the service unit here
+   }
+
+   unit "db" {
+     source = "git::git@github.com:acme/infrastructure-catalog.git//units/mysql"
+     path   = "db"
+
+     no_dot_terragrunt_stack = true
+
+     ## Add any additional configuration for the db unit here
+   }
+
+   unit "asg-sg" {
+     source = "git::git@github.com:acme/infrastructure-catalog.git//units/security-group"
+     path   = "sgs/asg"
+
+     no_dot_terragrunt_stack = true
+
+     ## Add any additional configuration for the asg-sg unit here
+   }
+   ```
+
+   **Note the use of the `no_dot_terragrunt_stack` attribute.** This is used to prevent Terragrunt from automatically generating the units into a `.terragrunt-stack` directory. This is important, because you are probably using `path_relative_to_include()` in the `key` attribute of the `remote_state` block of the root `root.hcl` file, which is included in every unit. By specifying `no_dot_terragrunt_stack = true`, the generated units will be generated into the same directory as they were before, and the `path_relative_to_include()` function will resolve to the same path as before. Migrating to a `terragrunt.stack.hcl` file in this way allows you to migrate your infrastructure to the new patterns outlined here at your own pace, and to migrate state between the old and new patterns if you want to.
+
+   Now, you can remove the existing unit configurations, and regenerate them on demand using the `terragrunt stack generate` command.
+
+   ```bash
+   cd non-prod/us-east-1/stateful-ec2-asg-service
+   rm -rf service db sgs
+   ```
+
+   If you have identical unit configurations after performing the following, you can remove the unit configurations again, add them to a `.gitignore` file, and commit the new `terragrunt.stack.hcl` file.
+
+   ```bash
+   terragrunt stack generate
+   ```
+
+   <FileTree>
+
+   - non-prod
+     - us-east-1
+       - stateful-ec2-asg-service
+         - **terragrunt.stack.hcl**
+         - service
+            - terragrunt.hcl < This should be identical to the unit configuration before
+         - db
+            - terragrunt.hcl < This should be identical to the unit configuration before
+         - sgs
+            - asg
+                - terragrunt.hcl < This should be identical to the unit configuration before
+
+   </FileTree>
+
+
+   Now that you've confirmed generation is working, you can remove the unit configurations again, add them to a `.gitignore` file, and commit the new `terragrunt.stack.hcl` file.
+
+   ```bash
+   rm -rf service db sgs
+   git add terragrunt.stack.hcl service db sgs
+   git commit -m "Remove unit configurations and add terragrunt.stack.hcl"
+   echo "service" >> .gitignore
+   echo "db" >> .gitignore
+   echo "sgs" >> .gitignore
+   git add .gitignore
+   git commit -m "Add unit configurations to .gitignore"
+   ```
+
+   Your repository should now look like this:
+
+   <FileTree>
+
+   - non-prod
+     - us-east-1
+       - stateful-ec2-asg-service
+          - **.gitignore**
+          - terragrunt.stack.hcl
+
+   </FileTree>
+
+
+   You can repeat this process as much as you want, abstracting more and more of your infrastructure into Terragrunt Stacks.
+
+### Step 5: Remove reliance on the `_envcommon` directory
+
+The `_envcommon` directory is no longer needed to create "Don't Repeat Yourself" (DRY) configurations with Terragrunt, and is no longer recommended as a best practice.
+
+If you would like to remove usage of the `_envcommon` directory, you can do so by replacing usage of the `include` block referencing the `_envcommon` directory with content directly committed to `terragrunt.hcl` files.
+
+For example, say you have a `terragrunt.hcl` file that looks like this:
+
+```hcl
+## non-prod/us-east-1/mysql/terragrunt.hcl
+
+include "root" {
+  path = find_in_parent_folders("root.hcl")
+}
+
+include "envcommon" {
+  path = "${dirname(find_in_parent_folders("root.hcl"))}/_envcommon/mysql.hcl"
+  expose = true
+}
+
+terraform {
+  source = "${include.envcommon.locals.base_source_url}?ref=v0.8.0"
+}
+
+inputs = {
+  instance_class    = "db.t2.medium"
+  allocated_storage = 100
+}
+```
+
+and an `_envcommon/mysql.hcl` file that looks like this:
+
+```hcl
+## _envcommon/mysql.hcl
+
+locals {
+  environment_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))
+
+  env = local.environment_vars.locals.environment
+
+  base_source_url = "git::git@github.com:acme/infrastructure-catalog.git//modules/mysql"
+}
+
+inputs = {
+  name              = "mysql_${local.env}"
+  instance_class    = "db.t2.micro"
+  allocated_storage = 20
+  storage_type      = "standard"
+  master_username   = "admin"
+}
+```
+
+This pattern was previously used to create "Don't Repeat Yourself" (DRY) configurations with Terragrunt. However, this pattern is no longer recommended as a best practice, and is no longer needed to create DRY configurations with Terragrunt.
+
+Instead, you can create a `terragrunt.hcl` file in your `infrastructure-catalog` repository that looks like this:
+
+```hcl
+## units/mysql/terragrunt.hcl
+
+include "root" {
+  path = find_in_parent_folders("root.hcl")
+}
+
+terraform {
+  source = "git::git@github.com:acme/infrastructure-catalog.git//modules/mysql?ref=${values.version}"
+}
+
+inputs = {
+  ## Required inputs
+  name              = values.name
+  instance_class    = values.instance_class
+  allocated_storage = values.allocated_storage
+  storage_type      = values.storage_type
+  master_username   = values.master_username
+  master_password   = values.master_password
+
+  ## Optional inputs
+  skip_final_snapshot = try(values.skip_final_snapshot, null)
+  engine_version      = try(values.engine_version, null)
+}
+```
+
+Then reference that `terragrunt.hcl` file in your `terragrunt.stack.hcl` files, like so:
+
+```hcl
+## non-prod/us-east-1/terragrunt.stack.hcl
+
+unit "mysql" {
+  source = "git::git@github.com:acme/infrastructure-catalog.git//units/mysql"
+  path   = "mysql"
+
+  ## As discussed above, this prevents Terragrunt from automatically generating the units into a `.terragrunt-stack` directory.
+  no_dot_terragrunt_stack = true
+
+  values = {
+    version = "v0.8.0"
+    name = "mysql_dev"
+    instance_class = "db.t2.micro"
+    allocated_storage = 20
+    storage_type = "standard"
+    master_username = "admin"
+  }
+}
+```
+
+Now, all your unit configurations can be found directly in the `terragrunt.hcl` file in the `infrastructure-catalog` repository, without having to bounce around between different included or referenced files, and you have an explicit interface for the values that can be set externally, via the `values` attribute.
+
+Different environments can pin different versions of the unit, and that allows for easy atomic updates (and rollbacks) of both OpenTofu/Terraform module versions and Terragrunt unit configurations if needed.
+
+### Step 6: Update your CI/CD pipeline
+
+Chances are, if you're currently performing Terragrunt updates via a CI/CD pipeline (and you aren't using [Gruntwork Pipelines](https://www.gruntwork.io/platform/pipelines)), your CI/CD pipeline doesn't yet have integration with Terragrunt Stacks.
+
+There are a few options for how to proceed here.
+
+{/*
+    Note to maintainers: This is basically ready to go, but we're not announcing it yet, so we'll leave this commented out for now.
+
+1. You can start using [Gruntwork Pipelines](https://www.gruntwork.io/platform/pipelines).
+
+   This is a little self-serving, as it's a paid product that we offer at Gruntwork, but we are constantly refining it as the best way to manage IaC at scale via GitOps, and we've built first-class support for Terragrunt Stacks into it, so it's the most straightforward way to get Terragrunt Stacks support out of the box.
+*/}
+
+1. You can simply commit the generated `.terragrunt-stack` directories to your repository.
+
+   This is the easiest option when managing CI/CD yourself, but it also means that you won't gain some of the benefits that come from using Terragrunt Stacks. When getting started, however, this is a good way to avoid the additional technical debt that comes from having to update your CI/CD pipeline to support Terragrunt Stacks, while learning how to use `terragrunt.stack.hcl` files, and reorganizing your infrastructure configurations.
+
+   To do this, remove the `.terragrunt-stack` entry from your `.gitignore` file, and commit the changes to your repository. You can then manually run the `terragrunt stack generate` command to generate the `.terragrunt-stack` directories on demand, and commit them to your repository, allowing your CI/CD pipeline to completely ignore the fact that you're using Terragrunt Stacks. The units generated by the `terragrunt stack generate` command are completely compatible with units that you can author manually, so you don't have to worry about any incompatibility issues that might arise from this approach.
+
+1. You can configure your CI/CD pipeline to run the `terragrunt stack generate` command whenever your pipeline runs, and leverage the generated `.terragrunt-stack` directories in your pipeline.
+
+   Depending on the complexity of your CI/CD pipeline, this might be as simple as performing the following:
+
+   ```bash
+   terragrunt stack generate
+   terragrunt run --all plan/apply --non-interactive
+   ```
+
+   This doesn't account for destroys or the reduction of blast radius for changes by carefully inspecting Git diffs, but it's a good start for users that don't have CI/CD pipelines that are too complex.
+
+   There is an open RFC in GitHub ([Filter Flag](https://github.com/gruntwork-io/terragrunt/issues/4060)) that would allow for this kind of complex filtering out of the box with Terragrunt, but at the moment, it's still an open RFC.
diff --git a/docs-starlight/src/data/commands/dag/graph.mdx b/docs-starlight/src/data/commands/dag/graph.mdx
index 1b7ca380..8fbd045f 100644
--- a/docs-starlight/src/data/commands/dag/graph.mdx
+++ b/docs-starlight/src/data/commands/dag/graph.mdx
@@ -6,12 +6,22 @@ sidebar:
   order: 1000
 description: Graph the Directed Acyclic Graph (DAG) in DOT language.
 usage: |
-  Graph the Directed Acyclic Graph (DAG) in DOT language.
+  Print a visual representation of the Terragrunt dependency graph in DOT language format.
+  This command analyzes your Terragrunt configuration and outputs a directed acyclic graph (DAG) showing the relationships and dependencies between your Terraform modules.
 experiment:
   control: cli-redesign
   name: cli redesign
 examples:
   - description: Graph all dependencies in the graph as a DotViz graph.
     code: |
-      terragrunt dag graph
+      $ terragrunt dag graph
+      digraph {
+        "alb" ;
+        "ecs" ;
+        "ecs" -> "alb";
+      }
+  - description: Graph all dependencies in visual diagram.
+    code: |
+      $ terragrunt dag graph  | dot -Tpng > graph.png
+
 ---
diff --git a/docs-starlight/src/data/commands/find.mdx b/docs-starlight/src/data/commands/find.mdx
index 81eba5fa..621996be 100644
--- a/docs-starlight/src/data/commands/find.mdx
+++ b/docs-starlight/src/data/commands/find.mdx
@@ -37,10 +37,40 @@ examples:
       Sort configurations based on their dependencies using DAG mode.
     code: |
       terragrunt find --dag
+  - description: |
+      Sort configurations based on dependency graph as if running plan command.
+    code: |
+      $ terragrunt find --queue-construct-as=plan
+      stacks/live/dev
+      stacks/live/prod
+      units/live/dev/vpc
+      units/live/prod/vpc
+      units/live/dev/db
+      units/live/prod/db
+      units/live/dev/ec2
+      units/live/prod/ec2
+
+  - description: |
+      Sort configurations based on dependency graph as if running destroy command.
+    code: |
+      $ terragrunt find --queue-construct-as=destroy
+      stacks/live/dev
+      stacks/live/prod
+      units/live/dev/ec2
+      units/live/prod/ec2
+      units/live/dev/db
+      units/live/prod/db
+      units/live/dev/vpc
+      units/live/prod/vpc
+
   - description: |
       Include dependency information in the output.
     code: |
       terragrunt find --dependencies --format 'json'
+  - description: |
+      Include exclude configuration in the output.
+    code: |
+      terragrunt find --exclude --format 'json'
   - description: |
       Include external dependencies in the output.
     code: |
@@ -51,7 +81,9 @@ flags:
   - find-dag
   - find-hidden
   - find-dependencies
+  - find-exclude
   - find-external
+  - queue-construct-as
 ---
 
 import { Aside, Badge } from '@astrojs/starlight/components';
@@ -111,6 +143,44 @@ unitD           # depends on unitC
 
 If multiple configurations share common dependencies, they will be sorted in lexical order.
 
+## Queue Construct As
+
+The `find` command supports the `--queue-construct-as` flag (or its shorter alias `--as`) to sort output based on the dependency graph, as if a particular command was run.
+
+For example, when using the `plan` command:
+
+```bash
+terragrunt find --queue-construct-as=plan
+stacks/live/dev
+stacks/live/prod
+units/live/dev/vpc
+units/live/prod/vpc
+units/live/dev/db
+units/live/prod/db
+units/live/dev/ec2
+units/live/prod/ec2
+```
+
+This will sort the output based on the dependency graph, as if the `plan` command was run. All dependent units will appear *after* the units they depend on.
+
+When using the `destroy` command:
+
+```bash
+terragrunt find --as=destroy
+stacks/live/dev
+stacks/live/prod
+units/live/dev/ec2
+units/live/prod/ec2
+units/live/dev/db
+units/live/prod/db
+units/live/dev/vpc
+units/live/prod/vpc
+```
+
+This will sort the output based on the dependency graph, as if the `destroy` command was run. All dependent units will appear *before* the units they depend on.
+
+**Note:** The `--queue-construct-as` flag implies the `--dag` flag.
+
 ## Dependencies
 
 You can include dependency information in the output using the `--dependencies` flag. When enabled, the JSON output will include the dependency relationships between configurations:
@@ -131,6 +201,37 @@ terragrunt find --dependencies --format=json
 ]
 ```
 
+## Exclude Configuration
+
+You can include exclude configuration in the output using the `--exclude` flag. When enabled, the JSON output will include the configurations of the `exclude` block in the discovered units:
+
+```bash
+terragrunt find --exclude --format=json
+[
+  {
+    "type": "unit",
+    "path": "action/exclude-apply",
+    "exclude": {
+      "exclude_dependencies": true,
+      "actions": [
+        "apply"
+      ],
+      "if": true
+    }
+  }
+]
+```
+
+You can combine this with the `--queue-construct-as` flag to dry-run behavior relevant to excludes:
+
+```bash
+terragrunt find --exclude --queue-construct-as=plan --format=json
+```
+
+`find` will remove any units that would match the exclude configuration.
+
+## External Dependencies
+
 By default, external dependencies (those outside the working directory) are not part of the overall results (although, they will be mentioned in the dependency section of the JSON output). Use the `--external` flag to include them as top-level results:
 
 ```bash
diff --git a/docs-starlight/src/data/commands/list.mdx b/docs-starlight/src/data/commands/list.mdx
index 1052f544..40f8f252 100644
--- a/docs-starlight/src/data/commands/list.mdx
+++ b/docs-starlight/src/data/commands/list.mdx
@@ -65,6 +65,22 @@ examples:
       unit  b-dependency
       unit  a-dependent   b-dependency
 
+  - description: |
+      List all units in dependency order as if running plan command.
+    code: |
+      $ terragrunt list --queue-construct-as=plan
+      stacks/live/dev      stacks/live/prod     units/live/dev/vpc
+      units/live/prod/vpc  units/live/dev/db    units/live/prod/db
+      units/live/dev/ec2   units/live/prod/ec2
+
+  - description: |
+      List all units in dependency order as if running destroy command.
+    code: |
+      $ terragrunt list --queue-construct-as=destroy
+      stacks/live/dev      stacks/live/prod     units/live/dev/ec2
+      units/live/prod/ec2  units/live/dev/db    units/live/prod/db
+      units/live/dev/vpc   units/live/prod/vpc
+
 flags:
   - list-format
   - list-hidden
@@ -73,6 +89,7 @@ flags:
   - list-tree
   - list-long
   - list-dag
+  - queue-construct-as
 ---
 
 import { Aside, Badge } from '@astrojs/starlight/components';
@@ -149,6 +166,34 @@ $ terragrunt list --tree --dag
     ╰── live/prod/ec2
 ```
 
+## Queue Construct As
+
+The `list` command supports the `--queue-construct-as` flag (or its shorter alias `--as`) to sort output based on the dependency graph, as if a particular command was run.
+
+For example, when using the `plan` command:
+
+```bash
+$ terragrunt list --queue-construct-as=plan
+stacks/live/dev      stacks/live/prod     units/live/dev/vpc
+units/live/prod/vpc  units/live/dev/db    units/live/prod/db
+units/live/dev/ec2   units/live/prod/ec2
+```
+
+This will sort the output based on the dependency graph, as if the `plan` command was run. All dependent units will appear *after* the units they depend on.
+
+When using the `destroy` command:
+
+```bash
+$ terragrunt list --queue-construct-as=destroy
+stacks/live/dev      stacks/live/prod     units/live/dev/ec2
+units/live/prod/ec2  units/live/dev/db    units/live/prod/db
+units/live/dev/vpc   units/live/prod/vpc
+```
+
+This will sort the output based on the dependency graph, as if the `destroy` command was run. All dependent units will appear *before* the units they depend on.
+
+**Note:** The `--queue-construct-as` flag implies the `--dag` flag.
+
 ## Dependencies and Discovery
 
 ### Dependencies
diff --git a/docs-starlight/src/data/flags/find-exclude.mdx b/docs-starlight/src/data/flags/find-exclude.mdx
new file mode 100644
index 00000000..75f717be
--- /dev/null
+++ b/docs-starlight/src/data/flags/find-exclude.mdx
@@ -0,0 +1,89 @@
+---
+name: find-exclude
+description: Include exclude configuration in the output
+type: boolean
+env:
+  - TG_FIND_EXCLUDE
+---
+
+Include exclude configuration in the output. When enabled, the JSON output will include the configurations of the `exclude` block in the discovered units.
+
+## Usage
+
+```bash
+--exclude
+```
+
+## Examples
+
+Show exclude configurations in JSON format:
+```bash
+terragrunt find --exclude --format=json
+```
+
+Show exclude configurations with queue construct simulation:
+```bash
+terragrunt find --exclude --queue-construct-as=plan --format=json
+```
+
+## Behavior
+
+When enabled, the JSON output will include any `exclude` block configurations found in the units:
+
+```bash
+$ terragrunt find --exclude --format=json | jq
+[
+  {
+    "type": "unit",
+    "path": "action/exclude-apply",
+    "exclude": {
+      "exclude_dependencies": true,
+      "actions": [
+        "apply"
+      ],
+      "if": true
+    }
+  },
+  {
+    "type": "unit",
+    "path": "action/exclude-plan",
+    "exclude": {
+      "exclude_dependencies": true,
+      "actions": [
+        "plan"
+      ],
+      "if": true
+    }
+  },
+  {
+    "type": "unit",
+    "path": "all-except-output/app1",
+    "exclude": {
+      "exclude_dependencies": true,
+      "actions": [
+        "all_except_output"
+      ],
+      "if": true
+    }
+  }
+]
+```
+
+Note that you can combine this with the `--queue-construct-as` flag to dry-run behavior relevant to excludes.
+
+```bash
+$ terragrunt find --exclude --queue-construct-as=plan --format=json | jq
+[
+  {
+    "type": "unit",
+    "path": "action/exclude-apply",
+    "exclude": {
+      "exclude_dependencies": true,
+      "actions": [
+        "apply"
+      ],
+      "if": true
+    }
+  }
+]
+```
diff --git a/docs-starlight/src/data/flags/queue-construct-as.mdx b/docs-starlight/src/data/flags/queue-construct-as.mdx
new file mode 100644
index 00000000..85f318f3
--- /dev/null
+++ b/docs-starlight/src/data/flags/queue-construct-as.mdx
@@ -0,0 +1,45 @@
+---
+name: queue-construct-as
+description: |
+  Sort output based on the dependency graph, as if a particular command was run. This flag implies the `--dag` flag.
+
+  The flag has a shorter alias `--as`.
+type: string
+env:
+  - TG_QUEUE_CONSTRUCT_AS
+aliases:
+  - --as
+---
+
+Sort output based on the dependency graph, as if a particular command was run. This flag implies the `--dag` flag.
+
+The flag has a shorter alias `--as`.
+
+## Usage
+
+```bash
+--queue-construct-as=COMMAND
+--as=COMMAND
+```
+
+Where `COMMAND` is the command to simulate (e.g., `plan`, `destroy`).
+
+## Examples
+
+Sort output as if running plan command (dependencies after dependents):
+```bash
+terragrunt find --queue-construct-as=plan
+```
+
+Sort output as if running destroy command (dependencies before dependents):
+```bash
+terragrunt find --queue-construct-as=destroy
+```
+
+## Behavior
+
+When using `plan` command, all dependent units will appear *after* the units they depend on.
+
+When using `destroy` command, all dependent units will appear *before* the units they depend on.
+
+**Note:** This flag implies the `--dag` flag.
diff --git a/docs/_docs/04_reference/01-configuration.md b/docs/_docs/04_reference/01-configuration.md
index d309f0c8..0931fbd9 100644
--- a/docs/_docs/04_reference/01-configuration.md
+++ b/docs/_docs/04_reference/01-configuration.md
@@ -11,7 +11,7 @@ nav_title_link: /docs/
 slug: configuration
 ---
 
-Terragrunt configuration is defined in a `terragrunt.hcl` file. This uses the same HCL syntax as OpenTofu/Terraform itself.
+Terragrunt unit configuration is defined in `terragrunt.hcl` files. These files use the same HCL syntax as OpenTofu/Terraform itself.
 
 Here’s an example:
 
@@ -84,6 +84,18 @@ The results of this pass are then used to build the dependency graph of the unit
 
 This allows Terragrunt to avoid resolving `dependency` on units that haven’t been applied yet when doing a clean deployment from scratch with `run-all apply`.
 
+## Stacks
+
+When multiple units, each with their own `terragrunt.hcl` file exist in child directories of a single parent directory, that parent directory becomes a [stack](/docs/getting-started/terminology#stack).
+
+To make it easier to generate configurations like this, Terragrunt has special tooling in the form of `terragrunt.stack.hcl` files. `terragrunt.stack.hcl` files support all the same HCL functions as `terragrunt.hcl` files, however, they don't support any top-level attributes, and the configuration blocks they support are limited to the following:
+
+- [unit](/docs/reference/hcl/config-blocks-and-attributes#unit)
+- [stack](/docs/reference/hcl/config-blocks-and-attributes#stack)
+- [locals](/docs/reference/hcl/config-blocks-and-attributes#locals)
+
+These special configurations are used by the [stack generate command](/docs/reference/cli/commands/stack/generate) (and all the other `stack` prefixed commands) to generate units programmatically, on demand. The units they generate are valid unit configurations, and can be read and used as if they were manually authored.
+
 ## Formatting HCL files
 
 You can rewrite the HCL files to a canonical format using the `hclfmt` command built into `terragrunt`. Similar to `tofu fmt`, this command applies a subset of [the OpenTofu/Terraform language style conventions](https://www.terraform.io/docs/configuration/style.html), along with other minor adjustments for readability.
diff --git a/docs/_docs/04_reference/02-cli-options.md b/docs/_docs/04_reference/02-cli-options.md
index 51b6ff99..23a6b99b 100644
--- a/docs/_docs/04_reference/02-cli-options.md
+++ b/docs/_docs/04_reference/02-cli-options.md
@@ -57,6 +57,7 @@ The commands used for managing Terragrunt configuration itself are:
   - [output-module-groups](#output-module-groups)
   - [render-json](#render-json)
   - [info](#info)
+  - [dag](#dag)
   - [terragrunt-info](#terragrunt-info)
   - [validate-inputs](#validate-inputs)
 
@@ -731,6 +732,41 @@ unitC # depends on unitA
 unitD # depends on unitC
 ```
 
+##### Find Queue Construct As
+
+The `find` command supports the `--queue-construct-as` flag to sort output based on the dependency graph, as if a particular command was run.
+
+```bash
+$ terragrunt find --queue-construct-as=plan
+stacks/live/dev
+stacks/live/prod
+units/live/dev/vpc
+units/live/prod/vpc
+units/live/dev/db
+units/live/prod/db
+units/live/dev/ec2
+units/live/prod/ec2
+```
+
+This will sort the output based on the dependency graph, as if the `plan` command was run. Meaning that all dependent units will appear _after_ the units they depend on.
+
+```bash
+# Note that you can also use the shorter alias of the flag, `--as`.
+$ terragrunt find --as=destroy
+stacks/live/dev
+stacks/live/prod
+units/live/dev/ec2
+units/live/prod/ec2
+units/live/dev/db
+units/live/prod/db
+units/live/dev/vpc
+units/live/prod/vpc
+```
+
+This will sort the output based on the dependency graph, as if the `destroy` command was run. Meaning that all dependent units will appear _before_ the units they depend on.
+
+**Note:** The `--queue-construct-as` flag implies the `--dag` flag.
+
 ##### Find Dependencies
 
 You can include dependency information in the output using the `--dependencies` flag. When enabled, the JSON output will include the dependency relationships between configurations:
@@ -751,6 +787,70 @@ $ terragrunt find --dependencies --format=json | jq
 ]
 ```
 
+##### Find Exclude
+
+You can also include exclude configuration in the output using the `--exclude` flag. When enabled, the JSON output will include the configurations of the `exclude` block in the discovered units.
+
+```bash
+$ terragrunt find --exclude --format=json | jq
+[
+  {
+    "type": "unit",
+    "path": "action/exclude-apply",
+    "exclude": {
+      "exclude_dependencies": true,
+      "actions": [
+        "apply"
+      ],
+      "if": true
+    }
+  },
+  {
+    "type": "unit",
+    "path": "action/exclude-plan",
+    "exclude": {
+      "exclude_dependencies": true,
+      "actions": [
+        "plan"
+      ],
+      "if": true
+    }
+  },
+  {
+    "type": "unit",
+    "path": "all-except-output/app1",
+    "exclude": {
+      "exclude_dependencies": true,
+      "actions": [
+        "all_except_output"
+      ],
+      "if": true
+    }
+  }
+]
+```
+
+Note that you can combine this with the `--queue-construct-as` flag to dry-run behavior relevant to excludes.
+
+```bash
+$ terragrunt find --exclude --queue-construct-as=plan --format=json | jq
+[
+  {
+    "type": "unit",
+    "path": "action/exclude-apply",
+    "exclude": {
+      "exclude_dependencies": true,
+      "actions": [
+        "apply"
+      ],
+      "if": true
+    }
+  }
+]
+```
+
+`find` will remove any units that would match the exclude configuration.
+
 ##### Find External Dependencies
 
 By default, external dependencies (those outside the working directory) are excluded. Use the `--external` flag to include them in the output:
@@ -886,6 +986,30 @@ $ terragrunt list --tree --dag
 
 ```
 
+##### List Queue Construct As
+
+Just like the `find` command, the `list` command supports the `--queue-construct-as` flag to sort output based on the dependency graph, as if a particular command was run.
+
+```bash
+$ terragrunt list --queue-construct-as=plan
+stacks/live/dev      stacks/live/prod     units/live/dev/vpc
+units/live/prod/vpc  units/live/dev/db    units/live/prod/db
+units/live/dev/ec2   units/live/prod/ec2
+```
+
+This will sort the output based on the dependency graph, as if the `plan` command was run. Meaning that all dependent units will appear _after_ the units they depend on.
+
+```bash
+$ terragrunt list --queue-construct-as=destroy
+stacks/live/dev      stacks/live/prod     units/live/dev/ec2
+units/live/prod/ec2  units/live/dev/db    units/live/prod/db
+units/live/dev/vpc   units/live/prod/vpc
+```
+
+This will sort the output based on the dependency graph, as if the `destroy` command was run. Meaning that all dependent units will appear _before_ the units they depend on.
+
+**Note:** The `--queue-construct-as` flag implies the `--dag` flag.
+
 ### Configuration commands
 
 #### graph-dependencies
@@ -1115,6 +1239,28 @@ Example output:
 }
 ```
 
+#### dag
+
+The `dag` command is used to interact with the Directed Acyclic Graph.
+
+It will soon replace the [`graph-dependencies`](#graph-dependencies) command. See the [CLI Redesign](/docs/reference/experiments/#cli-redesign) documentation for more information.
+
+##### Print graph
+
+Print a visual representation of the Terragrunt dependency graph in DOT language format. This command analyzes your Terragrunt configuration and outputs a directed acyclic graph (DAG) showing the relationships and dependencies between your Terraform modules.
+
+Example usage:
+
+```bash
+terragrunt dag graph 
+```
+
+The output can be piped to Graphviz tools to generate visual diagrams:
+
+```bash
+terragrunt dag graph  | dot -Tpng > graph.png
+```
+
 #### terragrunt-info
 
 Emits limited terragrunt state on `stdout` in a JSON format and exits.
@@ -1219,7 +1365,9 @@ This command will exit with an error if terragrunt detects any unused inputs or
     - [find](#find)
       - [Find Output Format](#find-output-format)
       - [Find DAG Mode](#find-dag-mode)
+      - [Find Queue Construct As](#find-queue-construct-as)
       - [Find Dependencies](#find-dependencies)
+      - [Find Exclude](#find-exclude)
       - [Find External Dependencies](#find-external-dependencies)
       - [Find Hidden Configurations](#find-hidden-configurations)
     - [list](#list)
@@ -1228,6 +1376,7 @@ This command will exit with an error if terragrunt detects any unused inputs or
         - [Long Format](#long-format)
         - [Tree Format](#tree-format)
       - [DAG Mode](#dag-mode)
+      - [List Queue Construct As](#list-queue-construct-as)
   - [Configuration commands](#configuration-commands)
     - [graph-dependencies](#graph-dependencies)
     - [hclfmt](#hclfmt)
@@ -1235,7 +1384,9 @@ This command will exit with an error if terragrunt detects any unused inputs or
     - [output-module-groups](#output-module-groups)
     - [render-json](#render-json)
     - [info](#info)
+    - [dag](#dag)
       - [Strict command](#strict-command)
+      - [Print command](#print-command)
     - [terragrunt-info](#terragrunt-info)
     - [validate-inputs](#validate-inputs)
 - [Flags](#flags)
diff --git a/docs/_docs/04_reference/04-config-blocks-and-attributes.md b/docs/_docs/04_reference/04-config-blocks-and-attributes.md
index 57310a82..e971882e 100644
--- a/docs/_docs/04_reference/04-config-blocks-and-attributes.md
+++ b/docs/_docs/04_reference/04-config-blocks-and-attributes.md
@@ -1665,6 +1665,7 @@ The `unit` block supports the following arguments:
 - `path` (attribute): The relative path where this unit should be deployed within the stack directory (`.terragrunt-stack`). If an absolute path is provided here, Terragrunt will generate the stack in that location, instead of generating it in a path relative to the `.terragrunt-stack` directory. Also take note of the `no_dot_terragrunt_stack` attribute below, which can impact this.
 - `values` (attribute, optional): A map of values that will be passed to the unit as inputs.
 - `no_dot_terragrunt_stack` (attribute, optional): A boolean flag (`true` or `false`). When set to `true`, the unit **will not** be placed inside the `.terragrunt-stack` directory but will instead be generated in the same directory where `terragrunt.stack.hcl` is located. This allows for a **soft adoption** of stacks, making it easier for users to start using `terragrunt.stack.hcl` without modifying existing directory structures, or performing state migrations.
+- `no_validation` (attribute, optional): A boolean flag (`true` or `false`) that controls whether Terragrunt should validate the unit's configuration. When set to `true`, Terragrunt will skip validation checks for this unit.
 
 Example:
 
@@ -1775,6 +1776,7 @@ The `stack` block supports the following arguments:
 - `path` (attribute): The relative path within `.terragrunt-stack` where this stack should be generated.If an absolute path is provided here, Terragrunt will generate the stack in that location, instead of generating it in a path relative to the `.terragrunt-stack` directory. Also take note of the `no_dot_terragrunt_stack` attribute below, which can impact this.
 - `values` (attribute, optional): A map of custom values that can be passed to the stack. These values can be referenced within the stack's configuration files, allowing for customization without modifying the stack source.
 - `no_dot_terragrunt_stack` (attribute, optional): A boolean flag (`true` or `false`). When set to `true`, the stack **will not** be placed inside the `.terragrunt-stack` directory but will instead be generated in the same directory where `terragrunt.stack.hcl` is located. This allows for a **soft adoption** of stacks, making it easier for users to start using `terragrunt.stack.hcl` without modifying existing directory structures, or performing state migrations.
+- `no_validation` (attribute, optional): A boolean flag (`true` or `false`) that controls whether Terragrunt should validate the stack's configuration. When set to `true`, Terragrunt will skip validation checks for this stack.
 
 Example:
 
diff --git a/docs/_docs/04_reference/06-experiments.md b/docs/_docs/04_reference/06-experiments.md
index ac5d11b0..d711b633 100644
--- a/docs/_docs/04_reference/06-experiments.md
+++ b/docs/_docs/04_reference/06-experiments.md
@@ -164,7 +164,7 @@ To transition `cli-redesign` features to a stable release, the following must be
   - [x] Add support for `find` with `--hidden` flag.
   - [x] Add support for `find` with `--sort=alpha` flag.
   - [x] Add support for `find` with `--sort=dag` flag.
-  - [ ] Add support for `find` with the `exclude` block used to exclude units from the search.
+  - [x] Add support for `find` with the `exclude` block used to exclude units from the search.
   - [ ] Add integration with `symlinks` experiment to support finding units/stacks via symlinks.
   - [x] Add handling of broken configurations or configurations requiring authentication.
   - [x] Add integration test for `find` with `--sort=dag` flag on all the fixtures in the `test/fixtures` directory.
@@ -179,7 +179,7 @@ To transition `cli-redesign` features to a stable release, the following must be
   - [x] Add support for `list` with `--sort=dag` flag.
   - [x] Add support for `list` with `--group-by=fs` flag.
   - [x] Add support for `list` with `--group-by=dag` flag.
-  - [ ] Add support for `list` with the `exclude` block used to exclude units from the search.
+  - [x] Add support for `list` with the `exclude` block used to exclude units from the search.
   - [ ] Add integration with `symlinks` experiment to support listing units/stacks via symlinks.
   - [x] Add handling of broken configurations or configurations requiring authentication.
   - [x] Add integration test for `list` with `--sort=dag` flag on all the fixtures in the `test/fixtures` directory.
diff --git a/docs/_docs/06_migration_guides/04-terragrunt-stacks.md b/docs/_docs/06_migration_guides/04-terragrunt-stacks.md
new file mode 100644
index 00000000..f6e31420
--- /dev/null
+++ b/docs/_docs/06_migration_guides/04-terragrunt-stacks.md
@@ -0,0 +1,318 @@
+---
+layout: collection-browser-doc
+title: Terragrunt Stacks
+category: migrate
+categories_url: migrate
+excerpt: Migration guide to rewrite configurations to use Terragrunt Stacks
+tags: ["migration", "community"]
+order: 503
+nav_title: Documentation
+nav_title_link: /docs/
+slug: terragrunt-stacks
+---
+
+> **Note:** This guide assumes that Terragrunt Stacks have already graduated from the [experimental stage](https://terragrunt.gruntwork.io/docs/reference/experiments/#stacks), and was written when Terragrunt Stacks were very close to being marked as generally available. If they are not yet generally available, you will need to use the `--experiment stacks` flag when running Terragrunt commands that use Terragrunt Stacks functionality.
+
+## Migrating from the `terragrunt-infrastructure-live-example` repository
+
+If you have an existing repository that was started using the [terragrunt-infrastructure-live-example](https://github.com/gruntwork-io/terragrunt-infrastructure-live-example) repository as a starting point, follow the steps below to migrate your existing configurations to take advantage of the patterns available using Terragrunt Stacks.
+
+### Step 1: Assess your current infrastructure
+
+Before you get started adjusting any of your existing configurations, it's important to understand the current state of your infrastructure.
+
+How much of it do you regularly update? Does any of it result in frustration or difficulty? Why?
+
+Determine whether it's a good time to be migrating your infrastructure to new patterns, and if so, how much of it you're willing to migrate. If you are happy, and successful with your current patterns, you may not need to migrate any existing configuration, and that's great! Consider this a best practice that you can adopt when you start to introduce new infrastructure, and that you may want to adjust your existing infrastructure configurations over time to take advantage of new patterns.
+
+The advantages of using the new paradigm with Terragrunt Stacks are:
+
+- You can more easily manage your infrastructure at scale.
+- You can more easily manage your infrastructure in different environments.
+- You can more easily manage your infrastructure across multiple accounts and regions.
+- You can more easily manage your infrastructure across multiple teams and organizations.
+
+We, at Gruntwork, generally consider this paradigm to be the best practice for managing Infrastructure as Code (IaC) at scale, which is why we've created this migration guide to help you transition to it.
+
+If you get overwhelmed at any point, read the [Support docs](/docs/community/support/) to learn how you can get help.
+
+### Step 2: Update your Terragrunt version
+
+Now that you've determined that you want to migrate some or all of your infrastructure to new patterns, the next step is to ensure that you have a version of Terragrunt that supports the `terragrunt.stack.hcl` file.
+
+You can do this by updating the version of Terragrunt you use to the latest available version. If you would like more information on how to update your Terragrunt version, see the [Installation](/docs/getting-started/install/) guide.
+
+### Step 3: Add `.terragrunt-stack` directories to your repository `.gitignore` file
+
+Now that you're adopting Terragrunt Stacks, you'll want to add the `.terragrunt-stack` directories to your repository `.gitignore` file.
+
+```bash
+echo ".terragrunt-stack" >> .gitignore
+git add .gitignore
+git commit -m "Add .terragrunt-stack to .gitignore"
+```
+
+This will prevent you from accidentally committing `.terragrunt-stack` directories to your repository, which is good because you can always regenerate them on demand using the `terragrunt stack generate` command.
+
+All other `terragrunt stack` commands also automatically generate the `.terragrunt-stack` directory on demand, so you can safely ignore it.
+
+### Step 4: Re-define existing infrastructure using `terragrunt.stack.hcl` files
+
+The infrastructure that you already have can be re-defined using `terragrunt.stack.hcl` files, reducing the amount of code that you need to maintain in your repository.
+
+To do this, you'll need to:
+
+<!-- Note to maintainers:
+    We have a repository here: [infrastructure-catalog](https://github.com/gruntwork-io/terragrunt-infrastructure-catalog-example), but we have not yet published it, so we'll just be vague about this for now.
+
+    Units example link: [units](https://github.com/gruntwork-io/terragrunt-infrastructure-catalog-example/tree/main/units)
+    Usage example link: [terragrunt.stack.hcl examples](https://github.com/gruntwork-io/terragrunt-infrastructure-catalog-example/tree/main/examples/terragrunt/stacks)
+-->
+
+1. Create an `infrastructure-catalog` repository if you don't already have one to store your infrastructure configurations.
+2. Define the units that you want to reproduce from your `infrastructure-catalog` repository in your `infrastructure-live` repository via `terragrunt.stack.hcl` files.
+3. Find a collection of units that you want to abstract into a stack, and define a `terragrunt.stack.hcl` file for them.
+
+   For example, say you have a collection of units like this, that you want to abstract into a stack:
+
+   ```tree
+   non-prod
+   └── us-east-1
+       └── stateful-ec2-asg-service
+           ├── service
+           │   └── terragrunt.hcl
+           ├── db
+           │   └── terragrunt.hcl
+           └── sgs
+               └── asg
+                   └── terragrunt.hcl
+   ```
+
+   This collection of units can be abstracted into a single stack by creating a `terragrunt.stack.hcl` file in the `stateful-ec2-asg-service` directory that references each unit configuration, as defined in your `infrastructure-catalog` repository (in this example, the `infrastructure-catalog` repository is hosted at `git@github.com:acme/infrastructure-catalog.git`):
+
+   ```hcl
+    ## non-prod/us-east-1/stateful-ec2-asg-service/terragrunt.stack.hcl
+
+   unit "service" {
+     source = "git::git@github.com:acme/infrastructure-catalog.git//units/ec2-asg-stateful-service"
+     path   = "service"
+
+     no_dot_terragrunt_stack = true
+
+     ## Add any additional configuration for the service unit here
+   }
+
+   unit "db" {
+     source = "git::git@github.com:acme/infrastructure-catalog.git//units/mysql"
+     path   = "db"
+
+     no_dot_terragrunt_stack = true
+
+     ## Add any additional configuration for the db unit here
+   }
+
+   unit "asg-sg" {
+     source = "git::git@github.com:acme/infrastructure-catalog.git//units/security-group"
+     path   = "sgs/asg"
+
+     no_dot_terragrunt_stack = true
+
+     ## Add any additional configuration for the asg-sg unit here
+   }
+   ```
+
+   **Note the use of the `no_dot_terragrunt_stack` attribute.** This is used to prevent Terragrunt from automatically generating the units into a `.terragrunt-stack` directory. This is important, because you are probably using `path_relative_to_include()` in the `key` attribute of the `remote_state` block of the root `root.hcl` file, which is included in every unit. By specifying `no_dot_terragrunt_stack = true`, the generated units will be generated into the same directory as they were before, and the `path_relative_to_include()` function will resolve to the same path as before. Migrating to a `terragrunt.stack.hcl` file in this way allows you to migrate your infrastructure to the new patterns outlined here at your own pace, and to migrate state between the old and new patterns if you want to.
+
+   Now, you can remove the existing unit configurations, and regenerate them on demand using the `terragrunt stack generate` command.
+
+   ```bash
+   cd non-prod/us-east-1/stateful-ec2-asg-service
+   rm -rf service db sgs
+   ```
+
+   If you have identical unit configurations after performing the following, you can remove the unit configurations again, add them to a `.gitignore` file, and commit the new `terragrunt.stack.hcl` file.
+
+   ```bash
+   terragrunt stack generate
+   ```
+
+   ```tree
+   non-prod
+   └── us-east-1
+       └── stateful-ec2-asg-service
+           ├── terragrunt.stack.hcl
+           ├── service
+           │   └── terragrunt.hcl ## This should be identical to the unit configuration before
+           ├── db
+           │   └── terragrunt.hcl ## This should be identical to the unit configuration before
+           └── sgs
+               └── asg/
+                   └── terragrunt.hcl ## This should be identical to the unit configuration before
+   ```
+
+   Now that you've confirmed generation is working, you can remove the unit configurations again, add them to a `.gitignore` file, and commit the new `terragrunt.stack.hcl` file.
+
+   ```bash
+   rm -rf service db sgs
+   git add terragrunt.stack.hcl service db sgs
+   git commit -m "Remove unit configurations and add terragrunt.stack.hcl"
+   echo "service" >> .gitignore
+   echo "db" >> .gitignore
+   echo "sgs" >> .gitignore
+   git add .gitignore
+   git commit -m "Add unit configurations to .gitignore"
+   ```
+
+   Your repository should now look like this:
+
+   ```tree
+   non-prod
+   └── us-east-1
+       └── stateful-ec2-asg-service
+           ├── .gitignore
+           └── terragrunt.stack.hcl
+   ```
+
+   You can repeat this process as much as you want, abstracting more and more of your infrastructure into Terragrunt Stacks.
+
+### Step 5: Remove reliance on the `_envcommon` directory
+
+The `_envcommon` directory is no longer needed to create "Don't Repeat Yourself" (DRY) configurations with Terragrunt, and is no longer recommended as a best practice.
+
+If you would like to remove usage of the `_envcommon` directory, you can do so by replacing usage of the `include` block referencing the `_envcommon` directory with content directly committed to `terragrunt.hcl` files.
+
+For example, say you have a `terragrunt.hcl` file that looks like this:
+
+```hcl
+## non-prod/us-east-1/mysql/terragrunt.hcl
+
+include "root" {
+  path = find_in_parent_folders("root.hcl")
+}
+
+include "envcommon" {
+  path = "${dirname(find_in_parent_folders("root.hcl"))}/_envcommon/mysql.hcl"
+  expose = true
+}
+
+terraform {
+  source = "${include.envcommon.locals.base_source_url}?ref=v0.8.0"
+}
+
+inputs = {
+  instance_class    = "db.t2.medium"
+  allocated_storage = 100
+}
+```
+
+and an `_envcommon/mysql.hcl` file that looks like this:
+
+```hcl
+## _envcommon/mysql.hcl
+
+locals {
+  environment_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))
+
+  env = local.environment_vars.locals.environment
+
+  base_source_url = "git::git@github.com:acme/infrastructure-catalog.git//modules/mysql"
+}
+
+inputs = {
+  name              = "mysql_${local.env}"
+  instance_class    = "db.t2.micro"
+  allocated_storage = 20
+  storage_type      = "standard"
+  master_username   = "admin"
+}
+```
+
+This pattern was previously used to create "Don't Repeat Yourself" (DRY) configurations with Terragrunt. However, this pattern is no longer recommended as a best practice, and is no longer needed to create DRY configurations with Terragrunt.
+
+Instead, you can create a `terragrunt.hcl` file in your `infrastructure-catalog` repository that looks like this:
+
+```hcl
+## units/mysql/terragrunt.hcl
+
+include "root" {
+  path = find_in_parent_folders("root.hcl")
+}
+
+terraform {
+  source = "git::git@github.com:acme/infrastructure-catalog.git//modules/mysql?ref=${values.version}"
+}
+
+inputs = {
+  ## Required inputs
+  name              = values.name
+  instance_class    = values.instance_class
+  allocated_storage = values.allocated_storage
+  storage_type      = values.storage_type
+  master_username   = values.master_username
+  master_password   = values.master_password
+
+  ## Optional inputs
+  skip_final_snapshot = try(values.skip_final_snapshot, null)
+  engine_version      = try(values.engine_version, null)
+}
+```
+
+Then reference that `terragrunt.hcl` file in your `terragrunt.stack.hcl` files, like so:
+
+```hcl
+## non-prod/us-east-1/terragrunt.stack.hcl
+
+unit "mysql" {
+  source = "git::git@github.com:acme/infrastructure-catalog.git//units/mysql"
+  path   = "mysql"
+
+  ## As discussed above, this prevents Terragrunt from automatically generating the units into a `.terragrunt-stack` directory.
+  no_dot_terragrunt_stack = true
+
+  values = {
+    version = "v0.8.0"
+    name = "mysql_dev"
+    instance_class = "db.t2.micro"
+    allocated_storage = 20
+    storage_type = "standard"
+    master_username = "admin"
+  }
+}
+```
+
+Now, all your unit configurations can be found directly in the `terragrunt.hcl` file in the `infrastructure-catalog` repository, without having to bounce around between different included or referenced files, and you have an explicit interface for the values that can be set externally, via the `values` attribute.
+
+Different environments can pin different versions of the unit, and that allows for easy atomic updates (and rollbacks) of both OpenTofu/Terraform module versions and Terragrunt unit configurations if needed.
+
+### Step 6: Update your CI/CD pipeline
+
+Chances are, if you're currently performing Terragrunt updates via a CI/CD pipeline (and you aren't using [Gruntwork Pipelines](https://www.gruntwork.io/platform/pipelines)), your CI/CD pipeline doesn't yet have integration with Terragrunt Stacks.
+
+There are a few options for how to proceed here.
+
+<!--
+    Note to maintainers: This is basically ready to go, but we're not announcing it yet, so we'll leave this commented out for now.
+
+1. You can start using [Gruntwork Pipelines](https://www.gruntwork.io/platform/pipelines).
+
+   This is a little self-serving, as it's a paid product that we offer at Gruntwork, but we are constantly refining it as the best way to manage IaC at scale via GitOps, and we've built first-class support for Terragrunt Stacks into it, so it's the most straightforward way to get Terragrunt Stacks support out of the box.
+-->
+
+1. You can simply commit the generated `.terragrunt-stack` directories to your repository.
+
+   This is the easiest option when managing CI/CD yourself, but it also means that you won't gain some of the benefits that come from using Terragrunt Stacks. When getting started, however, this is a good way to avoid the additional technical debt that comes from having to update your CI/CD pipeline to support Terragrunt Stacks, while learning how to use `terragrunt.stack.hcl` files, and reorganizing your infrastructure configurations.
+
+   To do this, remove the `.terragrunt-stack` entry from your `.gitignore` file, and commit the changes to your repository. You can then manually run the `terragrunt stack generate` command to generate the `.terragrunt-stack` directories on demand, and commit them to your repository, allowing your CI/CD pipeline to completely ignore the fact that you're using Terragrunt Stacks. The units generated by the `terragrunt stack generate` command are completely compatible with units that you can author manually, so you don't have to worry about any incompatibility issues that might arise from this approach.
+
+1. You can configure your CI/CD pipeline to run the `terragrunt stack generate` command whenever your pipeline runs, and leverage the generated `.terragrunt-stack` directories in your pipeline.
+
+   Depending on the complexity of your CI/CD pipeline, this might be as simple as performing the following:
+
+   ```bash
+   terragrunt stack generate
+   terragrunt run --all plan/apply --non-interactive
+   ```
+
+   This doesn't account for destroys or the reduction of blast radius for changes by carefully inspecting Git diffs, but it's a good start for users that don't have CI/CD pipelines that are too complex.
+
+   There is an open RFC in GitHub ([Filter Flag](https://github.com/gruntwork-io/terragrunt/issues/4060)) that would allow for this kind of complex filtering out of the box with Terragrunt, but at the moment, it's still an open RFC.
diff --git a/go.mod b/go.mod
index 08a4e6d8..ff912c5e 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,7 @@ require (
 	github.com/charmbracelet/lipgloss v1.1.0
 	github.com/creack/pty v1.1.24
 	github.com/fatih/structs v1.1.0
-	github.com/getsops/sops/v3 v3.9.4
+	github.com/getsops/sops/v3 v3.10.1
 	github.com/gitsight/go-vcsurl v1.0.1
 	github.com/go-errors/errors v1.5.1
 	github.com/gofrs/flock v0.12.1
@@ -72,13 +72,13 @@ require (
 	go.opentelemetry.io/otel/sdk/metric v1.35.0
 	go.opentelemetry.io/otel/trace v1.35.0
 	golang.org/x/mod v0.24.0
-	golang.org/x/oauth2 v0.28.0
+	golang.org/x/oauth2 v0.29.0
 	golang.org/x/sync v0.12.0
-	golang.org/x/sys v0.31.0
+	golang.org/x/sys v0.32.0
 	golang.org/x/term v0.30.0
 	golang.org/x/text v0.23.0
-	google.golang.org/api v0.227.0
-	google.golang.org/grpc v1.71.0
+	google.golang.org/api v0.228.0
+	google.golang.org/grpc v1.71.1
 	google.golang.org/protobuf v1.36.6
 	gopkg.in/ini.v1 v1.67.0
 )
@@ -93,24 +93,24 @@ require (
 	atomicgo.dev/cursor v0.2.0 // indirect
 	atomicgo.dev/keyboard v0.2.9 // indirect
 	atomicgo.dev/schedule v0.1.0 // indirect
-	cel.dev/expr v0.22.0 // indirect
+	cel.dev/expr v0.22.1 // indirect
 	cloud.google.com/go v0.120.0 // indirect
 	cloud.google.com/go/auth v0.15.0 // indirect
 	cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
 	cloud.google.com/go/compute/metadata v0.6.0 // indirect
 	cloud.google.com/go/iam v1.4.2 // indirect
-	cloud.google.com/go/kms v1.21.0 // indirect
-	cloud.google.com/go/longrunning v0.6.5 // indirect
+	cloud.google.com/go/kms v1.21.1 // indirect
+	cloud.google.com/go/longrunning v0.6.6 // indirect
 	cloud.google.com/go/monitoring v1.24.1 // indirect
 	filippo.io/age v1.2.1 // indirect
 	filippo.io/edwards25519 v1.1.0 // indirect
 	github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
-	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
-	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect
+	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 // indirect
+	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
-	github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.0 // indirect
-	github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0 // indirect
-	github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect
+	github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 // indirect
+	github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 // indirect
+	github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
 	github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
 	github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
 	github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
@@ -125,13 +125,13 @@ require (
 	github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
 	github.com/atotto/clipboard v0.1.4 // indirect
 	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
-	github.com/aws/aws-sdk-go-v2/config v1.29.6 // indirect
-	github.com/aws/aws-sdk-go-v2/credentials v1.17.59 // indirect
-	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 // indirect
-	github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.53 // indirect
+	github.com/aws/aws-sdk-go-v2/config v1.29.12 // indirect
+	github.com/aws/aws-sdk-go-v2/credentials v1.17.65 // indirect
+	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
+	github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.69 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect
 	github.com/aws/aws-sdk-go-v2/service/acm v1.30.6 // indirect
 	github.com/aws/aws-sdk-go-v2/service/autoscaling v1.51.0 // indirect
@@ -146,7 +146,7 @@ require (
 	github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.5 // indirect
 	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
 	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect
-	github.com/aws/aws-sdk-go-v2/service/kms v1.37.18 // indirect
+	github.com/aws/aws-sdk-go-v2/service/kms v1.38.1 // indirect
 	github.com/aws/aws-sdk-go-v2/service/lambda v1.69.0 // indirect
 	github.com/aws/aws-sdk-go-v2/service/rds v1.91.0 // indirect
 	github.com/aws/aws-sdk-go-v2/service/route53 v1.46.2 // indirect
@@ -154,9 +154,9 @@ require (
 	github.com/aws/aws-sdk-go-v2/service/sns v1.33.6 // indirect
 	github.com/aws/aws-sdk-go-v2/service/sqs v1.37.1 // indirect
 	github.com/aws/aws-sdk-go-v2/service/ssm v1.56.0 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 // indirect
-	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sso v1.25.2 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.0 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 // indirect
 	github.com/aws/smithy-go v1.22.3 // indirect
 	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
 	github.com/aymerick/douceur v0.2.0 // indirect
@@ -170,12 +170,11 @@ require (
 	github.com/charmbracelet/x/ansi v0.8.0 // indirect
 	github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
 	github.com/cloudflare/circl v1.6.0 // indirect
-	github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
+	github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect
 	github.com/containerd/console v1.0.4 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/dlclark/regexp2 v1.11.5 // indirect
-	github.com/docker/docker v27.5.1+incompatible // indirect
 	github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
 	github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
 	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
@@ -204,14 +203,14 @@ require (
 	github.com/hashicorp/errwrap v1.1.0 // indirect
 	github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
 	github.com/hashicorp/go-rootcerts v1.0.2 // indirect
-	github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 // indirect
+	github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect
 	github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
 	github.com/hashicorp/go-sockaddr v1.0.7 // indirect
 	github.com/hashicorp/go-uuid v1.0.3 // indirect
 	github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
 	github.com/hashicorp/logutils v1.0.0 // indirect
 	github.com/hashicorp/terraform-registry-address v0.2.4 // indirect
-	github.com/hashicorp/vault/api v1.15.0 // indirect
+	github.com/hashicorp/vault/api v1.16.0 // indirect
 	github.com/hashicorp/yamux v0.1.2 // indirect
 	github.com/huandu/xstrings v1.5.0 // indirect
 	github.com/inancgumus/screen v0.0.0-20190314163918-06e984b86ed3 // indirect
@@ -280,9 +279,9 @@ require (
 	golang.org/x/net v0.38.0 // indirect
 	golang.org/x/time v0.11.0 // indirect
 	golang.org/x/tools v0.29.0 // indirect
-	google.golang.org/genproto v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
+	google.golang.org/genproto v0.0.0-20250324211829-b45e905df463 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	sigs.k8s.io/yaml v1.4.0 // indirect
diff --git a/go.sum b/go.sum
index 7afebea6..0625af83 100644
--- a/go.sum
+++ b/go.sum
@@ -2,8 +2,8 @@ atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg=
 atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ=
 c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0=
 c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w=
-cel.dev/expr v0.22.0 h1:+hFFhLPmquBImfs1BiN2PZmkr5ASse2ZOuaxIs9e4R8=
-cel.dev/expr v0.22.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
+cel.dev/expr v0.22.1 h1:xoFEsNh972Yzey8N9TCPx2nDvMN7TMhQEzxLuj/iRrI=
+cel.dev/expr v0.22.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -344,8 +344,8 @@ cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4
 cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w=
 cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24=
 cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI=
-cloud.google.com/go/kms v1.21.0 h1:x3EeWKuYwdlo2HLse/876ZrKjk2L5r7Uexfm8+p6mSI=
-cloud.google.com/go/kms v1.21.0/go.mod h1:zoFXMhVVK7lQ3JC9xmhHMoQhnjEDZFoLAr5YMwzBLtk=
+cloud.google.com/go/kms v1.21.1 h1:r1Auo+jlfJSf8B7mUnVw5K0fI7jWyoUy65bV53VjKyk=
+cloud.google.com/go/kms v1.21.1/go.mod h1:s0wCyByc9LjTdCjG88toVs70U9W+cc6RKFc8zAqX7nE=
 cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
 cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
 cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE=
@@ -361,8 +361,8 @@ cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhX
 cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE=
 cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc=
 cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
-cloud.google.com/go/longrunning v0.6.5 h1:sD+t8DO8j4HKW4QfouCklg7ZC1qC4uzVZt8iz3uTW+Q=
-cloud.google.com/go/longrunning v0.6.5/go.mod h1:Et04XK+0TTLKa5IPYryKf5DkpwImy6TluQ1QTLwlKmY=
+cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw=
+cloud.google.com/go/longrunning v0.6.6/go.mod h1:hyeGJUrPHcx0u2Uu1UFSoYZLn4lkMrccJig0t4FI7yw=
 cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE=
 cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM=
 cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA=
@@ -636,20 +636,20 @@ github.com/Azure/azure-sdk-for-go v47.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
 github.com/Azure/azure-sdk-for-go v51.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/azure-sdk-for-go v52.5.0+incompatible h1:/NLBWHCnIHtZyLPc1P7WIqi4Te4CC23kIQyK3Ep/7lA=
 github.com/Azure/azure-sdk-for-go v52.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
-github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.0 h1:7rKG7UmnrxX4N53TFhkYqjc+kVUZuw0fL8I3Fh+Ld9E=
-github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.0/go.mod h1:Wjo+24QJVhhl/L7jy6w9yzFF2yDOf3cKECAa8ecf9vE=
-github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0 h1:eXnN9kaS8TiDwXjoie3hMRLuwdUBUMW9KRgOqB3mCaw=
-github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0/go.mod h1:XIpam8wumeZ5rVMuhdDQLMfIPDf1WO3IzrCRO3e3e3o=
-github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
-github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w=
+github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14=
+github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI=
+github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww=
+github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
+github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 github.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
 github.com/Azure/go-autorest/autorest v0.11.10/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
@@ -672,8 +672,8 @@ github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzS
 github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
 github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
 github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
@@ -787,20 +787,20 @@ github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38y
 github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
-github.com/aws/aws-sdk-go-v2/config v1.29.6 h1:fqgqEKK5HaZVWLQoLiC9Q+xDlSp+1LYidp6ybGE2OGg=
-github.com/aws/aws-sdk-go-v2/config v1.29.6/go.mod h1:Ft+WLODzDQmCTHDvqAH1JfC2xxbZ0MxpZAcJqmE1LTQ=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.59 h1:9btwmrt//Q6JcSdgJOLI98sdr5p7tssS9yAsGe8aKP4=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.59/go.mod h1:NM8fM6ovI3zak23UISdWidyZuI1ghNe2xjzUZAyT+08=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 h1:KwsodFKVQTlI5EyhRSugALzsV6mG/SGrdjlMXSZSdso=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28/go.mod h1:EY3APf9MzygVhKuPXAc5H+MkGb8k/DOSQjWS0LgkKqI=
-github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.53 h1:3jYpOndmkKtmlPOhMNIV7Q92GD61x/KNjmxUcB95btw=
-github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.53/go.mod h1:+s7tPUl4uy7FMpT5qnjkY5YJNuKU2HZL6trkYxQNtb4=
+github.com/aws/aws-sdk-go-v2/config v1.29.12 h1:Y/2a+jLPrPbHpFkpAAYkVEtJmxORlXoo5k2g1fa2sUo=
+github.com/aws/aws-sdk-go-v2/config v1.29.12/go.mod h1:xse1YTjmORlb/6fhkWi8qJh3cvZi4JoVNhc+NbJt4kI=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.65 h1:q+nV2yYegofO/SUXruT+pn4KxkxmaQ++1B/QedcKBFM=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.65/go.mod h1:4zyjAuGOdikpNYiSGpsGz8hLGmUzlY8pc8r9QQ/RXYQ=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.69 h1:6VFPH/Zi9xYFMJKPQOX5URYkQoXRWeJ7V/7Y6ZDYoms=
+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.69/go.mod h1:GJj8mmO6YT6EqgduWocwhMoxTLFitkhIrK+owzrYL2I=
 github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
 github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
 github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM=
 github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
 github.com/aws/aws-sdk-go-v2/service/acm v1.30.6 h1:fDg0RlN30Xf/yYzEUL/WXqhmgFsjVb/I3230oCfyI5w=
@@ -829,8 +829,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
 github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg=
 github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA=
-github.com/aws/aws-sdk-go-v2/service/kms v1.37.18 h1:pi9M/9n1PLayBXjia7LfwgXwcpFdFO7Q2cqKOZa1ZmM=
-github.com/aws/aws-sdk-go-v2/service/kms v1.37.18/go.mod h1:vZXvmzfhdsPj/axc8+qk/2fSCP4hGyaZ1MAduWEHAxM=
+github.com/aws/aws-sdk-go-v2/service/kms v1.38.1 h1:tecq7+mAav5byF+Mr+iONJnCBf4B4gon8RSp4BrweSc=
+github.com/aws/aws-sdk-go-v2/service/kms v1.38.1/go.mod h1:cQn6tAF77Di6m4huxovNM7NVAozWTZLsDRp9t8Z/WYk=
 github.com/aws/aws-sdk-go-v2/service/lambda v1.69.0 h1:BXt75frE/FYtAmEDBJRBa2HexOw+oAZWZl6QknZEFgg=
 github.com/aws/aws-sdk-go-v2/service/lambda v1.69.0/go.mod h1:guz2K3x4FKSdDaoeB+TPVgJNU9oj2gftbp5cR8ela1A=
 github.com/aws/aws-sdk-go-v2/service/rds v1.91.0 h1:eqHz3Uih+gb0vLE5Cc4Xf733vOxsxDp6GFUUVQU4d7w=
@@ -847,12 +847,12 @@ github.com/aws/aws-sdk-go-v2/service/sqs v1.37.1 h1:39WvSrVq9DD6UHkD+fx5x19P5KpR
 github.com/aws/aws-sdk-go-v2/service/sqs v1.37.1/go.mod h1:3gwPzC9LER/BTQdQZ3r6dUktb1rSjABF1D3Sr6nS7VU=
 github.com/aws/aws-sdk-go-v2/service/ssm v1.56.0 h1:mADKqoZaodipGgiZfuAjtlcr4IVBtXPZKVjkzUZCCYM=
 github.com/aws/aws-sdk-go-v2/service/ssm v1.56.0/go.mod h1:l9qF25TzH95FhcIak6e4vt79KE4I7M2Nf59eMUVjj6c=
-github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 h1:/eE3DogBjYlvlbhd2ssWyeuovWunHLxfgw3s/OJa4GQ=
-github.com/aws/aws-sdk-go-v2/service/sso v1.24.15/go.mod h1:2PCJYpi7EKeA5SkStAmZlF6fi0uUABuhtF8ILHjGc3Y=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 h1:M/zwXiL2iXUrHputuXgmO94TVNmcenPHxgLXLutodKE=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14/go.mod h1:RVwIw3y/IqxC2YEXSIkAzRDdEU1iRabDPaYjpGCbCGQ=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 h1:TzeR06UCMUq+KA3bDkujxK1GVGy+G8qQN/QVYzGLkQE=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.14/go.mod h1:dspXf/oYWGWo6DEvj98wpaTeqt5+DMidZD0A9BYTizc=
+github.com/aws/aws-sdk-go-v2/service/sso v1.25.2 h1:pdgODsAhGo4dvzC3JAG5Ce0PX8kWXrTZGx+jxADD+5E=
+github.com/aws/aws-sdk-go-v2/service/sso v1.25.2/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.0 h1:90uX0veLKcdHVfvxhkWUQSCi5VabtwMLFutYiRke4oo=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.0/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
 github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
 github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
 github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
@@ -926,8 +926,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
 github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk=
-github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
+github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k=
+github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
 github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
 github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
 github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
@@ -956,10 +956,10 @@ github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQ
 github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
 github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
 github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
-github.com/docker/cli v27.4.1+incompatible h1:VzPiUlRJ/xh+otB75gva3r05isHMo5wXDfPRi5/b4hI=
-github.com/docker/cli v27.4.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
-github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8=
-github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A=
+github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
+github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
 github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -1016,8 +1016,8 @@ github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3G
 github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
 github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e h1:y/1nzrdF+RPds4lfoEpNhjfmzlgZtPqyO3jMzrqDQws=
 github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e/go.mod h1:awFzISqLJoZLm+i9QQ4SgMNHDqljH6jWV0B36V5MrUM=
-github.com/getsops/sops/v3 v3.9.4 h1:f5JQRkXrK1SWM/D7HD8gCFLrUPZIEP+XUHs0byaNaqk=
-github.com/getsops/sops/v3 v3.9.4/go.mod h1:zI9m7ji9gsegGA/4pWMT3EGkDdbeTiafgL9mAxz1weE=
+github.com/getsops/sops/v3 v3.10.1 h1:5xh+9yTN2sa0iAKlBWyBqu8SEzRsrlbsjdidDoHwY90=
+github.com/getsops/sops/v3 v3.10.1/go.mod h1:ywVxyFHOiHoJN9uXLO3aEj71OoRfaxlkOzR2wjduZLo=
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gitsight/go-vcsurl v1.0.1 h1:wkijKsbVg9R2IBP97U7wOANeIW9WJJKkBwS9XqllzWo=
@@ -1274,8 +1274,8 @@ github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5O
 github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
 github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
 github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
-github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 h1:FW0YttEnUNDJ2WL9XcrrfteS1xW8u+sh4ggM8pN5isQ=
-github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0=
+github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM=
+github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0=
 github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
 github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
 github.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8=
@@ -1318,8 +1318,8 @@ github.com/hashicorp/terraform-registry-address v0.2.4/go.mod h1:tUNYTVyCtU4OIGX
 github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg=
 github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
 github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
-github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA=
-github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8=
+github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4=
+github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA=
 github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
 github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
@@ -1501,8 +1501,8 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
 github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
 github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
 github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
-github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
-github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
+github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
+github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
@@ -1532,10 +1532,10 @@ github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuB
 github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
-github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
-github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
-github.com/opencontainers/runc v1.2.3 h1:fxE7amCzfZflJO2lHXf4y/y8M1BoAqp+FVmG19oYB80=
-github.com/opencontainers/runc v1.2.3/go.mod h1:nSxcWUydXrsBZVYNSkTjoQ/N6rcyTtn+1SD5D4+kRIM=
+github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
+github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
+github.com/opencontainers/runc v1.2.6 h1:P7Hqg40bsMvQGCS4S7DJYhUZOISMLJOB2iGX5COWiPk=
+github.com/opencontainers/runc v1.2.6/go.mod h1:dOQeFo29xZKBNeRBI0B19mJtfHv68YgCTh1X+YphA+4=
 github.com/ory/dockertest/v3 v3.11.0 h1:OiHcxKAvSDUwsEVh2BjxQQc/5EHz9n0va9awCtNGuyA=
 github.com/ory/dockertest/v3 v3.11.0/go.mod h1:VIPxS1gwT9NpPOrfD3rACs8Y9Z7yhzO4SB194iUDnUI=
 github.com/owenrumney/go-sarif v1.1.1 h1:QNObu6YX1igyFKhdzd7vgzmw7XsWN3/6NMGuDzBgXmE=
@@ -1980,8 +1980,8 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec
 golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
 golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
 golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
-golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
-golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
+golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
+golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -2106,8 +2106,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
-golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
+golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -2303,8 +2303,8 @@ google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/
 google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI=
 google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
 google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
-google.golang.org/api v0.227.0 h1:QvIHF9IuyG6d6ReE+BNd11kIB8hZvjN8Z5xY5t21zYc=
-google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ=
+google.golang.org/api v0.228.0 h1:X2DJ/uoWGnY5obVjewbp8icSL5U4FzuCfy9OjbLSnLs=
+google.golang.org/api v0.228.0/go.mod h1:wNvRS1Pbe8r4+IfBIniV8fwCpGwTrYa+kMUDiC5z5a4=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -2445,12 +2445,12 @@ google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOl
 google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
 google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
 google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
-google.golang.org/genproto v0.0.0-20250313205543-e70fdf4c4cb4 h1:kCjWYliqPA8g5z87mbjnf/cdgQqMzBfp9xYre5qKu2A=
-google.golang.org/genproto v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:SqIx1NV9hcvqdLHo7uNZDS5lrUJybQ3evo3+z/WBfA0=
-google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 h1:IFnXJq3UPB3oBREOodn1v1aGQeZYQclEmvWRMN0PSsY=
-google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:c8q6Z6OCqnfVIqUFJkCzKcrj8eCvUrz+K4KRzSTuANg=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
+google.golang.org/genproto v0.0.0-20250324211829-b45e905df463 h1:qEFnJI6AnfZk0NNe8YTyXQh5i//Zxi4gBHwRgp76qpw=
+google.golang.org/genproto v0.0.0-20250324211829-b45e905df463/go.mod h1:SqIx1NV9hcvqdLHo7uNZDS5lrUJybQ3evo3+z/WBfA0=
+google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM=
+google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
 google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -2493,8 +2493,8 @@ google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v
 google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
 google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
 google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
-google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
-google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
+google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
+google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
diff --git a/internal/discovery/discovery.go b/internal/discovery/discovery.go
index 163a0570..79076d80 100644
--- a/internal/discovery/discovery.go
+++ b/internal/discovery/discovery.go
@@ -15,6 +15,7 @@ import (
 	"github.com/gruntwork-io/terragrunt/options"
 	"github.com/gruntwork-io/terragrunt/pkg/log"
 	"github.com/gruntwork-io/terragrunt/pkg/log/format"
+	"github.com/zclconf/go-cty/cty"
 )
 
 const (
@@ -30,29 +31,38 @@ type ConfigType string
 // Sort is the sort order of the discovered configurations.
 type Sort string
 
-// Exclude is the exclude configuration for a discovered configuration.
-type Exclude struct {
-	If                  string
-	Actions             []string
-	ExcludeDependencies bool
-}
-
 // DiscoveredConfig represents a discovered Terragrunt configuration.
 type DiscoveredConfig struct {
+	Parsed           *config.TerragruntConfig
+	DiscoveryContext *DiscoveryContext
+
 	Type ConfigType
 	Path string
 
 	Dependencies DiscoveredConfigs
-	Exclude      Exclude
 
 	External bool
 }
 
+// DiscoveryContext is the context in which
+// a DiscoveredConfig was discovered.
+//
+// It's useful to know this information,
+// because it can help us determine how the
+// DiscoveredConfig should be processed later.
+type DiscoveryContext struct {
+	Cmd  string
+	Args []string
+}
+
 // DiscoveredConfigs is a list of discovered Terragrunt configurations.
 type DiscoveredConfigs []*DiscoveredConfig
 
 // Discovery is the configuration for a Terragrunt discovery.
 type Discovery struct {
+	// discoveryContext is the context in which the discovery is happening.
+	discoveryContext *DiscoveryContext
+
 	// workingDir is the directory to search for Terragrunt configurations.
 	workingDir string
 
@@ -68,9 +78,15 @@ type Discovery struct {
 	// hidden determines whether to detect configurations in hidden directories.
 	hidden bool
 
+	// requiresParse is true when the discovery requires parsing Terragrunt configurations.
+	requiresParse bool
+
 	// discoverDependencies determines whether to discover dependencies.
 	discoverDependencies bool
 
+	// parseExclude determines whether to parse exclude configurations.
+	parseExclude bool
+
 	// discoverExternalDependencies determines whether to discover external dependencies.
 	discoverExternalDependencies bool
 
@@ -113,6 +129,8 @@ func (d *Discovery) WithSort(sort Sort) *Discovery {
 func (d *Discovery) WithDiscoverDependencies() *Discovery {
 	d.discoverDependencies = true
 
+	d.requiresParse = true
+
 	if d.maxDependencyDepth == 0 {
 		d.maxDependencyDepth = 1000
 	}
@@ -120,6 +138,15 @@ func (d *Discovery) WithDiscoverDependencies() *Discovery {
 	return d
 }
 
+// WithParseExclude sets the ParseExclude flag to true.
+func (d *Discovery) WithParseExclude() *Discovery {
+	d.parseExclude = 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
@@ -141,6 +168,13 @@ func (d *Discovery) WithSuppressParseErrors() *Discovery {
 	return d
 }
 
+// WithDiscoveryContext sets the DiscoveryContext flag to the given context.
+func (d *Discovery) WithDiscoveryContext(discoveryContext *DiscoveryContext) *Discovery {
+	d.discoveryContext = discoveryContext
+
+	return d
+}
+
 // String returns a string representation of a DiscoveredConfig.
 func (c *DiscoveredConfig) String() string {
 	return c.Path
@@ -162,6 +196,52 @@ func (c *DiscoveredConfig) ContainsDependencyInAncestry(path string) bool {
 	return false
 }
 
+// Parse parses the discovered configurations.
+func (c *DiscoveredConfig) Parse(ctx context.Context, opts *options.TerragruntOptions, suppressParseErrors bool) error {
+	parseOpts := opts.Clone()
+	parseOpts.WorkingDir = c.Path
+
+	// Suppress logging to avoid cluttering the output.
+	parseOpts.Writer = io.Discard
+	parseOpts.ErrWriter = io.Discard
+	parseOpts.Logger = log.New(
+		log.WithOutput(io.Discard),
+		log.WithFormatter(format.NewFormatter(format.NewPrettyFormatPlaceholders())),
+	)
+	parseOpts.SkipOutput = true
+
+	filename := config.DefaultTerragruntConfigPath
+
+	if c.Type == ConfigTypeStack {
+		filename = config.DefaultStackFile
+	}
+
+	parseOpts.TerragruntConfigPath = filepath.Join(parseOpts.WorkingDir, filename)
+
+	parsingCtx := config.NewParsingContext(ctx, parseOpts).WithDecodeList(
+		config.DependenciesBlock,
+		config.DependencyBlock,
+		config.FeatureFlagsBlock,
+		config.ExcludeBlock,
+	)
+
+	//nolint: contextcheck
+	cfg, err := config.ParseConfigFile(parsingCtx, parseOpts.TerragruntConfigPath, nil)
+	if err != nil {
+		if !suppressParseErrors || cfg == nil {
+			opts.Logger.Debugf("Unrecoverable parse error for %s: %s", parseOpts.TerragruntConfigPath, err)
+
+			return errors.New(err)
+		}
+
+		opts.Logger.Debugf("Suppressing parse error for %s: %s", parseOpts.TerragruntConfigPath, err)
+	}
+
+	c.Parsed = cfg
+
+	return nil
+}
+
 // isInHiddenDirectory returns true if the path is in a hidden directory.
 func (d *Discovery) isInHiddenDirectory(path string) bool {
 	for _, hiddenDir := range d.hiddenDirMemo {
@@ -205,15 +285,27 @@ func (d *Discovery) Discover(ctx context.Context, opts *options.TerragruntOption
 
 		switch filepath.Base(path) {
 		case config.DefaultTerragruntConfigPath:
-			cfgs = append(cfgs, &DiscoveredConfig{
+			cfg := &DiscoveredConfig{
 				Type: ConfigTypeUnit,
 				Path: filepath.Dir(path),
-			})
+			}
+
+			if d.discoveryContext != nil {
+				cfg.DiscoveryContext = d.discoveryContext
+			}
+
+			cfgs = append(cfgs, cfg)
 		case config.DefaultStackFile:
-			cfgs = append(cfgs, &DiscoveredConfig{
+			cfg := &DiscoveredConfig{
 				Type: ConfigTypeStack,
 				Path: filepath.Dir(path),
-			})
+			}
+
+			if d.discoveryContext != nil {
+				cfg.DiscoveryContext = d.discoveryContext
+			}
+
+			cfgs = append(cfgs, cfg)
 		}
 
 		return nil
@@ -223,9 +315,27 @@ func (d *Discovery) Discover(ctx context.Context, opts *options.TerragruntOption
 		return cfgs, errors.New(err)
 	}
 
+	errs := []error{}
+
+	// We do an initial parse loop if we know we need to parse configurations,
+	// as we might need to parse configurations for multiple reasons.
+	// e.g. dependencies, exclude, etc.
+	if d.requiresParse {
+		for _, cfg := range cfgs {
+			err := cfg.Parse(ctx, opts, d.suppressParseErrors)
+			if err != nil {
+				errs = append(errs, errors.New(err))
+			}
+		}
+	}
+
 	if d.discoverDependencies {
 		dependencyDiscovery := NewDependencyDiscovery(cfgs, d.maxDependencyDepth)
 
+		if d.discoveryContext != nil {
+			dependencyDiscovery = dependencyDiscovery.WithDiscoveryContext(d.discoveryContext)
+		}
+
 		if d.discoverExternalDependencies {
 			dependencyDiscovery = dependencyDiscovery.WithDiscoverExternalDependencies()
 		}
@@ -234,8 +344,6 @@ func (d *Discovery) Discover(ctx context.Context, opts *options.TerragruntOption
 			dependencyDiscovery = dependencyDiscovery.WithSuppressParseErrors()
 		}
 
-		errs := []error{}
-
 		err := dependencyDiscovery.DiscoverAllDependencies(ctx, opts)
 		if err != nil {
 			opts.Logger.Warnf("Parsing errors where encountered while discovering dependencies. They were suppressed, and can be found in the debug logs.")
@@ -266,6 +374,7 @@ func (d *Discovery) Discover(ctx context.Context, opts *options.TerragruntOption
 
 // DependencyDiscovery is the configuration for a DependencyDiscovery.
 type DependencyDiscovery struct {
+	discoveryContext    *DiscoveryContext
 	cfgs                DiscoveredConfigs
 	depthRemaining      int
 	discoverExternal    bool
@@ -296,6 +405,12 @@ func (d *DependencyDiscovery) WithDiscoverExternalDependencies() *DependencyDisc
 	return d
 }
 
+func (d *DependencyDiscovery) WithDiscoveryContext(discoveryContext *DiscoveryContext) *DependencyDiscovery {
+	d.discoveryContext = discoveryContext
+
+	return d
+}
+
 func (d *DependencyDiscovery) DiscoverAllDependencies(ctx context.Context, opts *options.TerragruntOptions) error {
 	errs := []error{}
 
@@ -328,60 +443,40 @@ func (d *DependencyDiscovery) DiscoverDependencies(ctx context.Context, opts *op
 		return nil
 	}
 
-	parseOpts := opts.Clone()
-	parseOpts.WorkingDir = dCfg.Path
-
-	// Suppress logging to avoid cluttering the output.
-	parseOpts.Writer = io.Discard
-	parseOpts.ErrWriter = io.Discard
-	parseOpts.Logger = log.New(
-		log.WithOutput(io.Discard),
-		log.WithFormatter(format.NewFormatter(format.NewPrettyFormatPlaceholders())),
-	)
-
-	// We can assume this is a unit config, because we already filtered out
-	// stack configs above.
-	parseOpts.TerragruntConfigPath = filepath.Join(parseOpts.WorkingDir, config.DefaultTerragruntConfigPath)
-
-	parsingCtx := config.NewParsingContext(ctx, parseOpts).WithDecodeList(
-		config.DependenciesBlock,
-		config.DependencyBlock,
-		config.FeatureFlagsBlock,
-		config.ExcludeBlock,
-	)
-
-	//nolint: contextcheck
-	cfg, err := config.PartialParseConfigFile(parsingCtx, parseOpts.TerragruntConfigPath, nil)
-	if err != nil {
-		if !d.suppressParseErrors || cfg == nil {
-			opts.Logger.Debugf("Unrecoverable parse error for %s: %s", parseOpts.TerragruntConfigPath, err)
-
+	// This should only happen if we're discovering an ancestor dependency.
+	if dCfg.Parsed == nil {
+		err := dCfg.Parse(ctx, opts, d.suppressParseErrors)
+		if err != nil {
 			return errors.New(err)
 		}
-
-		opts.Logger.Debugf("Suppressing parse error for %s: %s", parseOpts.TerragruntConfigPath, err)
 	}
 
-	dependencyBlocks := cfg.TerragruntDependencies
+	dependencyBlocks := dCfg.Parsed.TerragruntDependencies
 
 	depPaths := make([]string, 0, len(dependencyBlocks))
 
 	errs := []error{}
 
 	for _, dependency := range dependencyBlocks {
+		if dependency.ConfigPath.Type() != cty.String {
+			errs = append(errs, errors.New("dependency config path is not a string"))
+
+			continue
+		}
+
 		depPath := dependency.ConfigPath.AsString()
 
 		if !filepath.IsAbs(depPath) {
-			depPath = filepath.Join(parseOpts.WorkingDir, depPath)
+			depPath = filepath.Join(dCfg.Path, depPath)
 		}
 
 		depPaths = append(depPaths, depPath)
 	}
 
-	if cfg.Dependencies != nil {
-		for _, dependency := range cfg.Dependencies.Paths {
+	if dCfg.Dependencies != nil {
+		for _, dependency := range dCfg.Parsed.Dependencies.Paths {
 			if !filepath.IsAbs(dependency) {
-				dependency = filepath.Join(parseOpts.WorkingDir, dependency)
+				dependency = filepath.Join(dCfg.Path, dependency)
 			}
 
 			depPaths = append(depPaths, dependency)
@@ -422,6 +517,10 @@ func (d *DependencyDiscovery) DiscoverDependencies(ctx context.Context, opts *op
 				External: true,
 			}
 
+			if d.discoveryContext != nil {
+				ext.DiscoveryContext = d.discoveryContext
+			}
+
 			dCfg.Dependencies = append(dCfg.Dependencies, ext)
 
 			if d.discoverExternal {
diff --git a/internal/queue/queue.go b/internal/queue/queue.go
index 2fcb7528..8be9f526 100644
--- a/internal/queue/queue.go
+++ b/internal/queue/queue.go
@@ -23,118 +23,227 @@
 package queue
 
 import (
+	"errors"
+	"slices"
 	"sort"
 
 	"github.com/gruntwork-io/terragrunt/internal/discovery"
-	"github.com/gruntwork-io/terragrunt/internal/errors"
 )
 
+type Entry struct {
+	Config *discovery.DiscoveredConfig
+	Status Status
+}
+
+type Status byte
+
+const (
+	StatusPending Status = iota
+	StatusBlocked
+	StatusUnsorted
+	StatusReady
+)
+
+// UpdateBlocked updates the status of the entry to blocked, if it is blocked.
+// An entry is blocked if:
+//  1. It is an "up" command (none of destroy, apply -destroy or plan -destroy)
+//     and it has dependencies that are not ready.
+//  2. It is a "down" command (destroy, apply -destroy or plan -destroy)
+//     and it has dependents that are not ready.
+//
+// If the entry isn't blocked, then it is marked as unsorted, and is ready to be sorted.
+func (e *Entry) UpdateBlocked(entries Entries) {
+	// If the entry is already ready, we can skip the rest of the logic.
+	if e.Status == StatusReady {
+		return
+	}
+
+	if e.IsUp() {
+		for _, dep := range e.Config.Dependencies {
+			depEntry := entries.Entry(dep)
+			if depEntry == nil {
+				continue
+			}
+
+			if !depEntry.IsUp() {
+				continue
+			}
+
+			if depEntry.Status != StatusReady {
+				e.Status = StatusBlocked
+				return
+			}
+		}
+
+		e.Status = StatusUnsorted
+
+		return
+	}
+
+	// If the entry is a "down" command, we need to check if all of its dependents are ready.
+	for _, qEntry := range entries {
+		if qEntry.Config.Dependencies == nil {
+			continue
+		}
+
+		if !slices.Contains(qEntry.Config.Dependencies, e.Config) {
+			continue
+		}
+
+		if qEntry.IsUp() {
+			continue
+		}
+
+		if qEntry.Status != StatusReady {
+			e.Status = StatusBlocked
+			return
+		}
+	}
+
+	e.Status = StatusUnsorted
+}
+
+// IsUp returns true if the entry is an "up" command.
+func (e *Entry) IsUp() bool {
+	// If we don't have a discovery context,
+	// we should assume the command is an "up" command.
+	if e.Config.DiscoveryContext == nil {
+		return true
+	}
+
+	if e.Config.DiscoveryContext.Cmd == "destroy" {
+		return false
+	}
+
+	if e.Config.DiscoveryContext.Cmd == "apply" && slices.Contains(e.Config.DiscoveryContext.Args, "-destroy") {
+		return false
+	}
+
+	if e.Config.DiscoveryContext.Cmd == "plan" && slices.Contains(e.Config.DiscoveryContext.Args, "-destroy") {
+		return false
+	}
+
+	return true
+}
+
 type Queue struct {
-	entries discovery.DiscoveredConfigs
+	Entries Entries
+}
+
+type Entries []*Entry
+
+// Entry returns a given entry from the queue.
+func (e Entries) Entry(cfg *discovery.DiscoveredConfig) *Entry {
+	for _, entry := range e {
+		if entry.Config.Path == cfg.Path {
+			return entry
+		}
+	}
+
+	return nil
 }
 
-// Entries returns the queue entries. Used for testing.
-func (q *Queue) Entries() []*discovery.DiscoveredConfig {
-	return q.entries
+// Configs returns the queue configs.
+func (q *Queue) Configs() discovery.DiscoveredConfigs {
+	result := make(discovery.DiscoveredConfigs, 0, len(q.Entries))
+	for _, entry := range q.Entries {
+		result = append(result, entry.Config)
+	}
+
+	return result
 }
 
 // NewQueue creates a new queue from a list of discovered configurations.
 // The queue is populated with the correct Terragrunt run order.
-// Dependencies are guaranteed to come before their dependents.
-// Items with the same dependencies are sorted alphabetically.
-// Passing dependencies that that haven't been checked for cycles is unsafe.
+//
+// Discovered configurations will be sorted based on two criteria:
+//
+//  1. The discovery context of the configuration:
+//     - If the configuration is for an "up" command (none of destroy, apply -destroy or plan -destroy),
+//     it will be inserted at the front of the queue, before its dependencies.
+//     - Otherwise, it is considered a "down" command, and will be inserted at the back of the queue,
+//     after its dependents.
+//
+//  2. The name of the configuration. Configurations of the same "level" are sorted alphabetically.
+//
+// Passing configurations that haven't been checked for cycles in their dependency graph is unsafe.
 // If any cycles are present, the queue construction will halt after N
 // iterations, where N is the number of discovered configs, and throw an error.
 func NewQueue(discovered discovery.DiscoveredConfigs) (*Queue, error) {
 	if len(discovered) == 0 {
 		return &Queue{
-			entries: discovered,
+			Entries: Entries{},
 		}, nil
 	}
 
-	// Create a map for O(1) lookups of configs by path
-	configMap := make(map[string]*discovery.DiscoveredConfig, len(discovered))
+	// First, we need to take all the discovered configs
+	// and assign them a status of pending.
+	entries := make(Entries, 0, len(discovered))
+
 	for _, cfg := range discovered {
-		configMap[cfg.Path] = cfg
+		entries = append(entries, &Entry{
+			Config: cfg,
+			Status: StatusPending,
+		})
 	}
 
-	// Track if a given config has been processed
-	visited := make(map[string]bool, len(discovered))
-
-	// result will store configs in dependency order
-	result := make(discovery.DiscoveredConfigs, 0, len(discovered))
-
-	// depthBudget is initially the maximum dependency depth of the queue
-	// Given that a cycle-free graph has a maximum depth of N, we can
-	// use the length of the discovered configs as a safe upper bound.
-	depthBudget := len(discovered)
+	q := &Queue{
+		Entries: entries,
+	}
 
-	// Process nodes level by level, with deterministic ordering within each level
-	var processLevel func(configs discovery.DiscoveredConfigs) error
-	processLevel = func(configs discovery.DiscoveredConfigs) error {
-		// We need to check to see if we've reached
-		// the maximum allowed iterations.
-		if depthBudget < 0 {
-			return errors.New("cycle detected during queue construction")
+	// readyPending returns the index of the first pending entry if there is one,
+	// or -1 if there are no pending entries.
+	readyPending := func(entries Entries) int {
+		// Next, we need to iterate through the entries
+		// and check if any of them are blocked.
+		for _, entry := range entries {
+			entry.UpdateBlocked(entries)
 		}
 
-		depthBudget--
-
-		levelNodes := make([]*discovery.DiscoveredConfig, 0, len(configs))
-
-		for _, cfg := range configs {
-			if visited[cfg.Path] {
-				continue
+		// Next, we need to sort the entries by status and path.
+		sort.SliceStable(entries, func(i, j int) bool {
+			if entries[i].Status > entries[j].Status {
+				return true
 			}
 
-			hasUnprocessedDeps := false
+			if entries[i].Status == StatusUnsorted && entries[j].Status == StatusUnsorted {
+				return entries[i].Config.Path < entries[j].Config.Path
+			}
 
-			// Only consider dependencies that exist in our discovered configs
-			for _, dep := range cfg.Dependencies {
-				if _, exists := configMap[dep.Path]; !exists {
-					continue // Skip dependencies that don't exist in our discovered configs
-				}
+			return false
+		})
 
-				if !visited[dep.Path] {
-					hasUnprocessedDeps = true
-					break
-				}
+		// Now, we can mark all unsorted entries as ready,
+		// and check if all entries are ready.
+		for idx, entry := range entries {
+			if entry.Status == StatusUnsorted {
+				entry.Status = StatusReady
 			}
 
-			if !hasUnprocessedDeps {
-				levelNodes = append(levelNodes, cfg)
+			if entry.Status != StatusReady {
+				return idx
 			}
 		}
 
-		// Sort nodes by path for deterministic ordering within each level
-		sort.SliceStable(levelNodes, func(i, j int) bool {
-			return levelNodes[i].Path < levelNodes[j].Path
-		})
+		return -1
+	}
 
-		// Add all nodes at this level to result
-		for _, node := range levelNodes {
-			visited[node.Path] = true
+	// We need to iterate through the entries until all entries are ready.
+	// We can use the length of the entries as a safe upper bound for the number of iterations,
+	// because a cycle-free graph has a maximum depth of N, where N is the number of discovered configs.
+	maxIterations := len(entries)
 
-			result = append(result, node)
-		}
+	// We keep track of the index of the first pending entry
+	// to save us from iterating through the entire list of entries
+	// on each iteration.
+	firstPending := 0
 
-		// If every node has been visited, we're done
-		if len(visited) == len(discovered) {
-			return nil
+	for range maxIterations {
+		firstPending = readyPending(entries[firstPending:])
+		if firstPending == -1 {
+			return q, nil
 		}
-
-		// Process next level
-		return processLevel(configs)
-	}
-
-	// Start with all configs
-	if err := processLevel(discovered); err != nil {
-		return &Queue{
-			entries: result,
-		}, err
 	}
 
-	return &Queue{
-		entries: result,
-	}, nil
+	return q, errors.New("cycle detected during queue construction")
 }



Test patch:
diff --git a/cli/commands/dag/graph/command_test.go b/cli/commands/dag/graph/command_test.go
new file mode 100644
index 00000000..5a302cc4
--- /dev/null
+++ b/cli/commands/dag/graph/command_test.go
@@ -0,0 +1,57 @@
+package graph_test
+
+import (
+	"context"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"github.com/gruntwork-io/terragrunt/cli/commands/dag/graph"
+	"github.com/gruntwork-io/terragrunt/internal/cli"
+
+	"github.com/gruntwork-io/terragrunt/options"
+	"github.com/stretchr/testify/require"
+)
+
+// Run a benchmark on runGraphDependencies for all fixtures possible.
+// This should reveal regression on execution time due to new, changed or removed features.
+func BenchmarkRunGraphDependencies(b *testing.B) {
+	// Setup
+	b.StopTimer()
+	cwd, err := os.Getwd()
+	require.NoError(b, err)
+
+	testDir := "../../../../test/fixtures"
+
+	fixtureDirs := []struct {
+		description          string
+		workingDir           string
+		usePartialParseCache bool
+	}{
+		{"PartialParseBenchmarkRegressionCaching", "regressions/benchmark-parsing/production/deployment-group-1/webserver/terragrunt.hcl", true},
+		{"PartialParseBenchmarkRegressionNoCache", "regressions/benchmark-parsing/production/deployment-group-1/webserver/terragrunt.hcl", false},
+		{"PartialParseBenchmarkRegressionIncludesCaching", "regressions/benchmark-parsing-includes/production/deployment-group-1/webserver/terragrunt.hcl", true},
+		{"PartialParseBenchmarkRegressionIncludesNoCache", "regressions/benchmark-parsing-includes/production/deployment-group-1/webserver/terragrunt.hcl", false},
+	}
+
+	// Run benchmarks
+	for _, fixture := range fixtureDirs {
+		b.Run(fixture.description, func(b *testing.B) {
+			workingDir := filepath.Join(cwd, testDir, fixture.workingDir)
+			terragruntOptions, err := options.NewTerragruntOptionsForTest(workingDir)
+			if fixture.usePartialParseCache {
+				terragruntOptions.UsePartialParseConfigCache = true
+			} else {
+				terragruntOptions.UsePartialParseConfigCache = false
+			}
+			require.NoError(b, err)
+
+			b.ResetTimer()
+			b.StartTimer()
+			ctx := cli.NewAppContext(context.Background(), cli.NewApp(), nil)
+			err = graph.Run(ctx, terragruntOptions)
+			b.StopTimer()
+			require.NoError(b, err)
+		})
+	}
+}
diff --git a/cli/commands/graph-dependencies/graph-dependencies_test.go b/cli/commands/graph-dependencies/graph-dependencies_test.go
index e55ec865..e5527fef 100644
--- a/cli/commands/graph-dependencies/graph-dependencies_test.go
+++ b/cli/commands/graph-dependencies/graph-dependencies_test.go
@@ -19,7 +19,7 @@ func BenchmarkRunGraphDependencies(b *testing.B) {
 	cwd, err := os.Getwd()
 	require.NoError(b, err)
 
-	testDir := "../../../test"
+	testDir := "../../../test/fixtures"
 
 	fixtureDirs := []struct {
 		description          string
diff --git a/internal/discovery/discovery_test.go b/internal/discovery/discovery_test.go
index c1dbfc56..e55b91f1 100644
--- a/internal/discovery/discovery_test.go
+++ b/internal/discovery/discovery_test.go
@@ -200,8 +200,11 @@ func TestDiscoveryWithDependencies(t *testing.T) {
 	opts.RootWorkingDir = internalDir
 
 	tests := []struct {
-		name          string
-		discovery     *discovery.Discovery
+		name      string
+		discovery *discovery.Discovery
+		// Note that when comparing against this,
+		// we'll nil out the parsed configurations,
+		// as it doesn't matter for this test
 		wantDiscovery discovery.DiscoveredConfigs
 		errorExpected bool
 	}{
@@ -277,6 +280,11 @@ func TestDiscoveryWithDependencies(t *testing.T) {
 				}
 			}
 
+			// nil out the parsed configurations, as it doesn't matter for this test
+			for _, cfg := range configs {
+				cfg.Parsed = nil
+			}
+
 			assert.Equal(t, tt.wantDiscovery, configs)
 		})
 	}
@@ -389,3 +397,83 @@ func TestDiscoveredConfigsCycleCheck(t *testing.T) {
 		})
 	}
 }
+
+func TestDiscoveryWithExclude(t *testing.T) {
+	t.Parallel()
+
+	tmpDir := t.TempDir()
+
+	// Create test directory structure
+	testDirs := []string{
+		"unit1",
+		"unit2",
+		"unit3",
+	}
+
+	for _, dir := range testDirs {
+		err := os.MkdirAll(filepath.Join(tmpDir, dir), 0755)
+		require.NoError(t, err)
+	}
+
+	// Create test files with exclude configurations
+	testFiles := map[string]string{
+		"unit1/terragrunt.hcl": `
+exclude {
+  if      = true
+  actions = ["plan"]
+}`,
+		"unit2/terragrunt.hcl": `
+exclude {
+  if      = true
+  actions = ["apply"]
+}`,
+		"unit3/terragrunt.hcl": "",
+	}
+
+	for path, content := range testFiles {
+		err := os.WriteFile(filepath.Join(tmpDir, path), []byte(content), 0644)
+		require.NoError(t, err)
+	}
+
+	tgOpts := options.NewTerragruntOptions()
+	tgOpts.WorkingDir = tmpDir
+	tgOpts.Logger.Formatter().SetDisabledColors(true)
+
+	// Test discovery with exclude parsing
+	d := discovery.NewDiscovery(tmpDir).WithParseExclude()
+
+	cfgs, err := d.Discover(context.Background(), tgOpts)
+	require.NoError(t, err)
+
+	// Verify we found all configurations
+	assert.Len(t, cfgs, 3)
+
+	// Helper to find config by path
+	findConfig := func(path string) *discovery.DiscoveredConfig {
+		for _, cfg := range cfgs {
+			if filepath.Base(cfg.Path) == path {
+				return cfg
+			}
+		}
+		return nil
+	}
+
+	// Verify exclude configurations were parsed correctly
+	unit1 := findConfig("unit1")
+	require.NotNil(t, unit1)
+	require.NotNil(t, unit1.Parsed)
+	require.NotNil(t, unit1.Parsed.Exclude)
+	assert.Contains(t, unit1.Parsed.Exclude.Actions, "plan")
+
+	unit2 := findConfig("unit2")
+	require.NotNil(t, unit2)
+	require.NotNil(t, unit2.Parsed)
+	require.NotNil(t, unit2.Parsed.Exclude)
+	assert.Contains(t, unit2.Parsed.Exclude.Actions, "apply")
+
+	unit3 := findConfig("unit3")
+	require.NotNil(t, unit3)
+	if unit3.Parsed != nil {
+		assert.Nil(t, unit3.Parsed.Exclude)
+	}
+}
diff --git a/internal/queue/queue_test.go b/internal/queue/queue_test.go
index 4fc11876..c557e1a0 100644
--- a/internal/queue/queue_test.go
+++ b/internal/queue/queue_test.go
@@ -9,215 +9,218 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-func TestNewQueue(t *testing.T) {
+func TestNoDependenciesMaintainsAlphabeticalOrder(t *testing.T) {
 	t.Parallel()
 
-	t.Run("no dependencies - maintains alphabetical order", func(t *testing.T) {
-		t.Parallel()
+	// Create configs with no dependencies - should maintain alphabetical order at front
+	configs := []*discovery.DiscoveredConfig{
+		{Path: "c", Dependencies: []*discovery.DiscoveredConfig{}},
+		{Path: "a", Dependencies: []*discovery.DiscoveredConfig{}},
+		{Path: "b", Dependencies: []*discovery.DiscoveredConfig{}},
+	}
 
-		// Create configs with no dependencies - should maintain alphabetical order at front
-		configs := []*discovery.DiscoveredConfig{
-			{Path: "c", Dependencies: []*discovery.DiscoveredConfig{}},
-			{Path: "a", Dependencies: []*discovery.DiscoveredConfig{}},
-			{Path: "b", Dependencies: []*discovery.DiscoveredConfig{}},
-		}
+	q, err := queue.NewQueue(configs)
+	require.NoError(t, err)
 
-		q, err := queue.NewQueue(configs)
-		require.NoError(t, err)
+	entries := q.Entries
 
-		entries := q.Entries()
+	// Should be sorted alphabetically at front since none have dependencies
+	assert.Equal(t, "a", entries[0].Config.Path)
+	assert.Equal(t, "b", entries[1].Config.Path)
+	assert.Equal(t, "c", entries[2].Config.Path)
+}
 
-		// Should be sorted alphabetically at front since none have dependencies
-		assert.Equal(t, "a", entries[0].Path)
-		assert.Equal(t, "b", entries[1].Path)
-		assert.Equal(t, "c", entries[2].Path)
-	})
+func TestDependenciesOrderedByDependencyLevel(t *testing.T) {
+	t.Parallel()
 
-	t.Run("dependencies - ordered by dependency level", func(t *testing.T) {
-		t.Parallel()
+	// Create configs with dependencies - should order by dependency level
+	aCfg := &discovery.DiscoveredConfig{Path: "a", Dependencies: []*discovery.DiscoveredConfig{}}
+	bCfg := &discovery.DiscoveredConfig{Path: "b", Dependencies: []*discovery.DiscoveredConfig{aCfg}}
+	cCfg := &discovery.DiscoveredConfig{Path: "c", Dependencies: []*discovery.DiscoveredConfig{bCfg}}
 
-		// Create configs with dependencies - should order by dependency level
-		configs := []*discovery.DiscoveredConfig{
-			{Path: "c", Dependencies: []*discovery.DiscoveredConfig{{Path: "b"}}},
-			{Path: "b", Dependencies: []*discovery.DiscoveredConfig{{Path: "a"}}},
-			{Path: "a", Dependencies: []*discovery.DiscoveredConfig{}},
-		}
+	configs := []*discovery.DiscoveredConfig{aCfg, bCfg, cCfg}
 
-		q, err := queue.NewQueue(configs)
-		require.NoError(t, err)
+	q, err := queue.NewQueue(configs)
+	require.NoError(t, err)
 
-		entries := q.Entries()
-
-		// 'a' has no deps so should be at front
-		// 'b' depends on 'a' so should be after
-		// 'c' depends on 'b' so should be at back
-		assert.Equal(t, "a", entries[0].Path)
-		assert.Equal(t, "b", entries[1].Path)
-		assert.Equal(t, "c", entries[2].Path)
-	})
-
-	t.Run("complex dag - ordered by dependency level and alphabetically", func(t *testing.T) {
-		t.Parallel()
-
-		// Create a more complex dependency graph:
-		//   A (no deps)
-		//   B (no deps)
-		//   C -> A
-		//   D -> A,B
-		//   E -> C
-		//   F -> C
-		configs := []*discovery.DiscoveredConfig{
-			{Path: "F", Dependencies: []*discovery.DiscoveredConfig{{Path: "C"}}},
-			{Path: "E", Dependencies: []*discovery.DiscoveredConfig{{Path: "C"}}},
-			{Path: "D", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}, {Path: "B"}}},
-			{Path: "C", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}}},
-			{Path: "B", Dependencies: []*discovery.DiscoveredConfig{}},
-			{Path: "A", Dependencies: []*discovery.DiscoveredConfig{}},
-		}
+	entries := q.Entries
 
-		q, err := queue.NewQueue(configs)
-		require.NoError(t, err)
+	// 'a' has no deps so should be at front
+	// 'b' depends on 'a' so should be after
+	// 'c' depends on 'b' so should be at back
+	assert.Equal(t, "a", entries[0].Config.Path)
+	assert.Equal(t, "b", entries[1].Config.Path)
+	assert.Equal(t, "c", entries[2].Config.Path)
+}
 
-		entries := q.Entries()
-
-		// Verify ordering by dependency level and alphabetically within levels:
-		// Level 0 (no deps): A, B
-		// Level 1 (depends on level 0): C, D
-		// Level 2 (depends on level 1): E, F
-		assert.Equal(t, "A", entries[0].Path)
-		assert.Equal(t, "B", entries[1].Path)
-		assert.Equal(t, "C", entries[2].Path)
-		assert.Equal(t, "D", entries[3].Path)
-		assert.Equal(t, "E", entries[4].Path)
-		assert.Equal(t, "F", entries[5].Path)
-
-		// Also verify relative ordering
-		aIndex := findIndex(entries, "A")
-		bIndex := findIndex(entries, "B")
-		cIndex := findIndex(entries, "C")
-		dIndex := findIndex(entries, "D")
-		eIndex := findIndex(entries, "E")
-		fIndex := findIndex(entries, "F")
-
-		// Level 0 items should be before their dependents
-		assert.Less(t, aIndex, cIndex, "A should come before C")
-		assert.Less(t, aIndex, dIndex, "A should come before D")
-		assert.Less(t, bIndex, dIndex, "B should come before D")
-
-		// Level 1 items should be before their dependents
-		assert.Less(t, cIndex, eIndex, "C should come before E")
-		assert.Less(t, cIndex, fIndex, "C should come before F")
-	})
-
-	t.Run("deterministic ordering of parallel dependencies", func(t *testing.T) {
-		t.Parallel()
-
-		// Create a graph with parallel dependencies that could be ordered multiple ways:
-		//   A (no deps)
-		//   B -> A
-		//   C -> A
-		//   D -> A
-		configs := []*discovery.DiscoveredConfig{
-			{Path: "D", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}}},
-			{Path: "C", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}}},
-			{Path: "B", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}}},
-			{Path: "A", Dependencies: []*discovery.DiscoveredConfig{}},
-		}
+func TestComplexDagOrderedByDependencyLevelAndAlphabetically(t *testing.T) {
+	t.Parallel()
 
-		// Run multiple times to verify deterministic ordering
-		for range 5 {
-			q, err := queue.NewQueue(configs)
-			require.NoError(t, err)
+	// Create a more complex dependency graph:
+	// Create a more complex dependency graph:
+	//   A (no deps)
+	//   B (no deps)
+	//   C -> A
+	//   D -> A,B
+	//   E -> C
+	//   F -> C
+	configs := []*discovery.DiscoveredConfig{
+		{Path: "F", Dependencies: []*discovery.DiscoveredConfig{{Path: "C"}}},
+		{Path: "E", Dependencies: []*discovery.DiscoveredConfig{{Path: "C"}}},
+		{Path: "D", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}, {Path: "B"}}},
+		{Path: "C", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}}},
+		{Path: "B", Dependencies: []*discovery.DiscoveredConfig{}},
+		{Path: "A", Dependencies: []*discovery.DiscoveredConfig{}},
+	}
 
-			entries := q.Entries()
+	q, err := queue.NewQueue(configs)
+	require.NoError(t, err)
+
+	entries := q.Entries
+
+	// Verify ordering by dependency level and alphabetically within levels:
+	// Level 0 (no deps): A, B
+	// Level 1 (depends on level 0): C, D
+	// Level 2 (depends on level 1): E, F
+	assert.Equal(t, "A", entries[0].Config.Path)
+	assert.Equal(t, "B", entries[1].Config.Path)
+	assert.Equal(t, "C", entries[2].Config.Path)
+	assert.Equal(t, "D", entries[3].Config.Path)
+	assert.Equal(t, "E", entries[4].Config.Path)
+	assert.Equal(t, "F", entries[5].Config.Path)
+
+	// Also verify relative ordering
+	aIndex := findIndex(entries, "A")
+	bIndex := findIndex(entries, "B")
+	cIndex := findIndex(entries, "C")
+	dIndex := findIndex(entries, "D")
+	eIndex := findIndex(entries, "E")
+	fIndex := findIndex(entries, "F")
+
+	// Level 0 items should be before their dependents
+	assert.Less(t, aIndex, cIndex, "A should come before C")
+	assert.Less(t, aIndex, dIndex, "A should come before D")
+	assert.Less(t, bIndex, dIndex, "B should come before D")
+
+	// Level 1 items should be before their dependents
+	assert.Less(t, cIndex, eIndex, "C should come before E")
+	assert.Less(t, cIndex, fIndex, "C should come before F")
+}
 
-			// A should be first (no deps)
-			assert.Equal(t, "A", entries[0].Path)
+func TestDeterministicOrderingOfParallelDependencies(t *testing.T) {
+	t.Parallel()
 
-			// B, C, D should maintain alphabetical order since they're all at the same level
-			assert.Equal(t, "B", entries[1].Path)
-			assert.Equal(t, "C", entries[2].Path)
-			assert.Equal(t, "D", entries[3].Path)
-		}
-	})
-
-	t.Run("depth-based ordering verification", func(t *testing.T) {
-		t.Parallel()
-
-		// Create a graph where depth matters:
-		//   A (no deps, depth 0)
-		//   B (no deps, depth 0)
-		//   C -> A (depth 1)
-		//   D -> B (depth 1)
-		//   E -> C,D (depth 2)
-		configs := []*discovery.DiscoveredConfig{
-			{Path: "E", Dependencies: []*discovery.DiscoveredConfig{{Path: "C"}, {Path: "D"}}},
-			{Path: "D", Dependencies: []*discovery.DiscoveredConfig{{Path: "B"}}},
-			{Path: "C", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}}},
-			{Path: "B", Dependencies: []*discovery.DiscoveredConfig{}},
-			{Path: "A", Dependencies: []*discovery.DiscoveredConfig{}},
-		}
+	// Create a graph with parallel dependencies that could be ordered multiple ways:
+	// Create a graph with parallel dependencies that could be ordered multiple ways:
+	//   A (no deps)
+	//   B -> A
+	//   C -> A
+	//   D -> A
+	configs := []*discovery.DiscoveredConfig{
+		{Path: "D", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}}},
+		{Path: "C", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}}},
+		{Path: "B", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}}},
+		{Path: "A", Dependencies: []*discovery.DiscoveredConfig{}},
+	}
 
+	// Run multiple times to verify deterministic ordering
+	for range 5 {
 		q, err := queue.NewQueue(configs)
 		require.NoError(t, err)
 
-		entries := q.Entries()
+		entries := q.Entries
 
-		// Verify that items are grouped by their depth levels
-		// Level 0: A,B (no deps)
-		// Level 1: C,D (depend on level 0)
-		// Level 2: E (depends on level 1)
+		// A should be first (no deps)
+		assert.Equal(t, "A", entries[0].Config.Path)
 
-		// First verify the basic ordering
-		assert.Len(t, entries, 5, "Should have all 5 entries")
+		// B, C, D should maintain alphabetical order since they're all at the same level
+		assert.Equal(t, "B", entries[1].Config.Path)
+		assert.Equal(t, "C", entries[2].Config.Path)
+		assert.Equal(t, "D", entries[3].Config.Path)
+	}
+}
+
+func TestDepthBasedOrderingVerification(t *testing.T) {
+	t.Parallel()
 
-		// Find indices
-		aIndex := findIndex(entries, "A")
-		bIndex := findIndex(entries, "B")
-		cIndex := findIndex(entries, "C")
-		dIndex := findIndex(entries, "D")
-		eIndex := findIndex(entries, "E")
+	// Create a graph where depth matters:
+	// Create a graph where depth matters:
+	//   A (no deps, depth 0)
+	//   B (no deps, depth 0)
+	//   C -> A (depth 1)
+	//   D -> B (depth 1)
+	//   E -> C,D (depth 2)
+	configs := []*discovery.DiscoveredConfig{
+		{Path: "E", Dependencies: []*discovery.DiscoveredConfig{{Path: "C"}, {Path: "D"}}},
+		{Path: "D", Dependencies: []*discovery.DiscoveredConfig{{Path: "B"}}},
+		{Path: "C", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}}},
+		{Path: "B", Dependencies: []*discovery.DiscoveredConfig{}},
+		{Path: "A", Dependencies: []*discovery.DiscoveredConfig{}},
+	}
 
-		// Level 0 items should be at the start (indices 0 or 1)
-		assert.LessOrEqual(t, aIndex, 1, "A should be in first two positions")
-		assert.LessOrEqual(t, bIndex, 1, "B should be in first two positions")
+	q, err := queue.NewQueue(configs)
+	require.NoError(t, err)
 
-		// Level 1 items should be in the middle (indices 2 or 3)
-		assert.True(t, cIndex >= 2 && cIndex <= 3, "C should be in middle positions")
-		assert.True(t, dIndex >= 2 && dIndex <= 3, "D should be in middle positions")
+	entries := q.Entries
 
-		// Level 2 item should be at the end (index 4)
-		assert.Equal(t, 4, eIndex, "E should be in last position")
-	})
+	// Verify that items are grouped by their depth levels
+	// Level 0: A,B (no deps)
+	// Level 1: C,D (depend on level 0)
+	// Level 2: E (depends on level 1)
 
-	t.Run("error handling - cycle", func(t *testing.T) {
-		t.Parallel()
+	// First verify the basic ordering
+	assert.Len(t, entries, 5, "Should have all 5 entries")
 
-		// Create a cycle: A -> B -> C -> A
-		configs := []*discovery.DiscoveredConfig{
-			{Path: "C", Dependencies: []*discovery.DiscoveredConfig{{Path: "B"}}},
-			{Path: "B", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}}},
-			{Path: "A", Dependencies: []*discovery.DiscoveredConfig{{Path: "C"}}},
-		}
+	// Find indices
+	aIndex := findIndex(entries, "A")
+	bIndex := findIndex(entries, "B")
+	cIndex := findIndex(entries, "C")
+	dIndex := findIndex(entries, "D")
+	eIndex := findIndex(entries, "E")
 
-		q, err := queue.NewQueue(configs)
-		require.Error(t, err)
-		assert.NotNil(t, q)
-	})
+	// Level 0 items should be at the start (indices 0 or 1)
+	assert.LessOrEqual(t, aIndex, 1, "A should be in first two positions")
+	assert.LessOrEqual(t, bIndex, 1, "B should be in first two positions")
 
-	t.Run("error handling - empty config list", func(t *testing.T) {
-		t.Parallel()
+	// Level 1 items should be in the middle (indices 2 or 3)
+	assert.True(t, cIndex >= 2 && cIndex <= 3, "C should be in middle positions")
+	assert.True(t, dIndex >= 2 && dIndex <= 3, "D should be in middle positions")
 
-		q, err := queue.NewQueue([]*discovery.DiscoveredConfig{})
-		require.NoError(t, err)
-		assert.Empty(t, q.Entries())
-	})
+	// Level 2 item should be at the end (index 4)
+	assert.Equal(t, 4, eIndex, "E should be in last position")
+}
+
+func TestErrorHandlingCycle(t *testing.T) {
+	t.Parallel()
+
+	// Create a cycle: A -> B -> C -> A
+	// Create a cycle: A -> B -> C -> A
+	configs := []*discovery.DiscoveredConfig{
+		{Path: "C", Dependencies: []*discovery.DiscoveredConfig{{Path: "B"}}},
+		{Path: "B", Dependencies: []*discovery.DiscoveredConfig{{Path: "A"}}},
+		{Path: "A", Dependencies: []*discovery.DiscoveredConfig{{Path: "C"}}},
+	}
+
+	q, err := queue.NewQueue(configs)
+	require.Error(t, err)
+	assert.NotNil(t, q)
+}
+
+func TestErrorHandlingEmptyConfigList(t *testing.T) {
+	t.Parallel()
+
+	// Create an empty config list
+	configs := []*discovery.DiscoveredConfig{}
+
+	q, err := queue.NewQueue(configs)
+	require.NoError(t, err)
+	assert.Empty(t, q.Entries)
 }
 
 // findIndex returns the index of the config with the given path in the slice
-func findIndex(configs []*discovery.DiscoveredConfig, path string) int {
-	for i, cfg := range configs {
-		if cfg.Path == path {
+func findIndex(entries queue.Entries, path string) int {
+	for i, cfg := range entries {
+		if cfg.Config.Path == path {
 			return i
 		}
 	}
diff --git a/test/fixtures/cli-flag-hints/main.hcl b/test/fixtures/cli-flag-hints/main.hcl
deleted file mode 100644
index bb7b160d..00000000
--- a/test/fixtures/cli-flag-hints/main.hcl
+++ /dev/null
@@ -1 +0,0 @@
-# Intentionally empty
diff --git a/test/fixtures/cli-flag-hints/terragrunt.hcl b/test/fixtures/cli-flag-hints/terragrunt.hcl
index bb7b160d..c78e8db8 100644
--- a/test/fixtures/cli-flag-hints/terragrunt.hcl
+++ b/test/fixtures/cli-flag-hints/terragrunt.hcl
@@ -1 +1,3 @@
-# Intentionally empty
+terraform {
+  source = "tfr://registry.terraform.io/yorinasub17/terragrunt-registry-test/null//modules/one?version=0.0.2"
+}
diff --git a/test/fixtures/exclude/basic/unit1/terragrunt.hcl b/test/fixtures/exclude/basic/unit1/terragrunt.hcl
new file mode 100644
index 00000000..d74ab07a
--- /dev/null
+++ b/test/fixtures/exclude/basic/unit1/terragrunt.hcl
@@ -0,0 +1,4 @@
+exclude {
+  if      = true
+  actions = ["plan"]
+}
diff --git a/test/fixtures/exclude/basic/unit2/terragrunt.hcl b/test/fixtures/exclude/basic/unit2/terragrunt.hcl
new file mode 100644
index 00000000..0854745d
--- /dev/null
+++ b/test/fixtures/exclude/basic/unit2/terragrunt.hcl
@@ -0,0 +1,4 @@
+exclude {
+  if      = true
+  actions = ["apply"]
+}
diff --git a/test/fixtures/exclude/basic/unit3/terragrunt.hcl b/test/fixtures/exclude/basic/unit3/terragrunt.hcl
new file mode 100644
index 00000000..711b9bba
--- /dev/null
+++ b/test/fixtures/exclude/basic/unit3/terragrunt.hcl
@@ -0,0 +1 @@
+// No exclude configuration
diff --git a/test/fixtures/stacks/no-validation/live/terragrunt.stack.hcl b/test/fixtures/stacks/no-validation/live/terragrunt.stack.hcl
new file mode 100644
index 00000000..2532f668
--- /dev/null
+++ b/test/fixtures/stacks/no-validation/live/terragrunt.stack.hcl
@@ -0,0 +1,12 @@
+unit "unit1" {
+  source        = "../units"
+  path          = "unit1"
+  no_validation = true
+}
+
+unit "stack1" {
+  source        = "../stacks"
+  path          = "stack1"
+  no_validation = true
+}
+
diff --git a/test/fixtures/stacks/no-validation/stacks/stack1/terragrunt.stack.hcl b/test/fixtures/stacks/no-validation/stacks/stack1/terragrunt.stack.hcl
new file mode 100644
index 00000000..b3555a3b
--- /dev/null
+++ b/test/fixtures/stacks/no-validation/stacks/stack1/terragrunt.stack.hcl
@@ -0,0 +1,5 @@
+unit "unit2" {
+  source        = "../../../../units"
+  path          = "unit2"
+  no_validation = true
+}
\ No newline at end of file
diff --git a/test/fixtures/stacks/no-validation/units/app1/code/main.tf b/test/fixtures/stacks/no-validation/units/app1/code/main.tf
new file mode 100644
index 00000000..b6ef90fb
--- /dev/null
+++ b/test/fixtures/stacks/no-validation/units/app1/code/main.tf
@@ -0,0 +1,4 @@
+resource "local_file" "file" {
+  content  = "test file"
+  filename = "${path.module}/file.txt"
+}
diff --git a/test/fixtures/stacks/no-validation/units/app1/code/terragrunt.hcl b/test/fixtures/stacks/no-validation/units/app1/code/terragrunt.hcl
new file mode 100644
index 00000000..e69de29b
diff --git a/test/integration_debug_test.go b/test/integration_debug_test.go
index 737c9676..4f453f69 100644
--- a/test/integration_debug_test.go
+++ b/test/integration_debug_test.go
@@ -453,7 +453,7 @@ func TestDependencyGraphWithMultiInclude(t *testing.T) {
 	stderr := bytes.Buffer{}
 	require.NoError(
 		t,
-		helpers.RunTerragruntCommand(t, "terragrunt graph-dependencies --terragrunt-non-interactive --terragrunt-working-dir "+rootPath, &stdout, &stderr),
+		helpers.RunTerragruntCommand(t, "terragrunt dag graph --non-interactive --working-dir "+rootPath, &stdout, &stderr),
 	)
 	stdoutStr := stdout.String()
 
diff --git a/test/integration_find_test.go b/test/integration_find_test.go
index 866caa77..08d6eff1 100644
--- a/test/integration_find_test.go
+++ b/test/integration_find_test.go
@@ -1,9 +1,13 @@
 package test_test
 
 import (
+	"encoding/json"
+	"fmt"
 	"path/filepath"
+	"strings"
 	"testing"
 
+	"github.com/gruntwork-io/terragrunt/cli/commands/find"
 	"github.com/gruntwork-io/terragrunt/test/helpers"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -14,6 +18,7 @@ const (
 	testFixtureFindHidden            = "fixtures/find/hidden"
 	testFixtureFindDAG               = "fixtures/find/dag"
 	testFixtureFindInternalVExternal = "fixtures/find/internal-v-external"
+	testFixtureFindExclude           = "fixtures/exclude/basic"
 )
 
 func TestFindBasic(t *testing.T) {
@@ -132,3 +137,142 @@ func TestFindExternalDependencies(t *testing.T) {
 	assert.Empty(t, stderr)
 	assert.Equal(t, "a-dependent\nb-dependency\n", stdout)
 }
+
+func TestFindExclude(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		name           string
+		args           string
+		expectedOutput string
+		expectedPaths  []string
+	}{
+		{
+			name:          "show exclude configs",
+			args:          "--exclude",
+			expectedPaths: []string{"unit1", "unit2", "unit3"},
+		},
+		{
+			name:          "exclude plan command",
+			args:          "--queue-construct-as plan",
+			expectedPaths: []string{"unit2", "unit3"},
+		},
+		{
+			name:          "exclude apply command",
+			args:          "--queue-construct-as apply",
+			expectedPaths: []string{"unit1", "unit3"},
+		},
+		{
+			name:          "show exclude configs with json",
+			args:          "--exclude --json",
+			expectedPaths: []string{"unit1", "unit2", "unit3"},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+
+			helpers.CleanupTerraformFolder(t, testFixtureFindExclude)
+
+			cmd := fmt.Sprintf("terragrunt find --experiment cli-redesign --no-color --working-dir %s %s", testFixtureFindExclude, tc.args)
+			stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, cmd)
+			require.NoError(t, err)
+			assert.Empty(t, stderr)
+
+			if strings.Contains(tc.args, "--json") {
+				var configs find.FoundConfigs
+				err = json.Unmarshal([]byte(stdout), &configs)
+				require.NoError(t, err)
+
+				var paths []string
+				for _, config := range configs {
+					paths = append(paths, config.Path)
+					if strings.Contains(tc.args, "--exclude") {
+						switch config.Path {
+						case "unit1":
+							assert.NotNil(t, config.Exclude)
+							assert.Contains(t, config.Exclude.Actions, "plan")
+						case "unit2":
+							assert.NotNil(t, config.Exclude)
+							assert.Contains(t, config.Exclude.Actions, "apply")
+						default:
+							assert.Nil(t, config.Exclude)
+						}
+					}
+				}
+				assert.ElementsMatch(t, tc.expectedPaths, paths)
+			} else {
+				paths := strings.Fields(stdout)
+				assert.ElementsMatch(t, tc.expectedPaths, paths)
+			}
+		})
+	}
+}
+
+func TestFindQueueConstructAs(t *testing.T) {
+	t.Parallel()
+
+	// I'm using the list fixture here because it's more convenient.
+	helpers.CleanupTerraformFolder(t, testFixtureListDag)
+
+	testCases := []struct {
+		name           string
+		args           string
+		expectedOutput string
+		expectedPaths  []string
+	}{
+		{
+			name: "up command",
+			args: "--queue-construct-as plan",
+			expectedPaths: []string{
+				"stacks/live/dev",
+				"stacks/live/prod",
+				"units/live/dev/vpc",
+				"units/live/prod/vpc",
+				"units/live/dev/db",
+				"units/live/prod/db",
+				"units/live/dev/ec2",
+				"units/live/prod/ec2",
+			},
+		},
+		{
+			name: "down command",
+			args: "--queue-construct-as destroy",
+			expectedPaths: []string{
+				"stacks/live/dev",
+				"stacks/live/prod",
+				"units/live/dev/ec2",
+				"units/live/prod/ec2",
+				"units/live/dev/db",
+				"units/live/prod/db",
+				"units/live/dev/vpc",
+				"units/live/prod/vpc",
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+
+			helpers.CleanupTerraformFolder(t, testFixtureListDag)
+
+			cmd := fmt.Sprintf("terragrunt find --json --experiment cli-redesign --no-color --working-dir %s %s", testFixtureListDag, tc.args)
+			stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, cmd)
+			require.NoError(t, err)
+			assert.Empty(t, stderr)
+
+			var configs find.FoundConfigs
+			err = json.Unmarshal([]byte(stdout), &configs)
+			require.NoError(t, err)
+
+			var paths []string
+			for _, config := range configs {
+				paths = append(paths, config.Path)
+			}
+
+			assert.Equal(t, tc.expectedPaths, paths)
+		})
+	}
+}
diff --git a/test/integration_list_test.go b/test/integration_list_test.go
index 0c8859bc..e695aace 100644
--- a/test/integration_list_test.go
+++ b/test/integration_list_test.go
@@ -9,6 +9,11 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
+const (
+	testFixtureListBasic = "fixtures/list/basic"
+	testFixtureListDag   = "fixtures/list/dag"
+)
+
 func TestListCommand(t *testing.T) {
 	t.Parallel()
 
@@ -20,13 +25,13 @@ func TestListCommand(t *testing.T) {
 	}{
 		{
 			name:           "Basic list with default format",
-			workingDir:     "fixtures/list/basic",
+			workingDir:     testFixtureListBasic,
 			args:           []string{"list"},
 			expectedOutput: "a-unit  b-unit  \n",
 		},
 		{
 			name:       "List with long format",
-			workingDir: "fixtures/list/basic",
+			workingDir: testFixtureListBasic,
 			args:       []string{"list", "--long"},
 			expectedOutput: `Type  Path
 unit  a-unit
@@ -35,7 +40,7 @@ unit  b-unit
 		},
 		{
 			name:       "List with tree format",
-			workingDir: "fixtures/list/basic",
+			workingDir: testFixtureListBasic,
 			args:       []string{"list", "--tree"},
 			expectedOutput: `.
 ├── a-unit
@@ -74,7 +79,7 @@ func TestListCommandWithDependencies(t *testing.T) {
 	}{
 		{
 			name:       "List with dependencies in tree format",
-			workingDir: "fixtures/list/dag",
+			workingDir: testFixtureListDag,
 			args:       []string{"list", "--tree", "--dag"},
 			expected: `.
 ├── stacks/live/dev
@@ -91,7 +96,7 @@ func TestListCommandWithDependencies(t *testing.T) {
 		},
 		{
 			name:       "List with dependencies in long format",
-			workingDir: "fixtures/list/dag",
+			workingDir: testFixtureListDag,
 			args:       []string{"list", "--long", "--dependencies"},
 			expected: `Type  Path                 Dependencies
 stack stacks/live/dev
@@ -124,3 +129,57 @@ unit  units/live/prod/vpc
 		})
 	}
 }
+
+func TestListCommandWithExclude(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		name           string
+		expectedOutput string
+		args           []string
+	}{
+		{
+			name:           "List with queue-construct-as plan",
+			args:           []string{"list", "--queue-construct-as", "plan"},
+			expectedOutput: "unit2  unit3  \n",
+		},
+		{
+			name:           "List with queue-construct-as apply",
+			args:           []string{"list", "--queue-construct-as", "apply"},
+			expectedOutput: "unit1  unit3  \n",
+		},
+		{
+			name: "List with queue-construct-as plan in long format",
+			args: []string{"list", "--queue-construct-as", "plan", "--long"},
+			expectedOutput: `Type  Path
+unit  unit2
+unit  unit3
+`,
+		},
+		{
+			name: "List with queue-construct-as apply in tree format",
+			args: []string{"list", "--queue-construct-as", "apply", "--tree"},
+			expectedOutput: `.
+├── unit1
+╰── unit3
+`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+
+			helpers.CleanupTerraformFolder(t, testFixtureFindExclude)
+
+			args := []string{"terragrunt", "--no-color", "--experiment", "cli-redesign"}
+			args = append(args, tc.args...)
+			args = append(args, "--working-dir", testFixtureFindExclude)
+
+			stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, strings.Join(args, " "))
+			require.NoError(t, err)
+			require.Empty(t, stderr)
+			assert.Equal(t, tc.expectedOutput, stdout)
+		})
+	}
+}
diff --git a/test/integration_stacks_test.go b/test/integration_stacks_test.go
index 816215a0..18b45d2b 100644
--- a/test/integration_stacks_test.go
+++ b/test/integration_stacks_test.go
@@ -46,6 +46,7 @@ const (
 	testFixtureReadStack                       = "fixtures/stacks/read-stack"
 	testFixtureStackSelfInclude                = "fixtures/stacks/self-include"
 	testFixtureStackNestedOutputs              = "fixtures/stacks/nested-outputs"
+	testFixtureStackNoValidation               = "fixtures/stacks/no-validation"
 )
 
 func TestStacksGenerateBasic(t *testing.T) {
@@ -1195,6 +1196,23 @@ func TestStackNestedOutputs(t *testing.T) {
 	assert.True(t, stackV2Exists, "stack_v2 block should exist")
 }
 
+func TestStacksNoValidation(t *testing.T) {
+	t.Parallel()
+
+	helpers.CleanupTerraformFolder(t, testFixtureStackNoValidation)
+	tmpEnvPath := helpers.CopyEnvironment(t, testFixtureStackNoValidation)
+	rootPath := util.JoinPath(tmpEnvPath, testFixtureStackNoValidation, "live")
+
+	stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, "terragrunt stack run plan --experiment stacks --non-interactive --working-dir "+rootPath)
+	require.NoError(t, err)
+
+	assert.Contains(t, stderr, "Module ./.terragrunt-stack/stack1/stack1/.terragrunt-stack/unit2/app1/code")
+	assert.Contains(t, stderr, "Module ./.terragrunt-stack/unit1/app1/code")
+
+	assert.Contains(t, stdout, "Plan: 1 to add, 0 to change, 0 to destroy")
+	assert.Contains(t, stdout, "local_file.file will be created")
+}
+
 // check if the stack directory is created and contains files.
 func validateStackDir(t *testing.T, path string) {
 	t.Helper()
diff --git a/test/integration_test.go b/test/integration_test.go
index d7d7db9f..b4fb3213 100644
--- a/test/integration_test.go
+++ b/test/integration_test.go
@@ -153,6 +153,51 @@ func TestCLIFlagHints(t *testing.T) {
 	}
 }
 
+func TestGlobalFlagMoveStrictControl(t *testing.T) {
+	t.Parallel()
+
+	tfPath := os.Getenv("TG_TF_PATH")
+
+	testCases := []struct {
+		containsLogMsg    string
+		notContainsLogMsg string
+		args              string
+	}{
+		{
+			notContainsLogMsg: "The global `--tf-path` flag has moved",
+			args:              "-tf-path=" + tfPath + " init",
+		},
+		{
+			containsLogMsg: "The global `--tf-path` flag has moved",
+			args:           "--experiment cli-redesign -tf-path=" + tfPath + " init",
+		},
+	}
+
+	for i, tc := range testCases {
+		t.Run(fmt.Sprintf("testCase-%d", i), func(t *testing.T) {
+			t.Parallel()
+
+			helpers.CleanupTerraformFolder(t, testFixtureCLIFlagHints)
+			tmpEnvPath := helpers.CopyEnvironment(t, testFixtureCLIFlagHints)
+
+			rootPath := util.JoinPath(tmpEnvPath, testFixtureCLIFlagHints)
+			rootPath, err := filepath.EvalSymlinks(rootPath)
+			require.NoError(t, err)
+
+			_, stderr, err := helpers.RunTerragruntCommandWithOutput(t, "terragrunt --working-dir "+rootPath+" "+tc.args)
+			require.NoError(t, err)
+
+			if tc.containsLogMsg != "" {
+				assert.Contains(t, stderr, tc.containsLogMsg)
+			}
+
+			if tc.notContainsLogMsg != "" {
+				assert.NotContains(t, stderr, tc.notContainsLogMsg)
+			}
+		})
+	}
+}
+
 func TestExecCommand(t *testing.T) {
 	t.Parallel()
 
@@ -904,7 +949,7 @@ func TestTerragruntGraphDependenciesCommand(t *testing.T) {
 		stdout bytes.Buffer
 		stderr bytes.Buffer
 	)
-	helpers.RunTerragruntRedirectOutput(t, "terragrunt graph-dependencies --terragrunt-working-dir "+environmentPath, &stdout, &stderr)
+	helpers.RunTerragruntRedirectOutput(t, "terragrunt dag graph --working-dir "+environmentPath, &stdout, &stderr)
 	output := stdout.String()
 	assert.Contains(t, output, strings.TrimSpace(`
 digraph {