gruntwork-io / terragrunt

Terragrunt is a flexible orchestration tool that allows Infrastructure as Code written in OpenTofu/Terraform to scale.
https://terragrunt.gruntwork.io/
MIT License
8.01k stars 971 forks source link

Add option to only check top level dependency blocks #1103

Open dmattia opened 4 years ago

dmattia commented 4 years ago

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:

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.

yorinasub17 commented 4 years ago

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.

dmattia commented 4 years ago

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.

yorinasub17 commented 4 years ago

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

dmattia commented 4 years ago

I think that is an excellent solution!

yorinasub17 commented 4 years ago

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

yorinasub17 commented 4 years ago

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.

dmattia commented 4 years ago

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!

yorinasub17 commented 4 years ago

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