Terragrunt: Add option to only check top level dependency blocks

Created on 26 Mar 2020  路  8Comments  路  Source: gruntwork-io/terragrunt

Current behavior

If I have a module named MainModule with two dependency blocks, and those two dependencies each depend on a few other modules, and I run a terragrunt plan on MainModule, terragrunt will run a terraform output -json to check the state of all modules described in this scenario, not just on the two top level dependencies.

What I don't love about this

It is slower

In my real world terragrunt config, we have hundreds of modules, some of which have over a dozen dependencies, each of which have their own. Even with provider caching, I still see cases where a terragrunt plan takes over ten minutes because it checks all of the state in series without parallelization.

It goes against my intuition of the permissions model

This one is highly opinionated, so I only mean it as a suggestion. But say I have the following modules:

  • DevOrg module, which creates an AWS organization for dev resources
  • ProdOrg module, which creates an AWS organization for prod resources
  • CommonEcr module, which creates a Docker repo on AWS Ecr with read access from DevOrg and ProdOrg
  • EcsDevModule, which depends on that ECR module to get the url of the docker image to use

Running a plan on EcsDevModule would have a deep level dependency on being able to access the Prod env state, which I do no like. Instead, I would prefer if I only had to give access to CommonsOrg state.

enhancement help wanted

Most helpful comment

Just tested it out and it's working wonderfully!

Screen Shot 2020-08-26 at 12 23 05 AM

Pictured is a module with ~100 nested dependencies, but only the 11 declared dependencies that you see in the output before the state lock is acquired 馃槃

When I use v0.23.34 without this optimization, you can see that there are many more dependencies:

Screen Shot 2020-08-26 at 12 29 21 AM

The total time to run this module is down from 2m13s to 40s, but there's probs 15ish seconds just to aquire the state lock and refresh the state, so this is extremely efficient now in my mind.

Thank you so much for all your work on this!

All 8 comments

That's a fair point. This happens because getting the output calls RunTerragrunt here, and that function is currently parsing the target config in full.

The implementation to fix this is a bit involved though because it means we need to update the RunTerragrunt routine to do "just-in-time" config parsing. That is, it needs to index all the terraform commands and construct a mapping so that it only parses the relevant pieces of the config for each command. E.g., output may only need the terraform block, while apply needs all the inputs, unless it has a plan file (you can see how this can get complicated).

Note that we can't just parse out the terraform block for calling output and be done with it. The crux of the issue is parsing out the dependency blocks from the target config, and avoiding that requires an update to the configuration parsing mechanism. Right now, we parse dependency blocks before terraform blocks (see https://terragrunt.gruntwork.io/docs/getting-started/configuration/#configuration-parsing-order) because we want to support the usage of dependency in setting hooks and extra_arguments and the like. Updating that means we need some kind graph based parsing mechanism so that we only parse dependency blocks if it is used: a very involved refactor!

I'd be willing to overhaul the configuration parsing mechanism, but right now we are buried with other initiatives to take on this. If anyone else in the community wants to take a crack at this, please submit an RFC detailing the approach given the changes involved.

Awesome, thanks for the write up @yorinasub17! I won't be able to work on an RFC in the near future, but will keep this in my back log just in case I ever have time.

I came up with a potential intermediate solution that I hope will cover 80% of use cases. Check out the PR description for more info: https://github.com/gruntwork-io/terragrunt/pull/1311

I think that is an excellent solution!

The intermediate optimization is now released as https://github.com/gruntwork-io/terragrunt/releases/tag/v0.23.35 (binaries will show up shortly).

Keeping this issue open as this update does not allow this optimization for all use cases. In particular, we've been directing folks to start using generate blocks to manage state backends that are not s3 or gcs, which means those folks won't benefit from the optimization.

Will keep this ticket open until we can support generate block based backend configuration.

Just tested it out and it's working wonderfully!

Screen Shot 2020-08-26 at 12 23 05 AM

Pictured is a module with ~100 nested dependencies, but only the 11 declared dependencies that you see in the output before the state lock is acquired 馃槃

When I use v0.23.34 without this optimization, you can see that there are many more dependencies:

Screen Shot 2020-08-26 at 12 29 21 AM

The total time to run this module is down from 2m13s to 40s, but there's probs 15ish seconds just to aquire the state lock and refresh the state, so this is extremely efficient now in my mind.

Thank you so much for all your work on this!

That's great to hear @dmattia ! Thanks for sharing!

Was this page helpful?
0 / 5 - 0 ratings