Gruntwork Release Notes

Follow

171 release notes curated from 2 sources by the Releasebot Team. Last updated: Sep 24, 2026

Get this feed:

Gruntwork Products

  • Sep 24, 2026
    • Date parsed from source:
      Sep 24, 2026
    • First seen by Releasebot:
      Sep 24, 2026
    Gruntwork logo

    Terragrunt by Gruntwork

    v1.2.0-rc1

    Terragrunt ships v1.2.0 release candidate with major new capabilities, including OCI module sources, non-interactive catalog output, runtime profiling, Azure state backend improvements, direct dependency reads from state, expansion blocks, and an MCP server for AI agents.

    v1.2.0 Release Candidate

    This is the first release candidate for Terragrunt v1.2.

    This release completes the following experiments:

    • block-iteration
    • oci
    • bounded-discovery
    • catalog-format
    • mutable-generate
    • optional-dependency-outputs
    • optional-hooks
    • azure-backend
    • version-attribute
    • profiling
    • dependency-fetch-output-from-state

    Future release candidates for v1.2.0 will include bug fixes related to these experiments or other urgent bug fixes as necessary, and documentation improvements.

    Please try out this release candidate in lower environments and share your feedback in the Associated GitHub discussion.

    Breaking Changes

    base64gzip() uses the Go 1.27 encoder

    Go 1.27 changed the compressed output of its gzip encoder. Terragrunt v1.1.5 kept base64gzip() on the older output and warned once per run that this was legacy behavior. base64gzip() now returns what the Go 1.27 encoder produces.

    A resource that compares the encoded value can plan a replacement on the first run after upgrading. An aws_instance with user_data_base64 set from base64gzip() and user_data_replace_on_change set to true is one such resource.

    Where the encoded value has to stay stable, call the experimental base64gzip_compat(), which returns the v1.1.3 output permanently. It is behind the base64gzip-compat experiment and may be renamed or removed:

    terragrunt run --all --experiment base64gzip-compat -- plan
    inputs = {
    user_data_base64 = base64gzip_compat(file("${get_terragrunt_dir()}/user-data.sh"))
    }
    

    This completes the legacy-base64gzip strict control.

    Note

    Within the 1.0 guarantees

    The 1.0 guarantees make promises about how Terragrunt remains backwards compatible. This change does not break those promises. It is listed under breaking changes so you are aware of it, in case it affects your workflows.

    base64gzip() still takes a string and returns valid gzipped base64 content that decompresses to the same value, but the encoded value itself changes. base64gzip_compat() keeps the old one.

    S3 state buckets no longer get the RootAccess bucket policy statement

    When bootstrapping an S3 state bucket, Terragrunt attached a bucket policy statement with the Sid RootAccess that granted s3:* on the bucket and its objects to arn:aws:iam::<account-id>:root, an ARN that grants access to the AWS account as a whole rather than only to its root user. That statement has been removed. It widened reach to state files that routinely hold secrets, and the account already owns the bucket.

    The skip_bucket_root_access config no longer has anything to skip, and is now deprecated. Terragrunt still accepts it, and warns about it when bootstrapping a backend whose config sets it. Enable the skip-bucket-root-access strict control to turn that warning into an error.

    To grant the AWS account root user access to the state bucket, set the new enable_bucket_root_access config:

    # root.hcl
    remote_state {
    # ... other args omitted for brevity ...
    config = {
    # ... other config omitted for brevity ...
    enable_bucket_root_access = true
    }
    }
    

    Buckets that already have the statement keep it. The state backend docs cover how to remove it yourself.

    Note

    Within the 1.0 guarantees

    The 1.0 guarantees make promises about how Terragrunt remains backwards compatible. This change does not break those promises. It is listed under breaking changes so you are aware of it, in case it affects your workflows.

    The bucket policy Terragrunt writes is not part of the CLI, HCL, or output schemas the guarantees pin, and skip_bucket_root_access remains valid configuration. Removing the statement is a bug fix, and enable_bucket_root_access restores it. See Bugs in the guarantees for how a bug fix in 1.x can change your workflows.

    New Features

    Non-interactive terragrunt catalog output with --format

    The catalog TUI needs a terminal. The --format flag (env: TG_FORMAT) writes what the catalog discovers to standard output, so a script or an agent can read the catalog without one.

    --format=jsonl writes one JSON object per catalog entry, following a published JSON schema:

    terragrunt catalog --format=jsonl | jq -c '{kind, title, component_source}'
    

    --format=md writes a Markdown document with a section per entry:

    terragrunt catalog --format=md &gt; catalog.md
    

    Without --format, terragrunt catalog opens the TUI only when standard input and standard output are both terminals. Anywhere else it writes jsonl, so piping the command needs no flag:

    terragrunt catalog | jq -c '{kind, title, component_source}'
    

    Terragrunt writes each entry as it discovers it. See Non-interactive catalog for the structure of each format and how streaming behaves.

    Previously gated behind the catalog-format experiment, non-interactive catalog output no longer requires --experiment catalog-format.

    Collect runtime profiles

    Terragrunt writes CPU, heap, and goroutine profiles on request, so you can see where a slow run spends its time. Pass --profile-cpu, --profile-mem or --profile-goroutine with a path, or --profile-dir to collect all three into one directory under conventional names. Each flag has a matching TG_PROFILE_* environment variable.

    terragrunt --profile-dir /tmp/profiles run --all -- plan
    

    Read the result with go tool pprof. The profiles cover Terragrunt itself, not the OpenTofu/Terraform processes it runs.

    Previously gated behind the profiling experiment, the profile flags no longer require --experiment profiling.

    Bound discovery with --discovery-boundary and (dir) filters

    Graph filters search up to the Git repository root for dependents and follow dependencies wherever they point, so in a monorepo they can parse sibling environments a command never needed.

    A (dir) operand in a graph filter stops traversal at that directory:

    cd environments/staging
    terragrunt find --filter '(.)...vpc'
    

    From the same directory, the --discovery-boundary flag (env: TG_DISCOVERY_BOUNDARY) applies one boundary to every --filter expression on the command:

    terragrunt run --all --filter '...vpc' --discovery-boundary . -- plan
    

    Previously gated behind the bounded-discovery experiment, bounded discovery no longer requires --experiment bounded-discovery.

    Terragrunt docs MCP server

    Terragrunt now publishes the read-only Terragrunt docs MCP server, which answers Terragrunt questions from the official docs, the CLI reference, a curated design-pattern library, and real example config. The server is public and unauthenticated. Results are pinned to a Terragrunt version, and docs pages can be read at any release tag from v0.80 onward.

    For Claude Code:

    claude mcp add -s user --transport http terragrunt-docs https://mcp.docs.terragrunt.com/mcp
    

    Cursor and other MCP clients that read an mcp.json point at https://mcp.docs.terragrunt.com/mcp instead.

    The server is in public beta. It has no availability guarantee and may change significantly.

    See the install docs for the full setup.

    Download modules from OCI registries

    An oci:// source downloads a module from an OCI Distribution registry, such as Amazon ECR, GitHub Container Registry, Azure Container Registry, Google Artifact Registry, or a self-hosted one. It works in a terraform block:

    # terragrunt.hcl
    terraform {
    source = "oci://ghcr.io/acme/tofu-modules/vpc?tag=1.0.0"
    }
    

    And in the unit and stack blocks of a terragrunt.stack.hcl:

    # terragrunt.stack.hcl
    unit "vpc" {
    source = "oci://ghcr.io/acme/terragrunt-units/vpc?tag=1.0.0"
    path = "vpc"
    }
    

    Pin the artifact with tag or digest. Setting neither selects the latest tag, and a //subdir selector reaches a directory inside the module, unit, or stack. Credentials come from OpenTofu's CLI config, from ambient Docker config, and from credential helpers such as ecr-login, so one source string resolves the same way under both tofu and Terragrunt.

    Previously gated behind the oci experiment, these sources no longer require --experiment oci. See OCI registries for the publishing contract and the full authentication order.

    Track the files that OpenTofu file functions read

    Terragrunt records the files that the built-in file functions read, so reading-based filters select the units that read them without a mark_as_read call:

    • file
    • templatefile
    • fileset
    • fileexists
    • the file* hash functions, such as filesha256

    A file read from inside a template counts too.

    mark_as_read remains the way to record a file that only OpenTofu/Terraform reads, such as one passed to a module as an input, or one a run_cmd script reads.

    Generated files stored in the CAS

    The Content Addressable Store (CAS) now stores the files that generate blocks produce. Each unit's working directory gets a hard link to the stored copy, so every unit that includes this block shares one provider.tf on disk:

    # root.hcl
    generate "provider" {
    path = "provider.tf"
    if_exists = "overwrite_terragrunt"
    contents = "provider \"aws\" {}"
    }
    

    Generated files are read-only by default, so an existing hook or script that edits a generated file in place fails with a permission error. Set mutable = true on that generate block to give each unit a writable file of its own:

    generate "provider" {
    path = "provider.tf"
    if_exists = "overwrite_terragrunt"
    mutable = true
    contents = "provider \"aws\" {}"
    }
    

    Passing --no-cas turns off the CAS for a run, and Terragrunt writes generated files as plain files:

    terragrunt run --all --no-cas -- plan
    

    See Generate blocks and Immutable by default for details.

    Previously gated behind the mutable-generate experiment, CAS storage for generated files no longer requires --experiment mutable-generate.

    Iterate unit, stack, and dependency blocks with expansion

    An expansion block declares a count or a for_each, and Terragrunt reads the block it sits in once per element. This unit block generates two units, at .terragrunt-stack/aurora/web and .terragrunt-stack/aurora/api:

    # terragrunt.stack.hcl
    unit "aurora" {
    expansion {
    for_each = toset(["web", "api"])
    }
    source = "../units/app"
    path = "aurora/${each.key}"
    values = {
    role = each.key
    }
    }
    

    A stack block expands the same way, generating one stack per element.

    An expanded dependency block produces one dependency per element, and inputs reads each one by its key:

    # terragrunt.hcl
    dependency "aurora" {
    expansion {
    for_each = toset(["web", "api"])
    }
    config_path = "../aurora-${each.key}"
    }
    inputs = {
    web_id = dependency.aurora["web"].outputs.id
    }
    

    unit and stack blocks also accept an enabled attribute. Setting it to false skips the component during stack generation:

    # terragrunt.stack.hcl
    unit "canary" {
    enabled = false
    source = "../units/app"
    path = "canary"
    }
    

    Adding an expansion block to an existing block, or shrinking one, changes the addresses of the components it produces. Read the expansion reference before changing one that has already been applied.

    Previously gated behind the block-iteration experiment, expansion blocks and the enabled attribute no longer require --experiment block-iteration.

    Bootstrap, delete, and migrate Azure Storage state backends

    Terragrunt provisions the resource group, storage account, and blob container backing an azurerm state, and converges blob versioning and soft delete on both new and pre-existing accounts. It also deletes state blobs and containers, and migrates state within a storage account. Dependency outputs of Azure-backed units are read straight from the state blob, the same as S3 and GCS.

    If you already pass --backend-bootstrap, Terragrunt now creates Azure resources it skipped before.

    Previously gated behind the azure-backend experiment, these operations no longer require --experiment azure-backend. See State Backend for configuration keys and authentication.

    Set the minimum TLS version on a bootstrapped Azure storage account

    The ARM API treats an unset minimum TLS version as TLS1_0, but Azure deprecated TLS1_0 and TLS1_1 in August 2025.

    Terragrunt now provisions a new storage account with a minimum TLS version of TLS1_2. The minimum_tls_version option raises it to TLS1_3:

    remote_state {
    backend = "azurerm"
    config = {
    storage_account_name = "myterragruntstate"
    container_name = "tfstate"
    key = "${path_relative_to_include()}/tofu.tfstate"
    resource_group_name = "tofu-rg"
    use_azuread_auth = true
    minimum_tls_version = "TLS1_3"
    }
    }
    

    TLS1_2 and TLS1_3 are the only accepted values. Terragrunt rejects the deprecated TLS1_0 and TLS1_1.

    The setting applies only when Terragrunt creates the account. Terragrunt leaves an existing account's setting alone, so change it there with the Azure portal or CLI.

    Call OpenTofu 1.13 built-in functions in Terragrunt configurations

    Terragrunt evaluates the built-in functions in configurations using its own copy of the OpenTofu implementations, which tracks OpenTofu 1.13.

    These functions are now available:

    • assumeequal
    • assumelistlength
    • assumelistlengthmax
    • assumelistlengthmin
    • assumemaplength
    • assumemaplengthmax
    • assumemaplengthmin
    • assumenotnull
    • assumesetlength
    • assumesetlengthmax
    • assumesetlengthmin
    • assumestringprefix
    • base64gunzip
    • cidrcontains
    • ephemeralasnull
    • issensitive
    • templatestring
    • urldecode

    Read dependency outputs from state by default

    Terragrunt reads dependency outputs straight from the remote state object, without initializing each dependency to run tofu output or terraform output against it. This covers the S3, GCS, and Azure Storage (azurerm) backends.

    When a direct read is unsupported, such as for a backend Terragrunt has no reader for, or fails on a permissions or network error, Terragrunt falls back to tofu/terraform output -json. The outputs are the same either way, so only the speedup is lost.

    Pass --no-dependency-fetch-output-from-state (env: TG_NO_DEPENDENCY_FETCH_OUTPUT_FROM_STATE) to always load dependency outputs through tofu/terraform output -json.

    Previously gated behind the dependency-fetch-output-from-state experiment, direct state reads no longer require --experiment dependency-fetch-output-from-state. Passing --dependency-fetch-output-from-state still works, and the dependency-fetch-output-from-state strict control turns its deprecation warning into an error.

    Version constraints for registry modules

    The terraform block accepts a version attribute holding a version constraint for a tfr:// registry module. Terragrunt downloads the highest published version that satisfies the constraint, using the same syntax as the version argument on OpenTofu and Terraform module blocks:

    terraform {
    source = "tfr://registry.opentofu.org/terraform-aws-modules/vpc/aws"
    version = "~> 3.3"
    }
    

    See the terraform block reference for the full rules.

    Previously gated behind the version-attribute experiment, version constraints for registry modules no longer require --experiment version-attribute.

    S3 buckets can be created in your account regional namespace

    An account regional namespace is a reserved subdivision of the S3 bucket namespace that only your account can create buckets in, so no one else can take or re-create those names. Bucket names in it end with your account ID, the region, and -an.

    When a bucket name matches that convention, Terragrunt creates the bucket in the account regional namespace:

    # root.hcl
    remote_state {
    backend = "s3"
    config = {
    bucket = "my-tofu-state-111122223333-us-east-1-an"
    key = "${path_relative_to_include()}/tofu.tfstate"
    region = "us-east-1"
    }
    }
    

    There is no setting to enable this. S3 accepts the -an suffix only for account regional buckets, so the name alone decides. accesslogging_bucket_name is read the same way. Buckets named any other way are created in the global namespace, and the namespace is left out of the request entirely, so S3-compatible object stores are unaffected.

    A name that fits the convention but names a region other than the bucket's own fails immediately.

    Skip dependency outputs with --no-dependency-outputs

    The --no-dependency-outputs flag (env: TG_NO_DEPENDENCY_OUTPUTS) skips output resolution for every dependency block in a run, so Terragrunt does not call tofu output on dependencies that may not be applied yet:

    terragrunt run --all --no-dependency-outputs -- validate
    

    Warning

    Use this flag with commands that do not read dependency outputs, such as init and validate. While it is set, references to dependency outputs get no real value, so plan and apply can pass empty values to OpenTofu/Terraform in their place.

    Previously gated behind the optional-dependency-outputs experiment, the flag no longer requires --experiment optional-dependency-outputs.

    Skip hooks for a run with --no-hooks

    The --no-hooks flag (env: TG_NO_HOOKS) skips every hook for a run: before_hook, after_hook, and error_hook blocks.

    terragrunt run --no-hooks -- plan
    

    Previously gated behind the optional-hooks experiment, --no-hooks no longer requires --experiment optional-hooks.

    Bug Fixes

    S3 access log delivery is granted with a bucket policy

    S3 disables ACLs on new buckets by default, and a bucket with ACLs disabled rejects the ACL grant Terragrunt wrote to make an access logging bucket accept logs. AWS recommends a bucket policy over an ACL for this grant, and recommends keeping ACLs disabled in general.

    Terragrunt now creates buckets with ACLs disabled and grants access log delivery through the logging bucket's policy instead, allowing s3:PutObject for the logging.s3.amazonaws.com service principal on behalf of buckets in the same AWS account:

    remote_state {
    backend = "s3"
    config = {
    bucket = "my-state-bucket"
    key = "${path_relative_to_include()}/tofu.tfstate"
    region = "us-east-1"
    accesslogging_bucket_name = "my-logs-bucket"
    }
    }
    

    This only applies to a logging bucket Terragrunt creates. One that already exists keeps the permissions it has, whether that is the ACL grant from an earlier Terragrunt version or something you set up yourself, and Terragrunt neither reads nor writes its policy.

    skip_accesslogging_bucket_policy opts out of that grant. skip_accesslogging_bucket_acl is deprecated and now has no effect: Terragrunt puts no ACL on the logging bucket, so there is nothing left for it to skip.

    If you set skip_accesslogging_bucket_acl to work around an AccessControlListNotSupported failure on a bucket with ACLs disabled, drop it. The bucket policy covers that bucket, and the attribute now suppresses nothing. Set skip_accesslogging_bucket_policy only if you grant log delivery yourself. Terragrunt warns when the deprecated attribute is used, and the skip-accesslogging-bucket-acl strict control turns that warning into an error.

    expansion works with autoinclude and stack dependencies

    terragrunt stack generate failed with There is no variable named "each" when a unit or stack block declared both an expansion block and an autoinclude block. The error pointed at each.key in path, even when autoinclude never referenced each.

    Generation now writes an autoinclude file for each element, and each.key, each.value, and count.index inside autoinclude resolve to that element:

    # terragrunt.stack.hcl
    unit "repo" {
    source = "../units/repo"
    path = "repo"
    }
    unit "environment" {
    expansion {
    for_each = toset(["dev", "prod"])
    }
    source = "../units/environment"
    path = "environment/${each.key}"
    autoinclude {
    dependency "repo" {
    config_path = unit.repo.path
    }
    inputs = {
    environment = each.key
    repository = dependency.repo.outputs.name
    }
    }
    }
    

    The prod element gets this terragrunt.autoinclude.hcl:

    dependency "repo" {
    config_path = "../../repo"
    }
    inputs = {
    environment = "prod"
    repository = dependency.repo.outputs.name
    }
    

    A dependency block inside autoinclude that declared its own expansion block failed generation with the same error. A unit whose terragrunt.autoinclude.hcl contained one also failed to parse. An expanded unit could not be referenced from the stack file at all, since unit.<name>.path skipped it.

    Each element of an expanded unit or stack is now referenced as unit.<name>[key].path. The generated dependency block keeps its expansion block. Generation evaluates for_each or count in the stack file, where local.* and values.* are available, writes the result as a literal, and resolves config_path for each element. The generated unit expands the dependency when it is parsed:

    # terragrunt.stack.hcl
    locals {
    regions = toset(["us-east-1", "us-west-1"])
    }
    unit "vpc" {
    expansion {
    for_each = local.regions
    }
    source = "../units/vpc"
    path = "vpc/${each.key}"
    values = {
    region = each.key
    }
    }
    unit "app" {
    source = "../units/app"
    path = "app"
    autoinclude {
    dependency "vpc" {
    expansion {
    for_each = local.regions
    }
    config_path = unit.vpc[each.key].path
    mock_outputs = { vpc_id = "vpc-mock-${each.key}" }
    }
    inputs = {
    vpc_ids = { for region, vpc in dependency.vpc : region =&gt; vpc.outputs.vpc_id }
    }
    }
    }
    
    # .terragrunt-stack/app/terragrunt.autoinclude.hcl
    dependency "vpc" {
    expansion {
    for_each = toset(["us-east-1", "us-west-1"])
    }
    config_path = {
    us-east-1 = "../vpc/us-east-1"
    us-west-1 = "../vpc/us-west-1"
    }[each.key]
    mock_outputs = { vpc_id = "vpc-mock-${each.key}" }
    }
    inputs = {
    vpc_ids = { for region, vpc in dependency.vpc : region =&gt; vpc.outputs.vpc_id }
    }
    

    A stack file that declares the same unit or stack label both with and without an expansion now fails to parse, because unit.<name> cannot refer to both.

    Discovery failed the same way on a dependency whose config_path pointed at a stack directory containing an expanded unit. It dropped the dependency instead of reporting the error, so run --all did not wait for the units in that stack. The dependency now covers every element of the expanded unit.

    Malformed {} groups in glob patterns no longer crash Terragrunt

    Some glob patterns with an empty or unclosed {} group crashed Terragrunt when it matched them, e.g. terragrunt find --filter '{./a{}'. Others silently failed to match, so {}a did not match a.

    Terragrunt now refuses these patterns with an invalid pattern error. This covers filter queries, include_in_copy and exclude_from_copy in the terraform block, and .terragrunt-catalog-ignore files. A group with one empty option next to a non-empty one, such as main.tf{,.bak}, still works.

    hcl validate --inputs reads -var and -var-file arguments verbatim

    hcl validate --inputs applied shell quoting rules to each entry in extra_arguments before reading -var and -var-file from it. Those rules treat a backslash as an escape character, so on Windows a var file path such as "-var-file=${get_terragrunt_dir()}\varfiles\main.tfvars" lost its separators, and validation failed to open the file.

    Terragrunt now reads each entry in arguments exactly as written, as the single argument it becomes on the OpenTofu/Terraform command line.

    Units with identical configs each resolve their own iam_role

    When two units had the same terragrunt.hcl content, Terragrunt could assume the first unit's IAM role for both. This hit any iam_role that depends on the unit's directory, such as:

    iam_role = "arn:aws:iam::123456789012:role/${basename(get_terragrunt_dir())}"
    

    With this config in a/ and b/, b assumed role/a instead of role/b. Terragrunt now evaluates iam_role in each unit's own directory, so get_terragrunt_dir(), find_in_parent_folders(), and similar functions return that unit's paths.

    terragrunt info print --all writes JSON Lines and reports each unit's own download directory

    info print --all wrote each unit's info indented over several lines, one object after another, and gave every unit the root's download_dir:

    $ terragrunt info print --all
    {
    "config_path": "/example/live/db/terragrunt.hcl",
    "download_dir": "/example/live/.terragrunt-cache",
    "iam_role": "",
    "terraform_binary": "tofu",
    "terraform_command": "print",
    "working_dir": "/example/live/.terragrunt-cache/EfNrjc2equLKYmOZbwT2qu1dO9c/ByrgT1vMBQjFneXYgAxchposVZ0"
    }
    {
    "config_path": "/example/live/vpc/terragrunt.hcl",
    "download_dir": "/example/live/.terragrunt-cache",
    ...
    }
    

    A line-oriented reader could not take one entry at a time:

    $ terragrunt info print --all | head -1 | jq .
    jq: parse error: Unfinished JSON term at EOF at line 2, column 0
    

    The download_dir was wrong as well. run --all creates each unit's .terragrunt-cache next to that unit's configuration, so the directory reported here was not the one the unit runs against.

    With --all, Terragrunt now writes one object per line, so the output is JSON Lines, and builds each unit's context the way run --all does:

    $ terragrunt info print --all | jq -c '{config_path, download_dir}'
    {"config_path":"/example/live/db/terragrunt.hcl","download_dir":"/example/live/db/.terragrunt-cache"}
    {"config_path":"/example/live/vpc/terragrunt.hcl","download_dir":"/example/live/vpc/.terragrunt-cache"}
    

    Printing a single unit is unchanged: one indented object.

    Malformed exclude blocks report an error

    Terragrunt silently dropped an exclude block with an attribute of the wrong type, such as actions = "plan" where a list belongs, and ran the unit as if the block weren't there. The parse now fails with an error that names the file and the attribute:

    exclude block in /live/unit/terragrunt.hcl: json: cannot unmarshal string into Go struct field ExcludeConfig.actions of type []string
    

    Discovery doesn't fetch dependency outputs, so it can't evaluate an exclude block that reads one. Terragrunt still skips that block during discovery, and now logs a warning naming the file.

    Dependencies on a stack skip its disabled units

    A dependency whose config_path pointed at a stack directory also depended on the units and stacks in that stack set to enabled = false. Stack generation never writes a disabled unit, so run --all failed on the missing directory:

    You attempted to run terragrunt in a folder that does not contain a terragrunt.hcl file. Please add a terragrunt.hcl file and try again.
    

    find --dependencies and dag graph listed the same missing path as a dependency.

    The dependency now covers only enabled units. The units of a disabled stack are left out, including a tree generated before the stack was disabled.

    Stack commands respect --discovery-boundary

    stack generate, stack run, and stack output scanned the whole working directory for stack files and ignored --discovery-boundary. In a monorepo with a catalog next to live infrastructure, a catalog stack referencing files that exist only in the live tree failed the command, even though the command never asked for that stack.

    The boundary now applies to these commands, including an inline (dir) operand, and it holds when a Git expression such as [main...HEAD] generates stacks for both compared commits. This works from the repository root:

    terragrunt stack run plan --filter '(./live/)...[main...HEAD]'
    

    Terragrunt skips the catalog units outside ./live. It still scans the whole working directory when a positive filter has no dependent-side boundary and --discovery-boundary is unset, or when the boundaries fall in separate directories.

    Dependent discovery had the same gap and parsed units outside the dependent-side boundary. It now starts the search for dependents at that boundary, which can be a directory inside the working directory.

    In a Git expression, a relative boundary resolves against the repository root like any other path in the expression. Changed units outside a dependent-side boundary are ignored, and a boundary that exists in neither compared commit is an error. A dependency-side boundary only limits dependency traversal.

    --auth-provider-cmd and --queue-construct-as reject unquoted shell operators

    Terragrunt splits --auth-provider-cmd and --queue-construct-as values into words without running a shell. An unquoted shell operator such as |, ;, &&, or > used to end the value, and Terragrunt used only the words before it, so --auth-provider-cmd 'get-creds | jq .creds' ran get-creds on its own.

    A value with an unquoted shell operator is now an error. Quote the operator to pass it as part of an argument. To run a pipeline as the auth provider, put it in a script and pass the script.

    Experiments Added

    mcp-command โ€” Serve Terragrunt operations to AI agents

    The new mcp-command experiment adds the mcp command, which serves Terragrunt operations to AI agents over the Model Context Protocol.

    An agent can ask which units exist, how they depend on each other, whether configurations pass validation, what order the units run in, what a unit's applied outputs are, and more.

    Point an MCP client at the Terragrunt binary and make sure that it enables the experiment:

    // .mcp.json
    {
    "mcpServers": {
    "terragrunt": {
    "command": "terragrunt",
    "args": ["mcp"],
    "env": {
    "TG_EXPERIMENT": "mcp-command"
    }
    }
    }
    }
    

    By default, the server refuses to start any subprocess (e.g. tofu, terraform, git, or a run_cmd program). Wherever Terragrunt would have started one, the result substitutes a stand-in for its output, such as mock_outputs for a dependency output that tofu output -json would have fetched, and lists each substitution in a degraded field.

    Pass --allow=exec to let the server run the tofu, terraform, and git that Terragrunt starts on its own during a run. A program a configuration names, through run_cmd(), a before_hook, or --auth-provider-cmd, is still refused, including a tofu, terraform, or git the configuration names for itself, so pointing the server at a repository does not hand it those programs. Allow the commands you want with --allow-cmd, a pattern matched against the program and each of its arguments (e.g. --allow-cmd='jq **'), or let the read-only tools ask: when discover, render_config, validate, or run_order meets a refused program, it sends the client an elicitation naming it, which the client usually shows the person operating the agent, and runs again with whatever they accept. plan, apply, and destroy never ask, since answering would mean running them a second time.

    The remaining capabilities are denied the same way, each granted on its own:

    • --allow=http lets Terragrunt make HTTP requests on its own.

    This includes downloading a unit's remote terraform { source }, fetching a stack's sources, reaching a cloud API to assume a role or read a bucket, and reading remote state directly from blob stores.

    • --allow=sops lets it decrypt SOPS-encrypted files.

    Unless it is granted, sops_decrypt_file fails rather than handing an agent the cleartext of your secrets.

    • --allow=env passes the server's environment variables to configurations and the commands the tools run.

    Unless it is granted, tool calls start from an empty environment, so get_env() returns its default. The server also clears its own environment variables and points HOME at an empty directory, so cloud SDKs, the SOPS decrypter, and git commands Terragrunt runs will find no credentials in environment variables or your home directory.

    Granting a capability only changes the capabilities of Terragrunt. A process started under --allow=exec can still reach out on the network on its own, so tofu init will download providers whether or not --allow=http was passed to allow Terragrunt to make network requests. The directory the server is launched in is its root. A tool call targeting a directory outside it is refused, and graph traversal is bounded there too, so a filter following dependencies or dependents cannot bring back a unit from a tree the server was never pointed at. That bounds what the server acts on, not what a configuration can read: an HCL function such as file() reads the real disk wherever it points.

    You can grant multiple capabilities at once:

    // .mcp.json
    {
    "mcpServers": {
    "terragrunt": {
    "command": "terragrunt",
    "args": ["mcp", "--allow=exec", "--allow=http"],
    "env": {
    "TG_EXPERIMENT": "mcp-command"
    }
    }
    }
    }
    

    A separate flag, --dangerously-allow-apply, adds apply and destroy tools on top of --allow=exec. Without it neither tool is registered, so a client is never told they exist, and the server won't ever run apply or destroy on behalf of a client.

    With it, the tool calls return an elicitation (the protocol's way for a server to ask the client's user a question) naming the units that would be run, and the run starts only once the person operating the client accepts it. A decline ends the tool call, and a client with no way to ask anyone is refused. The approval names the units, not the changes: nothing is planned to build that list, since a plan costs a full run that the acceptance then repeats. Call the plan tool first if you want the changes in front of you before accepting.

    Only grant this capability on infrastructure you are willing to lose.

    Passing it without --allow=exec is refused at startup, since applying means running OpenTofu/Terraform. Launch the server in the environment directory you are willing to have changed rather than at the repository root, because that directory is as far as any tool call can reach:

    // .mcp.json
    {
    "mcpServers": {
    "terragrunt-throwaway": {
    "command": "terragrunt",
    "args": [
    "mcp",
    "--working-dir", "/path/to/dev",
    "--allow=exec",
    "--dangerously-allow-apply"
    ],
    "env": {
    "TG_EXPERIMENT": "mcp-command"
    }
    }
    }
    }
    

    Warning

    Agents managing infrastructure

    An agent reading your estate is still an agent acting on it. A model can be confidently wrong about what a tool does, and it can be steered by things you don't expect, including the comments in configurations, module READMEs, and command output it was pointed at. Read what an agent responds with as a proposal. Keep a person between it and anything that changes real resources, and give it credentials scoped to what you are willing to have it reach.

    The restrictions this server places on itself are not a sandbox. They bound what the tools on this server do, and nothing else. An agent that can run shell commands can run terragrunt run --all apply itself, with your ambient credentials, whether or not the server was started with --allow=exec.

    You remain responsible for how your agents manage infrastructure, and for what they do with the access you give them. An agent with these tools and your credentials can change or destroy real resources, and accepting that risk is your decision.

    The tools that run OpenTofu/Terraform use the binary named by --tf-path, and ignore a unit's terraform_binary and engine block, since Terragrunt starts both without an --allow-cmd pattern or an approval. plan, apply, and destroy take parallelism to cap how many units run at once.

    The tools the server offers, the arguments they take, and what each capability grants are documented with the mcp command.

    This will not stabilize before v1.3, so treat the tool set, the flag names, and the shape of every result as subject to change until then. The experiment documentation lists the criteria that have to be met first.

    Experiments Updated

    Eleven experiments completed

    The following experiments graduated to general availability in this release, and the features they gated are now enabled by default:

    • azure-backend
    • block-iteration
    • bounded-discovery
    • catalog-format
    • dependency-fetch-output-from-state
    • mutable-generate
    • oci
    • optional-dependency-outputs
    • optional-hooks
    • profiling
    • version-attribute

    Each feature is described in the New Features section above.

    The corresponding --experiment flags (and TG_EXPERIMENT values) are no longer needed. Passing one still works, but emits a warning about the completed experiment, so you can drop it at your convenience.

    Thank you to everyone who ran these experiments early and filed the feedback that got them here.

    tg-login โ€” Signing in to the Gruntwork Developer Portal

    The tg-login experiment now enables terragrunt login, which signs you in to the Gruntwork Developer Portal from the CLI:

    terragrunt --experiment tg-login login
    

    Once you are signed in, terragrunt catalog discovers the repositories your organization selected in the portal and adds them to your catalog. See Gruntwork Developer Portal for how to select those repositories.

    See the experiment documentation for what still has to land before it stabilizes.

    Process Updates

    Dropped the hashicorp/terraform v0.15.3 dependency

    Terragrunt drew its built-in functions from github.com/hashicorp/terraform, pinned to v0.15.3 by a replace directive. Those functions now come from Terragrunt's own copy of the OpenTofu implementations, and go.mod has no replace directives.

    Projects that import Terragrunt as a Go module no longer resolve github.com/hashicorp/terraform, and their dependency graph drops by roughly 175 modules, most of them cloud provider SDKs that Terraform used for its backends.

    Installing and running the Terragrunt binary is unchanged.

    Pull Requests

    Features

    • feat(profiling): collect runtime profiles by default by @denis256 in #6945
    • feat(dependency): read dependency outputs from state by default by @denis256 in #6932
    • feat(hcl): switch base64gzip to the current encoder by @denis256 in #6947
    • feat: Adding login command by @yhakbar in #6804
    • feat: Adding portal Catalog API client by @yhakbar in #6805
    • feat: Adding portal repositories to terragrunt catalog by @yhakbar in #6937
    • feat: Allow creation of S3 buckets in the account regional namespace by @yhakbar in #6870
    • feat: Replace bucket ACL with policy for access logging by @yhakbar in #6868
    • feat(azurerm): adapt minimum_tls_version in azure state for v1.2.0 by @denis256 in #6968
    • feat: Adding mcp command by @yhakbar in #6851

    Bug Fixes

    • fix: Ensure that worktrees get cleaned up even when cancelled by @yhakbar in #6900
    • fix: reuse assumed role session and fix dependency init check with absolute TF_DATA_DIR by @denis256 in #6902
    • fix: Adding --cas-offline support for catalog by @yhakbar in #6904
    • fix: Fixing integration of expansion with autoinclude by @yhakbar in #6936
    • fix: Patching bug with discovery not respecting enabled attribute by @yhakbar in #6939
    • fix(docs): serve the vendor tag proxies the shared GTM container needs by @ZachGoldberg in #6974
    • fix(docs): run the Google tag on the main thread and serve the container first-party by @ZachGoldberg in #6975
    • fix(docs): resolve the container's extensionless tag paths past the trailing-slash redirect by @ZachGoldberg in #6976
    • fix(docs): resolve GA4's collect endpoints past the trailing-slash redirect by @ZachGoldberg in #6978
    • fix: More URL redaction by @yhakbar in #6961
    • fix: Remove root policy in buckets by @yhakbar in #6674
    • fix: Fixing worktree optimization with Git filter expressions on Windows by @yhakbar in #6970
    • fix: correct typo in base64gzip changelog note by @denis256 in #6983
    • fix: Addressing feedback in #6851 by @yhakbar in #6991
    • fix: respect discovery boundary in stack generate and stack run by @denis256 in #6989
    • fix: guard MustWalkTerraformOutput against negative index, nil by @denis256 in #6995
    • fix: Fixing race in TestStackExpansionGitFilterSelectsRemovedInstance by @yhakbar in #7004
    • fix: Adjusts how we ensure that the CAS only gets a Git-compatible venv by @yhakbar in #7003
    • fix(strict): name base64gzip-compat experiment in legacy-base64gzip control by @denis256 in #7014
    • fix: Resolving iam_role per unit directory and erroring on malformed exclude blocks by @yhakbar in #7012
    • fix: Fixing signin display name by @yhakbar in #7015
    • fix: respect discovery boundary when parsing dependents and in Git worktrees by @denis256 in #6998

    Documentation

    • docs: clean up released version gates for v1.1.5 by @github-actions[bot] in #6905
    • docs: Bumping Bun to 1.4.2 by @yhakbar in #6951
    • docs: More stacks docs clean-up by @yhakbar in #6960
    • docs: Dropping v1 banner by @yhakbar in #6965
    • docs: Adding GW Developer Portal Private Beta callouts by @yhakbar in #6980
    • docs: clean up released version gates for v1.1.6 by @github-actions[bot] in #6985
    • docs: Adding mcp.docs.terragrunt.com install instructions by @yhakbar in #6984
    • docs: Adding docs for the login command by @yhakbar in #6996
    • docs: Adding portal catalog setup docs by @yhakbar in #6997

    CI

    • ci(coverage): suppress TestGitStoreEnsureCommit_PinnedSHAFetchesOneCommit from timing report by @denis256 in #7017

    Chores

    • chore: Enforce usage of testify by @yhakbar in #6901
    • chore: PR comments by @denis256 in #6911
    • chore: Completing block-iteration experiment by @yhakbar in #6925
    • chore: Completing oci experiment by @denis256 in #6926
    • chore: Completing bounded-discovery experiment by @yhakbar in #6928
    • chore: Completing the catalog-format experiment by @yhakbar in #6931
    • chore: Completing the mutable-generate experiment by @yhakbar in #6933
    • chore: Completing the optional-dependency-outputs experiment by @yhakbar in #6935
    • chore: Completing azure-backend experiment by @denis256 in #6929
    • chore: Cleaning up stacks docs by @yhakbar in #6950
    • chore: Completing the version-attribute experiment by @yhakbar in #6949
    • chore: Fixing TestAwsDependencyOutputOptimization tests by @yhakbar in #6953
    • chore: Vendoring OpenTofu packages by @yhakbar in #6948
    • chore: Use RustFS where AWS usage isn't strictly necessary by @yhakbar in #6954
    • chore: Fixing these test names by @yhakbar in #6955
    • chore: Parallelizing more cloud tests by @yhakbar in #6957
    • chore: Completing the optional-hooks experiment by @yhakbar in #6938
    • chore: Consolidating URL redaction by @yhakbar in #6903
    • chore: More stack parser parity tests by @yhakbar in #6941
    • chore: Adding Windows unit tests by @yhakbar in #6907
    • chore: Fix-ups from recent merge flood by @yhakbar in #6963
    • chore: fail unstaged login stub requests by @denis256 in #6964
    • chore: Fixing Windows tests by @yhakbar in #6966
    • chore: Adding better login failure messages by @yhakbar in #6962
    • chore: Renaming TestAws to TestAWS by @yhakbar in #6979
    • chore(deps): bump cloud, azure, aws, golang dependencies by @denis256 in #6973
    • chore: Fixing race in CAS tree test race by @yhakbar in #6982
    • chore: Cache in CI better by @yhakbar in #6972
    • chore: Adding full CLI fuzz by @yhakbar in #6987
    • chore: Bumping JS deps by @yhakbar in #6992
    • chore: Getting rid of gopls workflow by @yhakbar in #6994
    • chore: Streamlining CI jobs by @yhakbar in #6840
    • chore: Setting GITHUB_STEP_SUMMARY in bats tests to avoid actually writing to summaries by @yhakbar in #7000
    • chore: Bumping Go tools by @yhakbar in #6986
    • chore: Setting up per-run env and version to increase test parallelization by @yhakbar in #6827
    • chore: Adding e2e login and catalog test by @yhakbar in #6999
    • chore: Using gotestsum better by @yhakbar in #7001
    • chore: Fuzz fixes by @yhakbar in #7002
    • chore: lint fix by @yhakbar in #7011
    • chore: Drop ContextWithEnv by @yhakbar in #7010
    • chore: Using a Dev Drive in Windows CI by @yhakbar in #7013
    • chore: Adding explanations for serial tests and parallelizing some tests by @yhakbar in #6824
    • chore: Getting rid of multierror use by @yhakbar in #7016
    Original source
  • Sep 23, 2026
    • Date parsed from source:
      Sep 23, 2026
    • First seen by Releasebot:
      Sep 23, 2026
    Gruntwork logo

    Terratest by Gruntwork

    modules/teststructure/v2.0.0

    Terratest ships v2 lockstep release v2.0.0.

    v2 lockstep release v2.0.0

    Original source
  • All of your release notes in one feed

    Join Releasebot and get updates from Gruntwork and hundreds of other software products.

    Create account
  • Sep 23, 2026
    • Date parsed from source:
      Sep 23, 2026
    • First seen by Releasebot:
      Sep 23, 2026
    Gruntwork logo

    Terratest by Gruntwork

    modules/terragrunt/v2.0.0

    Terratest ships v2 lockstep release v2.0.0.

    v2 lockstep release v2.0.0

    Original source
  • Sep 23, 2026
    • Date parsed from source:
      Sep 23, 2026
    • First seen by Releasebot:
      Sep 23, 2026
    Gruntwork logo

    Terratest by Gruntwork

    modules/terraform/v2.0.0

    Terratest ships v2 lockstep release v2.0.0.

    v2 lockstep release v2.0.0

    Original source
  • Sep 23, 2026
    • Date parsed from source:
      Sep 23, 2026
    • First seen by Releasebot:
      Sep 23, 2026
    Gruntwork logo

    Terratest by Gruntwork

    modules/ssh/v2.0.0

    Terratest ships v2 lockstep release v2.0.0.

    v2 lockstep release v2.0.0

    Original source
  • Similar to Gruntwork with recent updates:

  • Sep 23, 2026
    • Date parsed from source:
      Sep 23, 2026
    • First seen by Releasebot:
      Sep 23, 2026
    Gruntwork logo

    Terratest by Gruntwork

    modules/packer/v2.0.0

    Terratest ships v2 lockstep release v2.0.0.

    v2 lockstep release v2.0.0

    Original source
  • Sep 23, 2026
    • Date parsed from source:
      Sep 23, 2026
    • First seen by Releasebot:
      Sep 23, 2026
    Gruntwork logo

    Terratest by Gruntwork

    modules/opa/v2.0.0

    Terratest releases v2 lockstep v2.0.0.

    v2 lockstep release v2.0.0

    Original source
  • Sep 23, 2026
    • Date parsed from source:
      Sep 23, 2026
    • First seen by Releasebot:
      Sep 23, 2026
    Gruntwork logo

    Terratest by Gruntwork

    modules/k8s/v2.0.0

    Terratest ships v2 lockstep release v2.0.0.

    v2 lockstep release v2.0.0

    Original source
  • Sep 23, 2026
    • Date parsed from source:
      Sep 23, 2026
    • First seen by Releasebot:
      Sep 23, 2026
    Gruntwork logo

    Terratest by Gruntwork

    modules/httphelper/v2.0.0

    Terratest ships v2 lockstep release v2.0.0.

    v2 lockstep release

    v2.0.0

    Original source
  • Sep 23, 2026
    • Date parsed from source:
      Sep 23, 2026
    • First seen by Releasebot:
      Sep 23, 2026
    Gruntwork logo

    Terratest by Gruntwork

    modules/helm/v2.0.0

    Terratest releases v2 lockstep v2.0.0.

    v2 lockstep release v2.0.0

    Original source
  • Sep 23, 2026
    • Date parsed from source:
      Sep 23, 2026
    • First seen by Releasebot:
      Sep 23, 2026
    Gruntwork logo

    Terratest by Gruntwork

    modules/gcp/v2.0.0

    Terratest ships v2.0.0 lockstep release.

    v2 lockstep release v2.0.0

    Original source
  • Sep 21, 2026
    • Date parsed from source:
      Sep 21, 2026
    • First seen by Releasebot:
      Sep 21, 2026
    Gruntwork logo

    Terragrunt by Gruntwork

    v1.1.6

    Terragrunt fixes a Windows bug so Git filters can again find nested units, restoring find, list and browse when only nested Terragrunt configs change.

    Bug Fixes

    Git filters find nested units on Windows again

    On Windows, find, list and browse returned nothing for a Git-based filter such as --filter '[main...HEAD]' when the diff changed only unit configurations in nested directories, like nested\path\terragrunt.hcl.

    The worktree optimization in v1.1.5 checks out only the directories of changed units for these commands. On Windows, it spelled those directories with \ separators and looked them up in Git's file listing, which uses /. None matched, so it checked out nothing. Terragrunt now uses / separators for those directories on every platform.

    Original source
  • Sep 14, 2026
    • Date parsed from source:
      Sep 14, 2026
    • First seen by Releasebot:
      Sep 14, 2026
    Gruntwork logo

    Terragrunt by Gruntwork

    v1.1.5

    Terragrunt ships performance boosts, sturdier caching, and bug fixes across dependency handling, generated files, and provider setup, while adding safeguards for duplicate dependency paths and new experiments for offline CAS, block iteration, and base64gzip compatibility.

    โœจ New Features

    duplicate-dependency-labels also catches a shared config_path

    Two dependency blocks with different labels can point at the same config_path. Both parse, so the same unit is declared twice, and the two blocks drift apart as soon as one gains a mock_outputs or skip_outputs the other lacks:

    dependency "vpc" {
    config_path = "../vpc"
    }
    dependency "network" {
    config_path = "../vpc"
    }
    

    Terragrunt now warns when it finds this, alongside the existing warning for two blocks sharing a label. With the duplicate-dependency-labels strict control enabled, the warning becomes an error naming both addresses and the path they share:

    /path/to/terragrunt.hcl: dependencies vpc and network both point at ../vpc; declare that dependency once and reference it under one name
    

    ๐ŸŽ๏ธ Performance Improvements

    Fewer remote probes for sources shared across units

    run --all asked the remote what a source resolved to once per unit, so a hundred units sharing one module made a hundred requests. Each of them then read the same commit out of the store for itself.

    Units that resolve the same source at the same time now share one probe, and units that need the same Git commit share the work of reading it into the CAS.

    Measured over 100 units pointing at one Git module, counting the Git commands a run spawns:

    100 units, one shared module

    Before

    After

    First run: git ls-remote

    100

    1

    First run: reading the commit into the store

    202

    4

    First run: Git commands in total

    304

    7

    Later run, source on a branch

    100

    1

    Later run, source on a version tag

    100

    0

    The last row needs the offline-cas experiment described below; the rest apply to every run. Against a local Git server the first run went from roughly 5 seconds to 0.3, and a later run from 1 second to 0.1. A real remote makes each avoided ls-remote worth more, since it costs a network round trip rather than a local process.

    The new offline-cas experiment goes a step further and has the CAS record each probe answer in the store, so a later run can skip the request. How long it trusts an answer depends on the source:

    A source pinned to a specific revision keeps its answer for 24 hours: a semantic version tag, an S3 object version, an OCI manifest digest, an exact registry module version, or a full Mercurial changeset node.

    A source that can change upstream, such as a Git branch or an OCI tag, gets a fresh probe on every run, so a push or an upload shows up immediately.

    The experiment also unlocks three flags that change how the recorded answers are used:

    • --cas-offline never contacts a remote. Sources come from the local store and the recorded answers, and anything missing is an error rather than a fetch.
    • --cas-refresh ignores the recorded answers for one run and asks every remote again.
    • --cas-probe-ttl trusts a changeable source's answer for a duration you choose, such as 10m.

    See Recorded probes and the offline-cas experiment.

    Faster first-time source downloads

    The first time Terragrunt stores a repository in the Content Addressable Store (CAS), it copies the content of every file out of the clone. It used to launch a separate git process for each one, and on repositories with many files those launches dominated the time.

    Terragrunt now reads a repository's content through a single long-lived git process, and stores several files at a time.

    In benchmarks on an Apple M3 Max:

    files

    before

    after

    change

    200

    2.16s

    0.38s

    -83%

    1,000

    10.41s

    0.74s

    -93%

    3,000

    34.25s

    1.69s

    -95%

    The saving grows with the number of files.

    This applies when the CAS does not already hold the content, such as the first use of a new module version or a run against an empty store. Downloads that the CAS can already serve skipped this work before and are unchanged.

    CAS store improvements

    The CAS no longer writes a lock file beside each object it stores. A store had one lock file for every file and every directory listing it cached, so ~/.cache/terragrunt/cas held roughly twice as many entries as the cached content needed. Lock files already written stay where they are; deleting the store while no Terragrunt process is running against it reclaims them, and the store rebuilds without them.

    Terragrunt preserves a couple of files from a repository's .git directory when it materializes a Git source, and which files those are depends on the command. Those files used to be folded into the stored entry for the commit, so the first command to fetch a commit decided what every later command received from it: a commit first cached by stack generate, which asks for none of those files, left a later run against the same commit without them. Each file is now recorded against the commit on its own, and a command receives exactly the files it asked for whether the commit was already cached or not.

    A source pinned to a full commit SHA now asks the remote for that commit alone, one commit deep, instead of fetching every branch and tag with full history. Remotes that will not serve a commit by name, such as an older or locked-down server, still get the full fetch, so pinning keeps working everywhere. Where the remote does serve it, the first fetch of a large repository transfers the pinned commit and nothing else.

    The numbers below come from micro-benchmarks run against a git server on the same machine. The fixture is a 500-commit history whose pinned commit sits 100 commits behind the tip.

    Measurement

    Before

    After

    Change

    Git objects kept after fetching the pinned commit

    543

    43

    92% fewer

    Time to fetch the pinned commit

    575ms

    482ms

    16% faster

    Against a real remote the pinned fetch saves more than the table shows, since the objects it no longer asks for would also have to cross the network.

    Faster dependents filters

    A filter with ... before its target, such as ...vpc, finds dependents by walking the directory tree around the target and parsing each configuration it passes to see whether it depends on the target. That walk parsed every configuration from scratch, even one Terragrunt had earlier in the same command, and it runs again from each dependent it finds. On a large repository, one query could read and parse the same unrelated unit once per dependent it selected.

    Terragrunt now reuses a configuration it has already parsed, so each unit is read from disk about once per query.

    In benchmarks on an Apple M3 Max, querying the dependents of a unit from its own directory, where every other unit depends on it:

    units

    before

    after

    10

    15.3ms

    10.0ms

    50

    235ms

    150ms

    200

    3.19s

    2.09s

    From the repository root, where the walk only has to rule out the units that do not depend on the target, the same query over a 1,024-unit repository went from 250ms to 131ms.

    Faster file work, especially on small CI runners

    Terragrunt frequently does a lot of small file operations at once: copying a module into its working directory, storing a repository in the Content Addressable Store (CAS), and materializing one back out. How many it ran at once scaled with the number of vCPUs seen by the Terragrunt process or the --parallelism flag if configured.

    Terragrunt now picks that number by probing the filesystem it is about to write to to guess how much throughput it can handle to improve performance.

    The gain is largest where the filesystem is much faster or slower than Terragrunt would expect, just scaling off vCPUs.

    Materializing a 3,000 file repository on a 2 vCPU runner:

    filesystem

    before

    after

    change

    ext4

    40.8ms

    24.4ms

    -40%

    btrfs

    48.7ms

    34.2ms

    -30%

    overlayfs

    71.5ms

    56.4ms

    -21%

    On a 16 vCPU machine, storing that repository for the first time is 19% faster on ext4 and 20% faster on btrfs.

    terragrunt hcl fmt now formats at most 8 files at once by default, which measured about 14% faster than one worker per CPU on a 16 core machine.

    Runs with the fast-copy strict control enabled also copy module directories faster on macOS, by around 60% in benchmarks on an Apple M3 Max.

    Faster worktrees for Git filters

    A Git-based filter, such as --filter '[main...HEAD]', generates a worktrees to be able to run tofu in states that aren't reflected in the current worktree (e.g. when a unit is deleted, Terragrunt has to run a plan -destroy or apply -destroy in the main worktree, not the HEAD worktree in the earlier example).

    As a conditional optimization, Terragrunt now reads the Git diff first and generates worktrees only when on-disk worktrees are necessary downstream.

    For commands like find, list or browse worktree generation can be skipped more aggressively, and even more performance improvements were made there.

    On a repository with 15,000 tracked files, terragrunt find --filter '[HEAD~1...HEAD]' went from 4.7s to 0.4s on an M3 Max machine.

    Lower memory use during run --all

    When you set --json-out-dir, Terragrunt saves a JSON plan for every unit it runs. It used to build each of those documents in memory in full before writing any of it to disk, so a unit with a 64 MB plan needed roughly 168 MB to save it, and every unit running in parallel needed its own. Terragrunt now writes the document as it arrives. That same plan needs about 300 KB, roughly 550x less, and saving it finishes about 18% faster.

    Two other places held on to more than they needed. During run --all plan, Terragrunt kept every unit's error output until the run finished so it could check it for a single message at the end, and it now checks that as the output streams. Responses from a provider registry were read twice on the way in, and are now read once, which uses about 19% less memory per request.

    JSON plans are also replaced atomically now. A run that fails part way through leaves the previous file in place instead of truncating it.

    mutable = true sources are cloned instead of copied

    A source marked mutable = true needs a file of its own, because a hard link would hand out the store's read-only copy. Terragrunt now asks the filesystem for a copy-on-write clone of the stored file and copies only where the filesystem has none to give. APFS, btrfs, and XFS volumes with reflink support have one.

    A cloned target shares the stored content until you write to it, so it occupies disk space only for the parts you change. On those volumes, marking a source mutable in every unit costs disk space only for what each unit edits.

    These micro-benchmarks time materializing an editable tree on APFS on an M3 Max, once copied as in earlier releases and once cloned.

    Tree

    Before (copied)

    After (cloned)

    Change

    500 files, 7.3 MiB, most around 2 KiB

    71ms

    81ms

    14% slower

    120 files, 40 MiB, 20 of them 2 MiB each

    146ms

    23ms

    84% faster

    A clone takes about the same time for a file of any size, while a copy takes longer the bigger the file. A tree of small files takes about 10ms longer to materialize, and a tree with large files materializes about six times faster.

    Terragrunt no longer records what units read unless something needs it

    Every parse used to record the files it read. Part of that record is the content of each local module a unit sources, so Terragrunt walked those module directories once per unit, on every command, whether or not anything would look at the result.

    Only four things consult the record: reading-based filter expressions, the --queue-include-units-reading flag, find --reading, and the file tree in terragrunt browse. Terragrunt now keeps it for those and skips the module walk everywhere else.

    Benchmarks on an Apple M3 Max, across 1,000 units that all source the same local module:

    files in the module

    find --dependencies

    render --all

    50

    148 ms โ†’ 135 ms

    352 ms โ†’ 259 ms

    150

    180 ms โ†’ 135 ms

    430 ms โ†’ 263 ms

    400

    268 ms โ†’ 137 ms

    631 ms โ†’ 270 ms

    render --all performs the same full parse of each unit that run --all performs before it invokes OpenTofu, so a run over units with large local modules saves comparable time before the first plan starts.

    The saving grows with the size of the local modules a repository sources, and the new times hold steady as those modules grow. Commands that do ask about reads behave as they did before.

    Finding the repository root no longer launches git

    get_repo_root(), get_path_from_repo_root(), get_path_to_repo_root(), the runner, and discovery all need the root of the enclosing repository. Terragrunt used to ask Git for it by running git rev-parse --show-toplevel, and starting that process cost far more than producing the answer did.

    Terragrunt now finds the root itself, by looking for a .git entry in the working directory and each directory above it. Linked worktrees and submodules resolve the way they did before.

    In benchmarks on an Apple M3 Max, resolving one root, where depth is how many directories separate the starting point from the root:

    depth

    before

    after

    1

    5.29ms

    14ยตs

    5

    5.20ms

    24ยตs

    10

    5.25ms

    38ยตs

    Because Terragrunt no longer asks Git, some of Git's own settings for locating a repository stop applying. GIT_CEILING_DIRECTORIES still stops the search where it did. GIT_DIR, GIT_WORK_TREE and core.worktree are ignored, and the safe.directory ownership check is not applied, so get_repo_root() now answers in a repository owned by another user where Git refuses. A path inside a bare repository still reports that there is no repository. This is assumed to be more expected from the perspective of a Terragrunt user, and usage of git rev-parse --show-toplevel from a run_cmd is still available otherwise. If this impacts your workflows, please open a bug report, and maintainers are happy to work with you on this.

    ๐Ÿ› Bug Fixes

    More generated files are written atomically

    Terragrunt used to generate most files by opening the destination and writing into it, so the file spent time on disk half-written, and a run that failed partway through left a truncated one behind.

    These now go to a temporary file that replaces the destination once it is complete:

    • Files from generate blocks
    • The config from render --write
    • Run reports from --report-file
    • The debug file from --debug
    • .terraform.lock.hcl
    • The CLI config Terragrunt generates for OpenTofu/Terraform when the Provider Cache Server is enabled

    backend commands no longer fail on unapplied dependencies

    backend bootstrap, backend migrate and backend delete used to read the whole configuration of every unit they touched, which meant fetching the outputs of every dependency block. Declaring a dependency on a unit you had not applied yet was enough to stop them with the "detected no outputs" error, even when nothing in remote_state read that dependency.

    These commands now read only the remote_state block and the terraform block's source. They never fetch dependency outputs. A remote_state that does read a dependency output still resolves it, and still reports missing outputs when the dependency has not been applied.

    base64gzip() returns the v1.1.3 bytes again

    Terragrunt v1.1.4 was built with Go 1.27, which changed the compressed bytes produced by base64gzip(). The bytes decompress to the same content, but a resource that compares the encoded value, such as an EC2 instance with user_data_base64 and user_data_replace_on_change = true, planned a replacement after the upgrade.

    base64gzip() now returns the bytes it returned in v1.1.3 and earlier, so upgrading plans no change. Terragrunt warns once per run that this is legacy behavior. If you already applied the v1.1.4 output, every plan shows the encoded value changing back until you apply it or enable the strict control below, and a resource that depends on stability of base64gzip bytes is replaced by that apply.

    Terragrunt 1.2 will switch base64gzip() to the new encoder by default. The new base64gzip_compat() function, behind the base64gzip-compat experiment, returns the v1.1.3 bytes permanently (assuming the experiment eventually stabilizes), so call it where the encoded value must stay stable across upgrades. This function may be removed in a future release.

    To keep the current Go encoder's output now and silence the warning, enable the new legacy-base64gzip strict control:

    terragrunt run plan --strict-control legacy-base64gzip
    

    Deleted files in the CAS are fetched again instead of failing the run

    When something removes a file from the Content Addressable Store (CAS) that a cached source still needs, Terragrunt now downloads that source again and restores what is missing, then carries on.

    Terragrunt used to treat a cached source as complete once it had been downloaded, so a file deleted from the store afterwards ended the run with a read failure naming a path inside the store. Recovering meant clearing the store by hand.

    A source that no longer supplies the missing content still fails, and now says which object the store is missing. The same is true of a cas:: reference in a stack file, which names stored content directly and has no source behind it to download again, and of a run under --cas-offline, which forbids the download that would restore the store.

    catalog sanitizes the content it draws from a repository

    terragrunt catalog browses repositories you point it at, and draws their titles, descriptions, tags and READMEs to the terminal as it finds them. The catalog command did not appropriately sanitize content from repositories to ensure that the content rendered correctly in terminals.

    catalog now sanitizes everything it draws, the way terragrunt browse already sanitized the files it previews. Control characters become the Unicode replacement character, so that content draws as visible placeholders. --format jsonl and --format md keep the text as the repository wrote it.

    Fixed a crash in terragrunt catalog when a repository cannot be reached

    terragrunt catalog now reports the underlying git error when it cannot reach a repository listed in the catalog block. Previously, this could cause a crash part-way through loading. This affected any repository Terragrunt could not clone, e.g. an SSH URL with no usable key, a private repository without credentials, or a remote that timed out.

    find --dependencies lists dependencies in a stable order

    When a unit had more than one dependency, terragrunt find --dependencies --json could report them in a different order on each run, with no change to the configuration.

    The order is now fixed. list, dag graph, and browse sorted before rendering already, so their output is unchanged.

    Support backend assume_role during direct dependency state reads

    With dependency-fetch-output-from-state enabled, direct S3 state reads now correctly chain the backend's assume_role onto the dependency's execution role. Previously, cross-account dependency state reads failed with 403 AccessDenied when the remote_state block configured a separate assume_role for state access.

    Dependency state read failures fall back

    With the dependency-fetch-output-from-state experiment enabled, network, permissions, and parsing failures from a direct dependency state read could end a run that worked through native output retrieval.

    Outside render and render-json, Terragrunt now retries failed direct reads with tofu output or terraform output. If native output retrieval succeeds, the run continues and only the direct-read speedup is lost. Missing state and the two render commands retain their existing mock-output behavior.

    This fallback also covers OpenTofu client-side state encryption. Terragrunt recognizes the encrypted envelope and retries output retrieval through the configured binary instead of treating the dependency as having no outputs. If that binary can decrypt the state and native output retrieval succeeds, only the speedup is lost. render and render-json still require --no-dependency-fetch-output-from-state when they must resolve real outputs from encrypted state.

    Current flag names take precedence over deprecated ones

    A setting given under both its current name and a deprecated one took the deprecated value whatever the source of each, so TERRAGRUNT_LOG_LEVEL=debug in the environment overrode TG_LOG_LEVEL=info set beside it.

    A command-line argument now beats an environment variable under either name, and at the same level the current name beats the deprecated one. A --terragrunt-* argument still overrides a TG_* variable from the environment, so a script mixing the two keeps working.

    exec accepts --source, --source-map, and --no-auto-init

    terragrunt exec rejected --source, --source-map, and --no-auto-init as invalid flags, one message per flag: flag --source-map is not a valid flag for exec . It reads configuration and downloads source the same way run does, so there was no way to point exec at a local copy of a module, or to stop it from running init. All three flags are now registered on exec.

    terragrunt exec --source-map git::ssh://[email protected]/acme/modules.git=/local/modules -- tfmigrate plan
    

    exec therefore also reads TG_SOURCE, TG_SOURCE_MAP, and TG_NO_AUTO_INIT, along with the deprecated TERRAGRUNT_SOURCE, TERRAGRUNT_SOURCE_MAP, and TERRAGRUNT_AUTO_INIT, which it previously ignored. If you export any of those for run, exec starts honoring them too.

    --no-auto-init reaches the unit exec targets only under --in-download-dir, since exec otherwise never runs init for it. It also reaches units named in dependency blocks, with or without that flag, because Terragrunt initializes a dependency when resolving its outputs requires it.

    Direct GCS state reads work with Workload Identity Federation

    With the dependency-fetch-output-from-state experiment enabled, a GCS backend authenticated through Workload Identity Federation still ran tofu output or terraform output for every dependency, so the experiment made no difference.

    It affected any credentials file of type external_account, which is what google-github-actions/auth writes and points GOOGLE_APPLICATION_CREDENTIALS at. Terragrunt read only service_account and authorized_user files directly.

    Terragrunt now reads external_account credentials files directly, including the service-account impersonation that google-github-actions/auth configures when you give it a service account. A direct read requires the file's credential_source to be one of:

    • url
    • file with an absolute path

    Any other credential_source keeps the previous behavior, and the dependency still runs tofu output or terraform output. Reading those directly would use Terragrunt's own process rather than the unit's environment to resolve the identity:

    • executable would run the command with Terragrunt's environment.
    • AWS (an environment_id such as aws1) would use Terragrunt's AWS credentials.
    • file with a relative path would resolve against Terragrunt's working directory.

    The impersonate_service_account backend setting is a separate feature and is not affected. Backends that set it still run tofu output or terraform output.

    Files from generate blocks are created as 0600

    A generate block writes files for Terragrunt and the processes it spawns, all of which run as the user who ran Terragrunt. Creating them as 0644 granted read access that nothing uses.

    They are now created as 0600. Under the mutable-generate experiment, a block without mutable = true gets a read-only link to a copy shared between working directories, and Terragrunt stores new content as 0400 rather than 0444. With mutable = true, the block keeps a writable 0600 file of its own.

    Content the CAS is already holding keeps the permissions it was stored with, since changing them would change every file linked to that copy. Those files stay 0444 until the cache is cleared. Run with --log-level debug to see which ones.

    EC2 instance role credentials work again from inside a container

    Since v1.1.4, Terragrunt running in a container on an EC2 instance could fail to use the instance's IAM role when the instance metadata service has a hop limit of 1. Runs failed with:

    error assuming role: operation error STS: AssumeRole, get identity: get credentials:
    failed to refresh cached credentials, no EC2 IMDS role found,
    operation error ec2imds: GetMetadata, canceled, context deadline exceeded
    

    In that setup the IMDSv2 token request never gets an answer. Terragrunt v1.1.4 waited on it until the whole credential lookup timed out, so the IMDSv1 fallback that v1.1.3 and earlier relied on never ran.

    Terragrunt now gives up on the IMDSv2 token request quickly and falls back to IMDSv1, as it did before v1.1.4. No configuration change is needed.

    IAM role credentials are reused for --json-out-dir plan export

    After v1.1.4, run --all plan with an IAM role and --json-out-dir could make a second sts:AssumeRole request. That request used the role session itself and failed with AccessDenied unless the role trusted itself.

    Terragrunt now caches the assumed session in-process until five minutes before it expires (default session length is one hour when --iam-assume-role-duration is unset), keyed by role configuration and source identity, so the JSON export reuses the first assumption. Setting --iam-assume-role-duration was already a working workaround and remains supported.

    If a session cannot be refreshed but has not yet expired, Terragrunt logs a warning and continues with the cached credentials rather than failing the run.

    Provider Cache Server reads only the running implementation's CLI config files

    In v1.1.4, the Provider Cache Server started reading OpenTofu's CLI config file locations (~/.tofurc and $XDG_CONFIG_HOME/opentofu/tofurc) regardless of which binary Terragrunt was running. A machine with a stray ~/.tofurc (for example, one declaring a network_mirror) could break terragrunt init for Terraform users with errors like:

    ERROR Failed to get provider versions from "network_mirror '...'": invalid character '&lt;' looking for beginning of value
    

    Terragrunt now detects whether the configured binary is OpenTofu or Terraform before starting the cache server and reads only that implementation's CLI config files:

    • OpenTofu reads the first of these that exists: ~/.tofurc, ~/.terraformrc, $XDG_CONFIG_HOME/opentofu/tofurc (on Windows: %APPDATA%\tofu.rc, then %APPDATA%\terraform.rc).
    • Terraform reads only ~/.terraformrc (%APPDATA%\terraform.rc on Windows).

    For both implementations, Terragrunt also merges the *.tfrc and *.tfrc.json fragments from the CLI config directory: ~/.terraform.d (%APPDATA%\terraform.d on Windows), or $XDG_CONFIG_HOME/opentofu for OpenTofu when ~/.terraform.d does not exist.

    Setting TF_CLI_CONFIG_FILE continues to override the config file location for both implementations.

    The same selection applies to the credentials read for module registry downloads and version-constraint resolution, kept separately per implementation within a single run.

    The implementation is detected from the binary Terragrunt is configured to run at startup (--tf-path, TG_TF_PATH, or the first of tofu/terraform found on PATH); a terraform_binary setting inside a unit's configuration does not change which files the cache server reads. When a run's implementation differs from the one the cache server was configured for, and the two implementations would read different CLI config files on that machine, that run skips the provider cache and uses its own CLI configuration, and Terragrunt prints a warning when such a run initializes providers (init or providers lock). Both implementations resolve to the same files when none of the implementation-specific files above exist, or when TF_CLI_CONFIG_FILE names the file. Every run then uses the cache whichever binary it runs. If detection fails, Terragrunt falls back to OpenTofu's file locations.

    Run report includes cause for units that fail before OpenTofu/Terraform

    Units that failed during config evaluation or dependency output resolution were reported as a run error with an empty cause. The report now records the underlying error text in Cause.

    Thanks to @Tensho for contributing this fix!

    Fixed S3-compatible source downloads with environment credentials

    Downloading unit sources from S3-compatible services (s3::https://minio.example.com/...) now works when credentials are supplied via environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) or IAM roles rather than embedded in the URL query string. Previously, the custom endpoint was only pinned when credentials were present in the URL, causing the AWS SDK to redirect requests to amazonaws.com and fail with InvalidAccessKeyId.

    Stack dependencies include outputs from nested stacks

    A dependency pointing at a directory that holds a terragrunt.stack.hcl now reads the outputs of units generated by that stack's nested stack blocks. The run queue already waited for those units, but their outputs were missing from the dependency.

    Each nested stack adds a level named after it, the same address terragrunt stack output gives those units:

    dependency "network" {
    config_path = "../network"
    }
    inputs = {
    vpc_id = dependency.network.outputs.vpc.vpc_id
    subnet_id = dependency.network.outputs.subnets.subnet.subnet_id
    }
    

    mock_outputs entries for a nested stack's units nest under the stack's name the same way.

    A unit and a nested stack with the same name in one stack file share an address, so a dependency on that stack now errors once both have outputs to read. terragrunt stack output already rejects the same configuration. Rename one of the two blocks to give each its own address.

    ๐Ÿงช Experiments Added

    base64gzip-compat experiment adds a base64gzip_compat HCL function

    Enable the new base64gzip-compat experiment to use the base64gzip_compat(str) HCL function.

    base64gzip_compat returns the value base64gzip returned in Terragrunt v1.1.3 and earlier, and keeps returning it after Terragrunt 1.2 switches base64gzip() to the current Go encoder. Use it where the encoded value must stay stable across upgrades:

    inputs = {
    user_data_base64 = base64gzip_compat(file("${get_terragrunt_dir()}/user-data.sh"))
    }
    

    Calling base64gzip_compat without enabling the base64gzip-compat experiment returns an error. The name may still change to match OpenTofu.

    offline-cas gates the CAS probe cache

    The offline-cas experiment has been added as the gate for the probe cache the CAS keeps, in which it records what each source resolved to so a later run can skip asking the remote.

    Enabling the experiment turns the cache on and unlocks three flags that change how its answers are used: --cas-offline, --cas-refresh, and --cas-probe-ttl. Setting one of them without the experiment returns an error naming the flag.

    terragrunt run --experiment offline-cas --all --cas-offline -- plan
    

    Without the experiment nothing is recorded or served, and every run probes every source, as before.

    See the experiment documentation for what each flag does and what has to land before it stabilizes.

    tg-login reserved for signing in to the Gruntwork Developer Portal

    The tg-login experiment has been added as the gate for terragrunt login, a command for signing in to the Gruntwork Developer Portal. Once it lands, signing in lets terragrunt catalog read the repositories your organization selected in the portal rather than a catalog block you maintain yourself.

    In this release the flag is reserved only. Enabling it has no effect, and no command reads it.

    See the experiment documentation for what is planned and what has to land before it stabilizes.

    ๐Ÿงช Experiments Updated

    azure-backend can assign the blob data role during bootstrap

    Creating an Azure storage account grants no access to the blobs inside it, so an identity using use_azuread_auth could bootstrap the backend and then fail to read state as unauthorized until someone granted the data-plane role by hand.

    With the azure-backend experiment enabled, assign_blob_data_role = true now has bootstrap grant Storage Blob Data Contributor on the storage account:

    remote_state {
    backend = "azurerm"
    config = {
    storage_account_name = "myterragruntstate"
    container_name = "tfstate"
    key = "${path_relative_to_include()}/terraform.tfstate"
    resource_group_name = "terraform-rg"
    use_azuread_auth = true
    assign_blob_data_role = true
    }
    }
    

    The role goes to the identity Terragrunt authenticated as, resolved from the access token it already holds rather than from a directory lookup, so it works for identities that cannot read Microsoft Entra. Set principal_id to grant the role to a different user, group, or service principal.

    Existing assignments are detected and left alone, so reruns need only read permission on role assignments.

    The setting is opt-in: creating a role assignment requires Microsoft.Authorization/roleAssignments/write, which Contributor does not include. Leaving it unset preserves the previous behavior of assigning nothing.

    expansion blocks now iterate dependency, unit, and stack blocks

    With the block-iteration experiment enabled, a dependency, unit, or stack block can have an expansion block declaring a count or a for_each. Terragrunt reads the block once per element, producing one dependency, unit, or stack for each:

    # terragrunt.stack.hcl
    unit "aurora" {
    expansion {
    for_each = toset(["web", "api"])
    }
    source = "../units/app"
    path = "aurora/${each.key}"
    values = {
    role = each.key
    }
    }
    

    You address each element by its key. An expanded dependency is read as dependency.aurora["web"].outputs.id, and terragrunt stack output 'aurora["web"].role' reaches one element of an expanded unit.

    Adding an expansion to a block that did not have one therefore changes its address, and shrinking a for_each or lowering a count removes addresses. Terragrunt has no moved equivalent, so nothing records the rename for you: references and stack output scripts need updating by hand, and state left behind at an address that no longer exists has to be destroyed deliberately.

    The experiment also enables an enabled attribute on unit and stack blocks. Setting it to false drops the component from stack generation and from terragrunt stack output, and leaves every other address alone. dependency blocks accept enabled without the experiment.

    See the expansion block reference for the rules, the addressing scheme, and how to clean up state left behind when an expansion shrinks.

    symlinks experiment: include_in_copy copies the contents of symlinked directories again

    In v1.1.4, files behind a symlinked directory named in include_in_copy were not copied into the OpenTofu/Terraform working directory, so they were missing from .terragrunt-cache. exclude_from_copy patterns reaching through a symlinked directory also excluded nothing.

    With the symlinks experiment enabled (--experiment symlinks or TG_EXPERIMENT=symlinks), patterns rooted at a symlinked directory expand through the link again, for both include_in_copy and exclude_from_copy, as in v1.1.3 and earlier. Without the experiment, the v1.1.4 behavior is unchanged.

    A link that points back at a directory already being copied, or at a parent of one, such as a link to the unit directory itself, is skipped. Terragrunt logs a warning naming the link when that happens.

    render previews an expanded dependency block written in JSON

    With the block-iteration experiment enabled, a configuration written in JSON now renders the same way an HCL one does. It has no HCL to quote, so Terragrunt writes the block as the HCL that means the same thing and previews the elements underneath it:

    $ cat terragrunt.hcl.json
    {"dependency": {"shard": {
    "expansion": {"count": 2},
    "config_path": "../shard-${count.index}"
    }}}
    $ terragrunt render --experiment block-iteration
    dependency "shard" {
    expansion {
    count = 2
    }
    config_path = "../shard-${count.index}"
    }
    # Expands to:
    #
    # dependency "shard" {
    # config_path = "../shard-0"
    # }
    #
    # dependency "shard" {
    # config_path = "../shard-1"
    # }
    

    Previously the elements rendered as ordinary blocks, which repeated one label. Terragrunt warns about that and rejects it under the duplicate-dependency-labels strict control, so the rendered file did not read back.

    --format json no longer drops the elements either. Its dependency map is keyed by label, which every element shares, so it kept whichever element came last. JSON has no comment to preview the elements in, so it now emits the block as it was written, references and all:

    $ terragrunt render --format json --experiment block-iteration
    {
    "dependency": {
    "shard": {
    "expansion": { "count": 2 },
    "config_path": "../shard-${count.index}",
    "skip_outputs": true
    }
    }
    }
    

    Whichever syntax you write and whichever format you ask for, rendering the output again returns it unchanged.

    Expanded units keep their own outputs when a whole stack is a dependency

    With the block-iteration experiment enabled, a dependency pointing at a directory that holds a terragrunt.stack.hcl collected the outputs of an expanded unit under the block's bare label. Every element wrote to that one label, so only the last one survived, and reading it returned another element's outputs.

    Each element is now reachable under its own key, matching the address terragrunt stack output already gives it:

    dependency "networking" {
    config_path = "../live"
    }
    inputs = {
    web_id = dependency.networking.outputs.aurora["web"].id
    api_id = dependency.networking.outputs.aurora["api"].id
    }
    

    A unit that declares no expansion is still read as dependency.networking.outputs.vpc.id.

    Pull Requests

    โœจ Features

    • feat: Adding tg-login experiment by @yhakbar in #6759
    • feat(azure): assign blob data role on bootstrap in Azure by @denis256 in #6764
    • feat: Add ability to open browser for approval by @yhakbar in #6793
    • feat: Adding CLI poll for login approval by @yhakbar in #6795
    • feat: Adding token storage by @yhakbar in #6801

    ๐Ÿ› Bug Fixes

    • fix(cliconfig): isolate cloned helpers and hosts by @denis256 in #6782
    • fix: Check duplicate dependency config path by @yhakbar in #6834
    • fix: Writing more generated files atomically by @yhakbar in #6776
    • fix(config): harden dependency state output fetching by @denis256 in #6794
    • fix(provider-cache): Fix provider cache configuration selection by @denis256 in #6789
    • fix: Sanitizing some untrusted input by @yhakbar in #6802
    • fix: restore AWS IMDS credential fallback for container environments by @denis256 in #6837
    • fix(gcs): read dependency outputs from state with external_account creds by @denis256 in #6815
    • fix: Expand include_in_copy/exclude_from_copy globs through symlinked directories by @denis256 in #6817
    • fix: Fixing stack dependency unit output cty access by @yhakbar in #6839
    • fix: adding --source-map and --no-auto-init to terragrunt exec by @denis256 in #6792
    • fix(config): restore v1.1.3 base64gzip output by default by @denis256 in #6835
    • fix(getter): pin S3-compatible endpoint independently of URL credentials by @denis256 in #6857
    • fix: Fixing catalog nil logger panic by @yhakbar in #6871
    • fix: Fixing render --json expansion logic by @yhakbar in #6763
    • fix: populate run report cause for units that fail before tofu/terraform runs by @Tensho in #6819
    • fix: Use partial parse when possible for backend commands by @yhakbar in #6790
    • fix(config): use backend assume_role for direct dependency state reads by @denis256 in #6883
    • fix: Preventing Windows flag usage panic in tests by @yhakbar in #6877
    • fix: Adding store repair for corrupted CAS stores by @yhakbar in #6872
    • fix: Fixing accessing nested stack outputs in stack dependencies by @yhakbar in #6892
    • fix: Fixing mutable-generate tmp file leak by @yhakbar in #6895
    • fix(amazonsts): stop json-out-dir IAM self-assume by @denis256 in #6899

    ๐ŸŽ๏ธ Performance

    • perf: Pre-compile color table with a generate script by @yhakbar in #6780
    • perf: Use git cat-file --batch instead of multiple git cat-file calls per blob by @yhakbar in #6838
    • perf: Tuning FS concurrency by @yhakbar in #6854
    • perf: Inline git rev-parse --show-toplevel as a Go func by @yhakbar in #6867
    • perf: Streaming buffered output by @yhakbar in #6761
    • perf: Using singleflight cached CAS probe by @yhakbar in #6841
    • perf: Using a sync.Pool for hot buffers by @yhakbar in #6769
    • perf: Reuse already-parsed configs when discovering dependents by @yhakbar in #6849
    • perf: Improving worktree performance by @yhakbar in #6864
    • perf: Avoid tracking reading when unnecessary by @yhakbar in #6862
    • perf: Improve CAS store hygiene by @yhakbar in #6843

    ๐Ÿ“– Documentation

    • docs: Bumping Bun to v1.4 by @yhakbar in #6762
    • docs: Updating docs for supported flags on dag graph by @yhakbar in #6788
    • docs: Updating block-iteration documentation by @yhakbar in #6844
    • docs(changelog): note report Cause for prerun failures by @denis256 in #6878
    • docs: Adding sign-commits to the use of peter-evans/create-pull-request for docs clean-up PRs by @yhakbar in #6891
    • docs: Closing gaps in recent feature docs by @yhakbar in #6893
    • docs: Addressing feedback from #6892 by @yhakbar in #6896
    • docs: Fixing docs build by @yhakbar in #6897

    ๐Ÿงน Chores

    • chore: Adding exec sandboxed logic to seatbelt by @yhakbar in #6760
    • chore: Collapse runner packages by @yhakbar in #6765
    • chore: Running go fix ./... on all tags by @yhakbar in #6766
    • chore: dependencies update by @denis256 in #6719
    • chore: Validating device auth duration better by @yhakbar in #6779
    • chore: Moving integration tests to in-memory CLI tests by @yhakbar in #6771
    • chore: Moving discovery tests in-memory by @yhakbar in #6774
    • chore: Typed stack validation errors and consistent generated-path comparison by @yhakbar in #6777
    • chore: Adding injectable cap to speed up TestUnitPathsFromStackDir_DepthCapReturnsError test by @yhakbar in #6781
    • chore: Modernizing with new Go 1.27 constructs by @yhakbar in #6772
    • chore: Fixing lints on main by @yhakbar in #6785
    • chore: go mod fixes by @denis256 in #6786
    • chore: Deterministic dependency order for find --dependencies --json by @yhakbar in #6800
    • chore(deps): bump google.golang.org/grpc from 1.83.0 to 1.83.1 by @dependabot[bot] in #6808
    • chore(deps): bump the js-dependencies group across 1 directory with 11 updates by @dependabot[bot] in #6806
    • chore(deps): bump go dependencies by @denis256 in #6820
    • chore: Adding test coverage for filters with block iteration by @yhakbar in #6830
    • chore: Improving expansion diagnostics by @yhakbar in #6833
    • chore: Cover object and tuple for_each in the expansion engine tests by @yhakbar in #6829
    • chore: Enabling nolintlint by @yhakbar in #6803
    • chore: Adding explicit capacity hints by @yhakbar in #6825
    • chore: Adding full block-iteration lifecycle tests by @yhakbar in #6846
    • chore: Addressing review feedback from #6854 by @yhakbar in #6865
    • chore: Fixing cloud credentials test env semantics by @yhakbar in #6826
    • chore: Fixing sandbox tests by @yhakbar in #6882
    • chore(deps): bump astro from 7.2.7 to 7.2.8 in /docs by @dependabot[bot] in #6855
    • chore(deps): bump the js-dependencies group across 1 directory with 8 updates by @dependabot[bot] in #6885
    • chore: Clean-up partial worktrees on failure by @yhakbar in #6884
    • chore: Bumping go-runewidth to v0.0.30 by @yhakbar in #6879
    • chore: Bumping go-getter to v2.2.4 by @yhakbar in #6881
    • chore: Generating .terraform directory on-demand instead of leaving it committed in the repo by @yhakbar in #6887
    • chore: Globally replacing xsync.Map with a map and mutex by @yhakbar in #6880
    • chore: General clean-up from recent PRs by @yhakbar in #6886
    • chore: Revert to materializing worktrees with git checkout instead of git archive by @yhakbar in #6890
    Original source
  • Aug 27, 2026
    • Date parsed from source:
      Aug 27, 2026
    • First seen by Releasebot:
      Aug 28, 2026
    Gruntwork logo

    Terragrunt by Gruntwork

    v1.1.4

    Terragrunt ships interactive scaffold prompts, stricter dependency label checks, faster startup, and a wide set of fixes across CAS, provider caching, rendering, HCL validation, and security hardening. It also adds experiment updates for direct state reads and dependency expansion previews.

    โœจ New Features

    duplicate-dependency-labels strict control

    Declaring two dependency blocks with the same label in one terragrunt.hcl configuration file parsed without error, and then quietly resolved every reference to that label to whichever block came last. The blocks before it were silently overridden:

    dependency "vpc" {
    config_path = "../vpc-us-east-1"
    }
    dependency "vpc" {
    config_path = "../vpc-us-west-2"
    }
    inputs = {
    # Reads ../vpc-us-west-2.
    vpc_id = dependency.vpc.outputs.vpc_id
    }
    

    Terragrunt now warns when it finds this. With the new duplicate-dependency-labels strict control enabled, the warning becomes an error naming the address the blocks share:

    terragrunt run plan --strict-control duplicate-dependency-labels
    /path/to/terragrunt.hcl: dependency vpc is declared more than once; every dependency needs an address of its own
    

    Give each block a label of its own. A configuration that was relying on the shadowing to pick the last block should keep only that block.

    scaffold asks for values interactively

    Scaffolding from the command line wrote # TODO placeholders for every input and left you to fill them in by hand, while scaffolding the same component from the Catalog TUI opened a form and collected them. terragrunt scaffold now opens that same form:

    terragrunt scaffold github.com/gruntwork-io/terragrunt-infrastructure-modules-example//modules/mysql
    

    For a module or a template it lists the source's variables; for a unit or a stack it lists the values.* references its configuration makes, which are written to terragrunt.values.hcl. Dismissing the form with esc writes nothing.

    The form is skipped, and the placeholders written as before, when you pass --non-interactive, when stdin is not a terminal, or when the source asks for nothing. A scaffold in a CI job, or one run by another program, therefore behaves exactly as it did.

    See Scaffold for the full behavior, and the form's keybindings for driving it.

    ๐ŸŽ๏ธ Performance Improvements

    Faster startup when --tf-path is not set

    When you don't set --tf-path, Terragrunt picks the binary it wraps by looking for tofu on your PATH and falling back to terraform when it isn't there. Terragrunt used to make that choice by running tofu -version, which meant launching a process at the start of every command, including commands like find and list that never run the binary. That process launch is gone, and a terragrunt --version benchmark runs roughly 1.7x faster as a result.

    This changes what happens when tofu is on your PATH but can't run: Terragrunt now selects it and reports the failure rather than silently falling back to terraform. Set --tf-path or TG_TF_PATH to pick the binary yourself.

    ๐Ÿ› Bug Fixes

    Autoinclude dependency overrides no longer evaluate replaced paths

    Terragrunt used to evaluate a dependency's original config_path before applying a sibling autoinclude override. This could prevent a unit from being parsed when the original path referenced a value that the unit no longer supplied, even though the autoinclude replaced that path. Terragrunt now leaves replaced dependency blocks undecoded, then applies the autoinclude override. Dependency blocks without an autoinclude override are still validated.

    Blocks that use expansion are still decoded, because a bare autoinclude label does not name their instances. If the autoinclude also declares the same label without expansion, Terragrunt reports a dependency label collision.

    Fixed Git sources with a depth query parameter

    A terraform.source (or stack source) URL carrying the go-getter depth query parameter, such as ...vpc.git?depth=1&ref=v5.21.0, failed to download since v1.1.0, when the CAS became the default path for Git sources. Terragrunt lifted ref out of the URL but left depth in place, so git received ...vpc.git?depth=1 and rejected it as an invalid repository name. A URL with depth and no ref hit the same failure.

    Terragrunt now strips depth, with or without a ref, before invoking git, so these sources download again. The clone depth itself always comes from --cas-clone-depth, which defaults to 1; a depth on a source URL is never applied for CAS clones.

    CAS handles local sources that have already been initialized

    With CAS enabled, reading a local source that had already been initialized failed and fell back to the slower standard copy. Generating a stack from such a unit logged CAS processing failed ... source escapes repository root.

    Provider caching was the cause. Both the Provider Cache Server and the Automatic Provider Cache Dir leave the plugins under .terraform pointing into a shared cache outside the source. CAS read those links as the source reaching outside itself and refused to copy the link for safety.

    CAS now leaves .terraform and .terragrunt-cache out of local sources, keeping .terraform.lock.hcl and everything else. OpenTofu, Terraform, and Terragrunt rebuild both directories on demand, so units and stacks no longer receive a stale copy of either. Running tofu init in a source directory no longer changes that source's CAS key.

    Fixed the signal sent to a running command during shutdown

    On Windows, when a failure rather than Ctrl+C cancelled a run, Terragrunt crashed with a nil pointer panic instead of stopping the command it had started. It now terminates the command, which is the closest thing Windows offers to an interrupt.

    On every platform, when a command exited on its own during the grace period after Ctrl+C, Terragrunt could still send it the signal and then log a forwarding error against a process that was already gone.

    terraform_binary respected when reading dependency outputs

    Reading a dependency block's outputs ignored the terraform_binary of the unit being read and fell back to the auto-detected binary, which is OpenTofu whenever tofu is on your PATH. With terraform_binary = "terraform", a unit ran through Terraform while the dependency it consumed was read through OpenTofu. A run --all over units that each worked on their own then failed with a backend initialization error, followed by a misleading There is no variable named "dependency".

    Dependency outputs are now read through the binary the dependency itself configures, so a unit's terraform_binary applies wherever its state is read. --tf-path and TG_TF_PATH still take precedence over the config value.

    Numbers with extreme exponents fail fast instead of stalling

    A number literal such as 9E9999999 in inputs, locals, or a dependency block's mock_outputs used to cost over a minute of CPU on a single unit. Written out in decimal that number is ten million digits long, and terragrunt render --format=json produced every digit before failing with a ten megabyte error message.

    Terragrunt now rejects numbers larger than 1e4096, and non-zero numbers smaller than 1e-4096, before it tries to write them out, and names the attribute holding the value:

    count: number is outside the supported range of 1e-4096 to 1e4096
    

    Numbers inside that range are unaffected.

    Registry credentials are no longer copied into the generated CLI config

    When the Provider Cache Server is enabled, Terragrunt writes a CLI config for OpenTofu/Terraform into each unit's working directory, based on your own CLI config. That generated file used to include a copy of every credentials block from your config, including the ones for registries Terragrunt routes through the cache server.

    Those copies were never read. For a routed registry, Terragrunt sets the matching TF_TOKEN_<hostname> environment variable, which takes precedence over a credentials block, and the cache server presents your real credentials when it contacts the registry on your behalf. The generated file now leaves the block out for those registries, so your token stays in the CLI config you put it in instead of being duplicated somewhere it had no effect.

    Credentials for hosts the cache server does not route are unchanged, since OpenTofu/Terraform contacts those directly and still reads them from the generated config.

    Upgrading does not rewrite the files an earlier version already generated. Each is named .terraformrc and sits in a unit's working directory, which is under .terragrunt-cache for remote sources. Delete those files, or clear the cache, to get the copied credentials off disk.

    Generated files are readable only by the user who ran Terragrunt

    Terragrunt created several files and directories that other users on the same machine could read:

    The CLI config Terragrunt writes for OpenTofu/Terraform when the Provider Cache Server is enabled, and the directory holding it.
    The JSON plan files written to --json-out-dir, and that directory.
    The directories holding the plan files written to --out-dir.
    The config written by render --write, which holds the resolved values of inputs, locals, and dependency outputs.

    Terragrunt now creates those files as 0600 and those directories as 0700.

    hcl fmt --stdin honors --check and --diff

    terragrunt hcl fmt --stdin ignored --check and --diff. It printed the reformatted HCL and exited 0 whether or not the input needed formatting.

    --check now exits with status code 1 when the input needs formatting, and --diff prints a unified diff labeled old/stdin and new/stdin. Neither flag prints the formatted content, so getting that content back means running --stdin without them.

    hcl validate no longer crashes on errors that carry no source location

    terragrunt hcl validate crashed while formatting its output when one of the errors it found had no position in the configuration. Terragrunt now prints that error's summary and detail, without a location line.

    Fixed the deprecated environment variables for hcl validate

    TG_HCLVALIDATE_STRICT_VALIDATE, the deprecated name for --strict, also turned on --show-config-path. --strict only takes effect alongside --inputs, and --show-config-path cannot be combined with --inputs. With that variable set, terragrunt hcl validate --inputs failed with specifying both -show-config-path and -inputs is invalid.

    TG_HCLVALIDATE_SHOW_CONFIG_PATH, the deprecated name for --show-config-path, was not recognized at all.

    TG_HCLVALIDATE_STRICT_VALIDATE now sets only --strict, and TG_HCLVALIDATE_SHOW_CONFIG_PATH sets --show-config-path. TG_STRICT_VALIDATE, TERRAGRUNT_STRICT_VALIDATE, and TERRAGRUNT_HCLVALIDATE_SHOW_CONFIG_PATH are unchanged.

    Fixed panic on invalid if_disabled value with include block

    A generate block with an invalid if_disabled value combined with an include block caused a nil pointer panic instead of a descriptive error. Terragrunt now returns an error naming the generate block and the invalid value, consistent with if_exists validation.

    OCI sources reject Docker-style :tag suffixes instead of fetching latest

    An oci:// source that pinned a version with a Docker-style suffix, like oci://ghcr.io/acme/modules/vpc:1.0.0, silently ignored the suffix and resolved the latest tag, so a run could fetch a different module version than the one pinned. Terragrunt now validates the registry and repository the same way OpenTofu does and rejects such sources with an error that shows the source rewritten in the supported ?tag=/?digest= form, for example oci://ghcr.io/acme/modules/vpc?tag=1.0.0. Repository names that violate the OCI reference grammar are also rejected before any registry is contacted.

    Prompts accept a piped answer that has no trailing newline

    Piping an answer to a confirmation prompt, as in printf yes | terragrunt run --all destroy, failed with an EOF error because Terragrunt discarded a final answer that ended without a newline. Terragrunt now reads that final answer, and only a prompt that gets no input at all reports EOF.

    Provider cache supports signed provider download URLs

    When a provider mirror returned a signed download URL, the Provider Cache Server used the entire URL, including its query string, as the archive filename. Long authentication parameters could exceed filesystem filename limits and fail with file name too long.

    Terragrunt now derives the archive filename only from the URL path while preserving the query string when downloading it. Signed provider URLs, including archives in nested object paths and relative mirror URLs, now download and cache correctly.

    find and list reject a --queue-construct-as value that holds no command

    A value made only of shell punctuation, such as terragrunt find --queue-construct-as=';', ended the run with a crash report. A value that quotes an empty command, such as --queue-construct-as='""', was accepted even though it names no command.

    find and list now exit with an error that repeats the value you passed and shows what --queue-construct-as expects instead.

    render --write picks a default filename without a format flag

    terragrunt render --write failed with is a directory unless it was paired with --format or --json. Only those flags set the default filename, so a bare --write had no output path and Terragrunt tried to write to the unit directory itself.

    The default now follows the format in use. terragrunt render --write writes terragrunt.rendered.hcl next to the unit configuration, and --json or --format=json writes terragrunt.rendered.json. An explicit --out still takes precedence.

    sops_decrypt_file now uses the credentials your auth provider supplies

    When a run obtained credentials from --auth-provider-cmd, sops_decrypt_file ignored them for any variable already set in the environment Terragrunt started with. The rest of the run honored the auth provider, and correctly overrode any ambient environment variables. OpenTofu/Terraform received those credentials, and so did the AWS calls Terragrunt makes on a unit's behalf, such as get_aws_account_id.

    Decryption now runs as the identity Terragrunt resolved for the unit, the same one the rest of the run uses, regardless of ambient environment variables.

    info strict list <name> now honors --all

    Passing a control name to info strict list shows that control's subcontrols. Unlike the top-level listing, it ignored the --all flag and always included completed subcontrols.

    Terragrunt now applies the same rule when you name a control.

    String inputs reach modules with ${...} intact

    Passing a string input that contains ${...} to a variable declared with a type other than string used to fail with Variables not allowed, because OpenTofu/Terraform parse those values as HCL expressions and read ${...} as an interpolation. Reading a JSON or YAML file into an input hit this whenever the file happened to contain that sequence:

    inputs = {
    config = file("./config.json")
    }
    

    Terragrunt now escapes interpolation sequences in string inputs when the module declares the variable with a type that makes the value parse as HCL, so ${...} arrives as literal text instead of failing the run. Variables declared as string, and variables declared with no type at all, are read verbatim by OpenTofu/Terraform, and their values are still passed through untouched.

    ๐Ÿงช Experiments Updated

    Read dependency outputs directly from Azure state

    The dependency-fetch-output-from-state experiment can now read dependency outputs directly from Azure Storage (azurerm) state, in addition to S3. This avoids initializing the dependency and running tofu output or terraform output.

    Azure direct reads require the azure-backend experiment as well. Unsupported configurations requiring native-only authentication, endpoint, timeout, or customer-provided-key behavior continue to use the native output path.

    When a dependency has no state yet, Terragrunt uses that dependency block's mock_outputs, as it already does for S3. When Azure direct reads resolve a storage account key through Azure Resource Manager, which is the case unless access_key, sas_token, or use_azuread_auth is set, a resource_group_name, storage_account_name, or subscription_id naming a resource that does not exist fails with an error naming those keys rather than substituting mock outputs.

    Read dependency outputs directly from GCS state

    The dependency-fetch-output-from-state experiment can now read dependency outputs directly from GCS state, in addition to S3. This avoids initializing the dependency and running tofu output or terraform output.

    Unsupported GCS configurations continue to use the native output path. When a dependency has no state yet, Terragrunt uses that dependency block's mock_outputs, as it already does for S3.

    Thanks to @joshmyers for the original GCS implementation.

    render previews what an expanded dependency block expanded to

    With the block-iteration experiment enabled, a dependency block that carries an expansion block now renders as it was written, followed by the elements it expanded into, commented out and with their bodies resolved:

    $ terragrunt render --experiment block-iteration
    dependency "aurora" {
    expansion {
    for_each = toset(["web", "api"])
    }
    config_path = "../aurora-${each.key}"
    }
    # Expands to:
    #
    # dependency "aurora" {
    # config_path = "../aurora-api"
    # }
    #
    # dependency "aurora" {
    # config_path = "../aurora-web"
    # }
    

    The elements are comments because they aren't valid Terragrunt HCL configurations (you are not allowed to use the same dependency label twice in Terragrunt configurations), the previews are there to help you understand how expansion will resolve.

    โš™๏ธ Process Updates

    Go bumped to v1.27

    The version of Golang used to compile the Terragrunt binary has been updated from v1.26.6 to v1.27.0.

    If you build Terragrunt from source, or import it as a Go module, you now need a Go 1.27 toolchain.

    OpenTelemetry SDK updated to v1.45.0

    Terragrunt's OpenTelemetry tracing and metrics dependencies have been updated from v1.44.0 to v1.45.0. The logging packages and exporters have also been updated to their compatible releases, and Terragrunt now uses the v1.43.0 semantic conventions.

    Telemetry behavior is unchanged.

    Pull Requests

    โœจ Features

    • feat: Adding interactive scaffold form by @yhakbar in #6615
    • feat(azure): End to end Azure CICD by @denis256 in #6574
    • feat: Adding bare enabled to unit and stack blocks by @yhakbar in #6714
    • feat: Keying stack output addresses by iteration key by @yhakbar in #6715
    • feat: Adding render preview for expansion by @yhakbar in #6737
    • feat: remote state reading for GCP and Azure by @denis256 in #6710

    ๐Ÿ› Bug Fixes

    • fix: Strip credentials before local CLI config write by @yhakbar in #6678
    • fix: Tightening file permissions for generated files by @yhakbar in #6675
    • fix: Reject oci:// sources with docker-style name suffixes instead of resolving latest by @denis256 in #6696
    • fix: Escape interpolation in string inputs when type is verified by @yhakbar in #6685
    • fix: Fixing local copies when symlinks exist from provider caching by @yhakbar in #6684
    • fix(provider-cache): fixed handling arguments in urls by @denis256 in #6680
    • fix(cas): strip go-getter depth query parameter before invoking git by @HalisCz in #6513
    • fix(cas): use venvtest helper in depth query param tests by @denis256 in #6712
    • fix(test): fix for failing test TestNewSignalsForwarderMultipleUnix by @denis256 in #6713
    • fix: prevent nil pointer panic on invalid if_disabled by @denis256 in #6718
    • fix: Fixing --queue-construct-as resulting in empty tokenization by @yhakbar in #6720
    • fix: Fixing render --write when no --format is supplied by @yhakbar in #6724
    • fix: Preventing extremely small or large float exponents from crashing Terragrunt by @yhakbar in #6727
    • fix: Handling situation when diagnostics contain nil range by @yhakbar in #6729
    • fix: Addressing nil Range and Snippet in SourceSnippets by @yhakbar in #6731
    • fix: Propagating flag parse errors instead of swallowing them by @yhakbar in #6732
    • fix: Use the appropriate absolute path to unit config file, not basename when checking version constraints by @yhakbar in #6728
    • fix(security): upgrade Go to 1.27 by @denis256 in #6736
    • fix(test): pass config fixture to setupTest by @denis256 in #6738
    • fix: Fixing info strict list without --all by @yhakbar in #6725
    • fix: Fixing deprecated TG_HCLVALIDATE_STRICT_VALIDATE env var by @yhakbar in #6730
    • fix: Fixing hcl fmt with --stdin combined with --check and/or --diff by @yhakbar in #6726
    • fix: autoinclude config path fixes by @denis256 in #6711
    • fix: Normalize paths using ToSlash to hande old/new prefix appropriately by @yhakbar in #6744
    • fix: Fixing tofu/terraform binary selection for run --all usage by @yhakbar in #6753
    • fix: Updating render --write file permissions by @yhakbar in #6756

    ๐ŸŽ๏ธ Performance

    • perf: Refactor default --tf-path resolution by @yhakbar in #6651

    ๐Ÿ“– Documentation

    • docs: give each environment its own state backup path in the Terralith guide by @yhakbar in #6683
    • docs: serve the Google tag container first-party by @ZachGoldberg in #6699
    • docs: proxy CORS-less GTM tags so they load under Partytown by @ZachGoldberg in #6704
    • docs: Clean-up for v1.1.4 changelog by @yhakbar in #6747

    โœ… Tests

    • test(ci): validate OCI registry authentication by @denis256 in #6662

    ๐Ÿค– CI

    • ci: Add weekly security scans by @denis256 in #6691
    • ci(coverage): added support for test suppressions in weekly test report by @denis256 in #6752

    ๐Ÿงน Chores

    • chore: tests coverage increase by @denis256 in #6668
    • chore(deps): update aws-sdk-go-v2 by @denis256 in #6655
    • chore: runner pool test coverage by @denis256 in #6647
    • chore: A lot more venv plumbing by @yhakbar in #6644
    • chore(deps): bump @astrojs/vercel from 11.0.0 to 11.0.3 in /docs by @dependabot[bot] in #6658
    • chore: docs sync by @denis256 in #6676
    • chore: venv plumbing for cloud SDKs by @yhakbar in #6645
    • chore: Plumbing venv into cloud getters by @yhakbar in #6650
    • chore(deps): update OpenTelemetry SDK to v1.45.0 by @denis256 in #6686
    • chore: Resolve env var defined flags from venv by @yhakbar in #6652
    • chore: Increasing discovery boundary test coverage by @yhakbar in #6671
    • chore: Wire expansion into dependency parse by @yhakbar in #6679
    • chore: Virtualizing util.file.go functions by @yhakbar in #6653
    • chore: Upgrading go to 1.26.6 by @yhakbar in #6697
    • chore: Nesting the dependency cty map by iteration key by @yhakbar in #6689
    • chore(deps): bump github.com/moby/go-archive from 0.2.0 to 0.3.0 by @dependabot[bot] in #6705
    • chore: Increasing venv coverage further by @yhakbar in #6677
    • chore: Adding integration coverage for expanded dependencies by @yhakbar in #6693
    • chore: Use local catalog for TestCatalogWithLocalDefaultTemplate by @yhakbar in #6700
    • chore: Updating TestNewSignalsForwarderMultipleUnix to actually check for the signal by @yhakbar in #6701
    • chore: Use an injectable cap in TestPartialEval_DeeplyNestedExpressionReturnsTypedError by @yhakbar in #6702
    • chore: Moving the TestDiscovery_GraphConcurrentConfigAccessWithRacing in-memory by @yhakbar in #6703
    • chore: Wiring expansion into the unit and stack parse by @yhakbar in #6694
    • chore: Completing venv abstraction by @yhakbar in #6698
    • chore: addressing PR #6736 comemtns by @denis256 in #6741
    • chore: Adding sandboxed unit tests in CI by @yhakbar in #6742
    • chore: Isolate TestDependencyOutputSkipDependencyOutputsFlag fixtures by @yhakbar in #6740
    • chore: Preventing flakes from TestNewSignalsForwarderMultipleUnix by @yhakbar in #6750
    • chore: Adding FS sandboxed unit tests in CI by @yhakbar in #6754
    • chore: Running go fix ./... by @yhakbar in #6758
    Original source
  • Aug 13, 2026
    • Date parsed from source:
      Aug 13, 2026
    • First seen by Releasebot:
      Aug 13, 2026
    Gruntwork logo

    Terragrunt by Gruntwork

    v1.1.3

    Terragrunt ships a broad release with major bug fixes, new experiments, and smoother workflows. It improves dependency mocks, scaffold behavior, provider caching, filtering, and hooks, while adding browse-tui, bounded discovery, mutable generate output, and OCI source support.

    ๐Ÿ› Bug Fixes

    Fixed Unsupported attribute errors for values.* inputs that autoinclude overrides

    A unit input referencing a values.* key that the unit's values file doesn't define no longer fails with Unsupported attribute when an autoinclude block supplies that input. The autoinclude value is applied as intended.

    # stacks/terragrunt.stack.hcl
    unit "subnet" {
      source = "../units/subnet"
      path = "subnet"
      autoinclude {
        dependency "vpc" {
          config_path = unit.vpc.path
          mock_outputs = { vpc_id = "mock" }
        }
        inputs = {
          vpc_id = dependency.vpc.outputs.vpc_id
        }
      }
      values = {
        cidr_block = "10.0.0.0/24"
      }
    }
    # units/subnet/terragrunt.hcl
    inputs = {
      vpc_id = values.vpc_id # supplied by autoinclude, not the values file
      cidr_block = values.cidr_block # still resolves from values file
    }
    

    Fixed overwrite_terragrunt and remove_terragrunt on files with no trailing newline

    generate blocks using if_exists = "overwrite_terragrunt" or if_disabled = "remove_terragrunt" failed to properly handle existing files when the file at the target path had no newline after its first line, empty files included.

    Terragrunt now properly handles files like this, so a file carrying the Terragrunt signature is overwritten or removed as configured, and a file without it produces the usual error naming the path Terragrunt would not touch.

    Dependency mock_outputs apply when the state bucket doesn't exist yet

    When reading a dependency's outputs directly from remote state (--dependency-fetch-output-from-state), Terragrunt fell back to mock_outputs only when the state object was missing, not when the S3 bucket itself didn't exist. A dependency on an environment that hadn't been bootstrapped yet would fail instead of using its mocks.

    A missing bucket is now treated the same as a missing state object, so commands like plan and validate can resolve mocks before the dependency's backend has been created.

    Source permissions preserved on hidden directories copied by include_in_copy

    With the fast-copy strict control enabled, a hidden directory that Terragrunt copied due to include_in_copy matching something within it took the permissions of the first file generated within it, instead of the permissions it had in the source.

    Those directories now keep their source permissions, matching the copy Terragrunt performs with the control disabled.

    Applied the positive half of a filter that begins with a negation

    When a --filter query began with a negation, Terragrunt treated the whole query as an exclusion. The expressions chained after the negation stopped restricting the selection and only narrowed what got subtracted, so components matching none of them came back in the results. Those expressions are now applied.

    $ terragrunt list
    bar baz foo
    $ terragrunt list --filter '!name=foo | name=bar'
    bar baz foo
    $ terragrunt list --filter '!name=foo | name=bar'
    bar
    

    This follows the left-to-right refinement that | has everywhere else: each expression narrows what the one before it selected. A query is only treated as an exclusion when every one of its expressions is negated, such as '!name=foo' or '!name=foo | !name=bar'.

    See Combining Expressions for how negation, intersection and union interact.

    Fixed a race condition that left cached provider archives in the working directory

    With the provider cache server enabled via --provider-cache, a race let the server start responding to requests before it had finished preparing the directories it caches into. A provider requested in that window had its archive and lock file written relative to the working directory instead of into the cache, leaving zip files behind in your project.

    That race condition has been fixed. Providers now always download into the cache directory.

    Fixed a race condition between concurrent Terragrunt runs downloading providers

    A race condition in the logic used to synchronize provider downloads meant that two Terragrunt runs on the same machine could interfere with each other while caching the same provider. Each run staged its downloads at the same path, so a run that finished first could delete an archive another run was still unpacking, failing that run with failed to open zip archive.

    That race condition is now fixed. Two runs can cache the same provider at the same time.

    Fixed space-delimited flag values in providers lock

    The space-delimited form, providers lock -platform linux_amd64, now reaches OpenTofu and Terraform intact. Previously it was the attached form, -platform=linux_amd64, that worked: given the value as a separate argument, Terragrunt moved it to the end of the command, where it was read as a provider address and the run failed with Invalid provider type "linux_amd64".

    -fs-mirror and -net-mirror were moved the same way, and now keep their values too.

    With --provider-cache enabled, platforms are also split correctly across the per-platform providers lock runs used to warm the cache.

    Fixed scaffold on units and stacks

    terragrunt scaffold read every source as an OpenTofu/Terraform module. Given a unit or a stack, which are Terragrunt configurations rather than OpenTofu/Terraform modules, it exited successfully having written an invalid terragrunt.hcl file.

    Units and stacks are now scaffolded the way the Catalog TUI scaffolds them: their files are copied into the working directory for you to edit in place, along with a terragrunt.values.hcl listing every values.* reference the configuration makes.

    terragrunt scaffold 'github.com/gruntwork-io/terragrunt-scale-catalog//units/aws/oidc/iam-oidc-role'
    

    Copying refuses to overwrite: a file that would land on an existing path stops the command before anything is written. Modules and templates are unaffected and are still scaffolded from their variables.

    See Scaffold for what gets copied and how the values file is filled in.

    Answered every prompt when input is piped in

    A run that asks for confirmation more than once, such as terragrunt backend delete prompting for both the lock table entry and the state object, used to read only the first answer when the answers were piped in rather than typed. The remaining answers were discarded while reading ahead, and the next prompt failed with an end-of-input error. Every prompt in a run now reads from the same input, so piping yes for each one works.

    Stack dependencies honor mock_outputs with --dependency-fetch-output-from-state

    A dependency block that reads outputs from a stack (its config_path points at a terragrunt.stack.hcl directory) used to fail when a unit in that stack had no state yet, even when the dependency declared mock_outputs. This blocked commands like plan and validate against a stack that hadn't been applied.

    Such a dependency now falls back to mock_outputs for the units that have no state yet. In a partially applied stack, applied units resolve to their real outputs while the rest use their mocks.

    Mocks for a stack dependency are keyed by unit name, so mock_outputs has to be a map or object. Declaring it as any other type now reports that directly, instead of leaving the units it can't cover out of the stack outputs.

    Fixed --config= being ignored by the tflint hook

    The built-in tflint hook reads the configuration file out of the arguments you give it, then uses that path for tflint init and for the lint run. It only recognized the space-separated --config spelling, so a hook written as:

    before_hook "tflint" {
      commands = ["plan"]
      execute = ["tflint", "--config=custom.tflint.hcl"]
    }
    

    was treated as though no configuration file had been named at all. Terragrunt searched the unit directory and its parents for a .tflint.hcl file instead, and either failed with a config-not-found error or ran tflint init against whatever unrelated configuration the search turned up. Terragrunt now recognizes --config , --config=, -c , and -c=.

    The hook also builds --var arguments from the unit's inputs and from TF_VAR_ entries in extra_arguments blocks. Those arguments came out in a different order on every run, which made the logged command line, and anything comparing it between runs, needlessly unstable. They are now ordered by variable name.

    ๐Ÿงช Experiments Added

    block-iteration experiment reserves the expansion block

    The block-iteration experiment has been added as the gate for iterating a dependency, unit, or stack block over a count or for_each, declared through a nested expansion block, along with an enabled attribute on unit and stack blocks.

    In this release the flag is reserved only, and enabling it has no behavioral effect. Writing an expansion block without the experiment now reports an error naming the flag, rather than leaving the block to be silently discarded:

    the unit "app" block in /path/to/terragrunt.stack.hcl uses an expansion block, which requires the 'block-iteration' experiment; enable it with --experiment block-iteration
    

    Track progress and share feedback in #4504.

    bounded-discovery โ€” Added a directory boundary for graph traversal

    Filter expressions that traverse the dependency graph reach beyond the working directory: dependents (--filter '...{unit}') by walking up to the Git repository root, dependencies (--filter '{unit}...') by following declared paths. Either way, Terragrunt reads and parses every configuration it touches. In monorepos with isolated environments, that traversal can fail or do wasted work reading sibling environments.

    Enable the new bounded-discovery experiment to set a boundary for that traversal. The --discovery-boundary flag (env: TG_DISCOVERY_BOUNDARY) replaces the Git repository root as the enclosure for a whole run:

    cd environments/staging
    terragrunt run --all plan --experiment bounded-discovery --filter '...{vpc}' --discovery-boundary .
    

    The experiment also unlocks an inline (dir) boundary operand, which bounds a single expression and overrides the flag. It occupies the same slot as a traversal depth, so it bounds discovery by location the way a number bounds it by graph hops:

    cd environments/staging
    terragrunt run --all plan --experiment bounded-discovery --filter '(.)...{vpc}'
    

    Any configuration that resolves outside the boundary, whether a dependent or a dependency, is not read, parsed, or returned: find does not list it and run --all does not run it. Configurations inside the boundary are discovered as usual.

    The boundary must be an existing directory, and relative paths are resolved against the working directory. Dependent traversal searches upward from the working directory, so filters that use it also need the boundary to be the working directory or one of its parents. Dependency traversal follows declared paths from the units a filter matched, so dependency-only filters accept any directory, including one below the working directory:

    # From the repository root, follow app's dependencies but keep them within prod
    terragrunt find --experiment bounded-discovery --filter '{./prod/app}...' --discovery-boundary ./prod
    

    Reserving ( and ) for the boundary operand changes how --filter reads those characters everywhere, not only when the experiment is enabled. An expression such as --filter '1...(foo | bar)' previously matched a unit literally named (foo or bar); it is now rejected as a malformed boundary. Wrap a name or path containing parentheses in braces (e.g. --filter '{./weird(name)}') to keep it literal.

    browse-tui โ€” Added an interactive browser for your estate

    The new browse-tui experiment adds the terragrunt browse command. With the experiment enabled, terragrunt browse opens a three-column Terminal User Interface (TUI) browser of your infrastructure estate: the parent directory on the left, the current directory in the middle, and a detail pane on the right showing metadata for the highlighted unit, stack, or directory. The browser opens immediately and fills in metadata as discovery completes in the background.

    Enable it with --experiment browse-tui or TG_EXPERIMENT=browse-tui. See the experiment documentation for the keybindings, search, and the criteria for stabilization.

    mutable-generate โ€” Deduplicated generate block output

    The mutable-generate experiment has been added. With it enabled, the contents a generate block produces are stored in the Content Addressable Store (CAS), and the file written at path is a read-only link to that stored copy rather than a file of its own.

    Since the stored copy is addressed by the hash of its contents, anything generating identical contents links to the same copy. A generate block inherited by several hundred units therefore costs one copy in .terragrunt-cache rather than several hundred.

    The link is read-only because that copy is shared. Where a generated file does need to be edited in place, a new mutable attribute on the generate block gives it a writable file of its own:

    generate "provider" {
      path = "provider.tf"
      if_exists = "overwrite"
      mutable = true
      contents = "..."
    }
    

    Setting mutable without the experiment enabled is an error, since earlier Terragrunt versions reject the attribute. The CAS is required, so --no-cas writes generated files directly and mutable has no effect.

    For details, see the experiment documentation.

    optional-dependency-outputs โ€” Added --no-dependency-outputs flag to skip dependency output resolution

    Added a --no-dependency-outputs flag that skips all dependency output resolution globally, mirroring the existing skip_outputs = true attribute on individual dependency blocks.

    The feature is gated behind the optional-dependency-outputs experiment:

    TG_EXPERIMENT=optional-dependency-outputs terragrunt run --no-dependency-outputs -- init
    

    Using --no-dependency-outputs without enabling the optional-dependency-outputs experiment will return an error.

    Thanks to @pjrm for contributing this feature!

    ๐Ÿงช Experiments Updated

    catalog-format โ€” Added reading the catalog as JSON Lines

    The catalog command draws a terminal user interface, and refuses to start where there is no terminal to draw it on. With the catalog-format experiment enabled, --format=jsonl writes the same discovery to standard output instead, as one JSON object per line:

    terragrunt catalog --experiment=catalog-format --format=jsonl | jq -c '{kind, title, component_source}'
    

    Entries are written as they are discovered rather than collected first, so output is readable while the remaining repositories are still loading, and a reader that stops early ends the command quietly:

    terragrunt catalog --experiment=catalog-format --format=jsonl | head -5
    

    Note

    Closing the pipe

    In this example, the head program exits after reading in five lines, and Terragrunt detects the SIGPIPE signal from the OS, and shuts down cleanly.

    Entries appear in discovery order, which interleaves the repositories being loaded and differs between runs. Every entry carries the complete body of the component's README in the doc field. Combine usage of Terragrunt with other tools like jq to drop it.

    terragrunt catalog --experiment=catalog-format --format=jsonl | jq -c 'del(.doc)'
    

    Entries follow a published JSON schema. For the fields and their meanings, see Non-interactive catalog.

    --format=tui is the default, and leaves the terminal user interface exactly as it was.

    catalog-format โ€” Added reading the catalog as Markdown

    The catalog-format experiment gains a second non-interactive format. Where --format=jsonl writes a record per catalog entry for a program to parse, --format=md writes one Markdown document for a person or an agent to read:

    terragrunt catalog --experiment=catalog-format --format=md &gt; catalog.md
    

    Each entry becomes a section holding the metadata the catalog user interface shows for it, the source the component is scaffolded from, and the component's README. Sections are written as entries are discovered, so the document is readable while the remaining repositories are still loading.

    READMEs are reproduced inside fenced blocks, so the headings one carries are not read as sections of the catalog document. The document closes with a table naming every component it holds and a count of what was discovered, which is how a reader tells a complete document from one that was cut short by a consumer that stopped reading.

    For the fields each section carries, see Non-interactive catalog.

    oci โ€” Added OCI sources for stack units and stacks

    terragrunt.stack.hcl now accepts oci:// sources in unit and stack blocks, so a stack can pull its components straight from an OCI registry. Without the oci experiment enabled, such a source fails with a clear error instead of an unsupported-scheme failure.

    oci โ€” Added OpenTofu CLI-config credentials for OCI module sources

    oci:// module downloads now read OpenTofu's CLI-config credentials, so one configuration serves both OpenTofu and Terragrunt.

    Terragrunt honors the oci_credentials "[/]" blocks (username and password, OAuth tokens, or a docker_credentials_helper, which like tofu may only be set on a whole registry) and the oci_default_credentials fallback helper. A TF_CLI_CONFIG_FILE or TERRAFORM_CONFIG value selects the config file outright; otherwise Terragrunt reads the first of ~/.tofurc and ~/.terraformrc that exists, and merges the *.tfrc and *.tfrc.json files in OpenTofu's config directory.

    Terragrunt picks the most specific matching source across CLI config and ambient Docker config; an explicit CLI-config entry wins when both match equally. Set discover_ambient_credentials = false in the oci_default_credentials block to use CLI config only.

    โš™๏ธ Process Updates

    Go bumped to v1.26.5

    The version of Golang used to compile the Terragrunt binary has been updated from v1.26.0 to v1.26.5.

    Thanks to @apoiget for contributing this upgrade!

    Pull Requests

    โœจ Features

    • feat: Adding graph boundary via () syntax by @yhakbar in #6365
    • feat: Adding --discovery-boundary flag by @yhakbar in #6355
    • feat(getter): OpenTofu CLI-config credentials for oci:// sources by @denis256 in #6531
    • feat: Adding browse by @yhakbar in #6219
    • feat: Adding mutable attribute to the generate block by @yhakbar in #6563
    • feat: Adding md format for catalog by @yhakbar in #6608
    • feat: Add --skip-dependency-outputs flag to skip dependency output resolution by @pjrm in #6422

    ๐Ÿ› Bug Fixes

    • fix(providercache): log -lockfile=readonly skip at debug level by @bryanhorstmann in #6577
    • fix: Fixing handling of EOF in generate blocks by @yhakbar in #6592
    • fix: Fixing the --config= form of flags used in the tflint hook by @yhakbar in #6591
    • fix: Fixing fast-copy ancestor directory permissions by @yhakbar in #6593
    • fix: Addressing providers lock -platform usage with space delimited values by @yhakbar in #6597
    • fix: Fixing bug with negation | positive expression in the same query. by @yhakbar in #6598
    • fix: Fixing provider cache server archive dir race by @yhakbar in #6620
    • fix: Fixing scaffold on units and stacks by @yhakbar in #6607
    • fix: Addressing feedback from #6565 and #6605 by @yhakbar in #6628
    • fix: autoinclude values override for inputs by @denis256 in #6626
    • fix: Fixing md format catalog escaping by @yhakbar in #6638
    • fix: Plumbing through evalCtx for discovery boundary by @yhakbar in #6632
    • fix: Fixing stack dependency mock outputs by @yhakbar in #6530
    • fix: Fixing issue where dependency mock outputs aren't used when bootstrapping hasn't run yet. by @yhakbar in #6534

    ๐ŸŽ๏ธ Performance

    • perf: Reducing allocations in tree parse by @yhakbar in #6648

    ๐Ÿ“– Documentation

    • docs: Add call out for terragrunt scale in quick start by @yhakbar in #6583
    • docs: document oci module sources, authentication, and caching by @denis256 in #6636
    • docs: Cleaning up changelog for v1.1.3 by @yhakbar in #6669
    • docs: Cleaning up experiment docs by @yhakbar in #6627
    • docs: address review feedback on the oci and autoinclude docs by @denis256 in #6643

    โœ… Tests

    • test(getter): integration tests against a local OCI distribution registry by @denis256 in #6614
    • test: prove oci module portability between tofu and terragrunt by @denis256 in #6629
    • test(git): add unit coverage for internal/git command wrappers and parsers by @denis256 in #6661

    ๐Ÿงน Chores

    • chore: Pin exact provider versions for terralith to terragrunt guide by @yhakbar in #6578
    • chore: Using vfs handle for ParseFromFile by @yhakbar in #6561
    • chore: Walk in discovery with vfs by @yhakbar in #6564
    • chore: Registring catalog-format experiment by @yhakbar in #6582
    • chore(deps): update AWS, Azure, GCP SDKs by @denis256 in #6590
    • chore: Continuing clean-up of go test ./... on a fresh clone of the repo by @yhakbar in #6553
    • chore: Fixing usage of deprecated aws sdk by @yhakbar in #6600
    • chore: Cleaning up profile tests per feedback in #6553 by @yhakbar in #6599
    • chore: Clean-up by @yhakbar in #6584
    • chore: Addressing feedback from #6365 and #6355 by @yhakbar in #6603
    • chore: Register the block-iteration experiment by @yhakbar in #6562
    • chore: address review feedback from #6531 by @denis256 in #6609
    • chore: Addressing flake in TestCatalogJSONLFormatCleansUpOnEarlyExit by @yhakbar in #6613
    • chore: updated TestDiscovery_GraphConcurrentConfigAccessWithRacing to use VFS by @denis256 in #6622
    • chore: Adding expansion detection and internal expansion logic by @yhakbar in #6565
    • chore: Adding expansion blocks to the configs that accept expansion by @yhakbar in #6605
    • chore: Threading venv through getters and hcl fmt by @yhakbar in #6621
    • chore: Addressing feedback from #6621 by @yhakbar in #6633
    • chore: Fixing experiment tag in sidebar by @yhakbar in #6635
    • chore: Updating mem exec so that it fails closed by @yhakbar in #6634
    • chore: Gate real hg usage test behind the exec build flag by @yhakbar in #6637
    • chore: Replacing aws provider with null provider in init-cache fixture by @yhakbar in #6639
    • chore: Cleaning up NewParsingContext constructor by passing in venv as a param by @yhakbar in #6630
    • chore: Addressing lint finding by @yhakbar in #6649
    • chore: Refactoring markdown deps into internal/md by @yhakbar in #6640
    • chore: Adding unit tests for internal packages by @denis256 in #6660
    • chore: Bumping Go to 1.26.5 (#6664) by @apoiget in #6666
    • chore: Dropping stale tree parse test case by @yhakbar in #6672
    Original source
Releasebot

Curated by the Releasebot team

Releasebot is an aggregator of official release notes from hundreds of software vendors and thousands of sources.

Our editorial process involves the manual review and audit of release notes procured with the help of automated systems.