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.04k stars 975 forks source link

[Question] multiple module instances best practices #1441

Closed TheKangaroo closed 3 years ago

TheKangaroo commented 3 years ago

Hi all, I looked around in the terragrunt doc's and issues and stumbled over https://github.com/gruntwork-io/terragrunt/issues/781, https://github.com/gruntwork-io/terragrunt/issues/1156 and https://github.com/gruntwork-io/terragrunt/issues/957 which all link to https://github.com/gruntwork-io/terragrunt/issues/759 and I've read all of them. I'm still a bit confused and I think these Issue mix up slightly different problems and try to find the one solution for all of these problems. I just want to drop my solution here as well, to ask, if this is the/an intended way to use terragrunt. My goal is to provide "Azure Managed Instances as a Service" and therefore I need to deploy multiple Managed Instances in multiple environments (dev and prod). My terraform module for one environment used to be like this:

.
├── README.md
├── main.tf
├── modules
│   ├── infra
│   │   ├── import.tf
│   │   ├── infra.tf
│   │   └── variables.tf
│   └── managedinstance
│       ├── managedinstance.tf
│       ├── mi.json
│       └── variables.tf
├── modules.tf
└── variables.tf

I defined an instance of the infra module and multiple instances of the managedinstance module in the modules.tf file.

Now I wanted to deploy this setup to my dev environment and the same deployment with a different set of instances of the managedinstance module to my prod environment.

I created an terragrund structure like:

.
├── environments
│   ├── common.tfvars
│   ├── dev
│   │   ├── modules.tf
│   │   ├── stage.tfvars
│   │   └── terragrunt.hcl
│   ├── prod
│   │   ├── modules.tf
│   │   ├── stage.tfvars
│   │   └── terragrunt.hcl
│   └── terragrunt.hcl
└── modules
    ├── infra
    │   ├── import.tf
    │   ├── infra.tf
    │   └── variables.tf
    ├── managedinstance
    │   ├── import.tf
    │   ├── managed_instance.tf
    │   ├── mi.json
    │   └── variables.tf
    ├── main.tf
    └── variables.tf

Things to notice here:

}


* the modules.tf files in the stage folders (dev and prod) still do the same job like before, specifying multiple instances of the managedinstance module and one instance of the infra module.
* I needed to create a main.tf and variables.tf in the modules folder to specify azurerm provider, empty backend config and some variables.

That's basically how I achieved multiple instances from a  module with terragrunt, which are diffrent in dev and prod.

So the question is: Is terragrunt intended to use this way? I can't think of an better solution.
brikis98 commented 3 years ago

Terragrunt and Terraform allow you to solve problems in many different ways, each with their own trade offs, so there's no one true way to do things. Without deeply understanding your use case and requirements, it's hard to say what will work best.

That said, the most common usage of Terragrunt is to have solely a single terragrunt.hcl to deploy a single module per environment (whereas you seem to have a terragrunt.hcl and several .tf files and even .tfvars files). The idea is to encapsulate all the logic you can in your modules repo (or in your case, modules folder), and for the terragrunt.hcl files to be providing different inputs = { ... } to that module in different environments, so there's no need for additional .tf files in each environment. See https://github.com/gruntwork-io/terragrunt-infrastructure-live-example for a simple example.

TheKangaroo commented 3 years ago

Thank you, I actually refactored the code, to a structure without .tf files outside of the modules and only terragrunt files:

.
├── README.md
├── environments
│   ├── dev
│   │   ├── SQL-MI-01
│   │   │   └── terragrunt.hcl
│   │   ├── infra
│   │   │   └── terragrunt.hcl
│   │   └── subscription.hcl
│   ├── prod
│   │   ├── SQL-MI-01
│   │   │   └── terragrunt.hcl
│   │   ├── SQL-MI-02
│   │   │   └── terragrunt.hcl
│   │   ├── infra
│   │   │   └── terragrunt.hcl
│   │   └── subscription.hcl
│   └── terragrunt.hcl
└── modules
    ├── infra
    │   ├── import.tf
    │   ├── infra.tf
    │   ├── main.tf
    │   └── variables.tf
    └── instance
        ├── import.tf
        ├── main.tf
        ├── managed_instance.tf
        ├── mi.json
        └── variables.tf

Creating a new instance means creating a new folder with the terragrunt.hcl file in it. Even though this is slightly more work than typing a module block into a .tf file this seams to be more of the "intended terragrunt way" of doing things. Thanks for your thoughts on this @brikis98