terragrunt
terragrunt copied to clipboard
How should I apply a terrform root module construct using terragrunt?
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.).
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?
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.