terragrunt icon indicating copy to clipboard operation
terragrunt copied to clipboard

How should I apply a terrform root module construct using terragrunt?

Open sonic1981 opened this issue 1 year ago • 0 comments

I'm working on a terragrunt code base for the first time, having used terraform a lot in the past without terragrunt. I'm a bit confused as to the structure terragrunt seems to enforce. I would usually organise my terraform thus:

main.tf
--> module
    main.tf
--> module2
    main.tf

This is listed as best practice on the terraform docs:

The Root Module

Terraform always runs in the context of a single root module. A complete Terraform configuration consists of a root module and the tree of child modules (which includes the modules called by the root module, any modules called by those modules, etc.).

Source

But none of the terragrunt structures seem to represent this. It seems to be designed so that each module is independent and run using the run-all command.

This seems problematic to me, from the existing code base I can see that this initialises terraform for every module and I'd say causes issues with sharing secrets between modules. So I'd prefer to work with one root module and multiple child modules.

I can't find a terragrunt pattern that will allow me to do this?

I'm also confused as to how this responsibility is decomposed, do I actually structure my terraform (as above) or do I need an extra root .hcl file?

I'm after something a little like this I guess

└── live
    ├── prod
    │   ├── terragrunt.hcl
    │   ├── app
    │   │   └── terragrunt.hcl
    │   ├── mysql
    │   │   └── terragrunt.hcl
    │   └── vpc
    │       └── terragrunt.hcl
    ├── qa
    │   ├── terragrunt.hcl
    │   ├── app
    │   │   └── terragrunt.hcl
    │   ├── mysql
    │   │   └── terragrunt.hcl
    │   └── vpc
    │       └── terragrunt.hcl
    └── stage
        ├── terragrunt.hcl
        ├── app
        │   └── terragrunt.hcl
        ├── mysql
        │   └── terragrunt.hcl
        └── vpc
            └── terragrunt.hcl

But this example just talks about specifying the provider block and nothing about a root main.tf. So I'm lost?

sonic1981 avatar Aug 05 '22 11:08 sonic1981

I believe that you are mismatching some concepts here.

Module structure Let's first start with the terraform bit here. Guessing that your first code snippet is in directory layout i'll rewrite it a bit

└── my-terraform-project
    ├── my-module-a
    │   ├── my-submodule-a
    │   │   ├── ...
    │   │   └── main.tf <-- submodule main.tf
    │   ├── ...
    │   ├── variables.tf
    │   └── main.tf <-- module main.tf
    ├── my-module-b
    │   └── ..
    └── main.tf <-- root main.tf

If you want to declare module usage then usually this is written in the main.tf file. All calls should be top-to-bottom so that parents always declare child but not vice versa. That means the module main.tf can only declare the submodule main.tf . In a best case-scenario always the direct inheritance is allowed so that the root main.tf must never declare the submodule main.tf but can so declaring the module main.tf. Just consider this example of an AWS IAM module stack.

Terragrunt structure Just as you have guessed you will need an additional layer to control the deployment using terragrunt. For this you can now drop the root main.tf because it is not bound to any module logic and should only declare your modules for usage. Your terraform project may now only contain directories of modules which are abstracting the logic and allow for generic usage by providing input value through variables.

You should now consider to create a terragrunt directory to layout your deployment structure such as mentioned in the second code snippet.

└── my-terragrunt-project
    ├── prod <-- stage
    │   ├── terragrunt.hcl <-- stage information
    │   ├── my-module-a
    │   │   └── terragrunt.hcl <-- declaration of my-module-a usage
    │   └── my-module-b
    │       └── terragrunt.hcl <-- declaration of my-module-b usage
    ├── dev
    │   ├── terragrunt.hcl
    │   ├── my-module-a
    │   │   └── terragrunt.hcl
    │   └── my-module-b
    │       └── terragrunt.hcl
    └── root-terragrunt.hcl

There are different approaches to this layout depending on your deployment target (such as AWS, Google, Azure, ...) and its logic to separation of concerns. This one reflects a typical staging approach. Each stage contains at least one terragrunt.hcl file describing information about the stage. It must only contain variable or provider information for the deployment. The second level contains module information which previously would be part of a main.tf file. You will need to describe which module should be "loaded" and provide inputs to it. Like from the example:

terraform {
  # Deploy version v0.0.1 in prod
  source = "git::[email protected]:foo/modules.git//app?ref=v0.0.1"
}

inputs = {
  instance_count = 10
  instance_type  = "m2.large"
}

Hope this helps you a bit understanding. If i missed you question just let me know. It can be hard to describe what is missing sometimes.

maunzCache avatar Aug 17 '22 13:08 maunzCache