-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Identical .hcl files in too many places. how to make .hcl files DRY? #1451
Comments
Terraform and Terragrunt are flexible tools, with many ways to use them, each with different trade-offs. If you want to make things more DRY, there are multiple ways to do this. Here's an example I've been playing around with. Let's say you have your Terraform code in a
To deploy these modules across all your environments (e.g.,
This looks similar to what you had originally, but the If you open up, for example, locals {
# abspath(".") should be something like <PATH>/live/<ENV>/<REGION>/<MODULE>
parsed_path = regex(".*/live/(?P<env>.*?)/(?P<region>.*?)/(?P<module>.*)", abspath("."))
env = local.parsed_path.env
region = local.parsed_path.region
module = local.parsed_path.module
# Centrally manage CIDR blocks
cidr_block = {
dev = "10.0.0.0/16"
stage = "10.10.0.0/16"
prod = "10.20.0.0/16"
}
# Centrally manage what version of the VPC module is used in each environment. This makes it easier to promote
# a version from dev -> stage -> prod.
module_version = {
dev = "v1.2.4"
stage = "v1.2.3"
prod = "v1.2.3"
}
}
terraform {
source = "github.com/<org>/modules.git//vpc?ref=${local.module_version[local.env]}"
}
inputs = {
aws_region = local.region
name = "vpc-${local.env}"
cidr_block = local.cidr_block[local.env]
} This file contains all the logic of how to deploy your VPC module in your live environments. To use it, here's what include {
path = "../../../env/vpc/terragrunt.hcl"
} That's it! Those child Similarly, here's what locals {
# abspath(".") should be something like <PATH>/live/<ENV>/<REGION>/<MODULE>
parsed_path = regex(".*/live/(?P<env>.*?)/(?P<region>.*?)/(?P<module>.*)", abspath("."))
env = local.parsed_path.env
region = local.parsed_path.region
module = local.parsed_path.module
# Centrally manage what version of the VPC module is used in each environment. This makes it easier to promote
# a version from dev -> stage -> prod.
module_version = {
dev = "v1.2.4"
stage = "v1.2.3"
prod = "v1.2.3"
}
}
terraform {
source = "github.com/<org>/modules.git//mysql?ref=${local.module_version[local.env]}"
}
dependency "vpc" {
config_path = "../vpc"
}
inputs = {
aws_region = local.region
name = "mysql-${local.env}"
vpc_id = dependency.vpc.outputs.vpc_id
subnet_ids = dependency.vpc.outputs.subnet_ids
} And here's what include {
path = "../../../env/mysql/terragrunt.hcl"
}
inputs = {
instance_type = "db.t3.micro"
} Whereas include {
path = "../../../env/mysql/terragrunt.hcl"
}
inputs = {
instance_type = "db.m4.large"
} So now the child # (... rest of the file omitted for simplicity...)
locals {
instance_types = {
dev = "db.t3.micro"
stage = "db.t3.large"
prod = "db.m4.large"
}
}
inputs = {
instance_type = local.instance_types[local.env]
# (... other inputs omitted for simplicity...)
} There are many ways to put all these pieces together. Play around with it to see what works best for you! |
Thanks @brikis98 ! this is helpful kind of along the lines of what I was looking for. I also played with using the cli option something like:
and then running form within Also in either option how do I control the versioning of the |
It seems simpler to me to put a
The vars that change should be in the local |
Just a small usability thought: Many users are going to miss the idea that additional inputs are coming from an include earlier in the file (i.e. In general, I wish we could make it more explicit where all the inputs come from in a given |
I think this will be addressed with imports. The imports feature also gives us some flexibility around breaking up the sources of Unfortunately, we haven't been able to properly prioritize that feature since we did the design work (primarily because of COVID impacting my availability for terragrunt contributions), but perhaps this will be the driving force for it to be prioritized with some of our feature work? |
Ah very nice! Yep, that exactly addresses my concern. In practice, I found that when customers would look at a "leaf" |
This is a nice idea and I like the marker idea. This falls on me when I have a root terragrunt.hcl file and wanting to have multiple includes. I have terraform and hcl in same repo:
If you open up, for example, env/vpc/terragrunt.hcl, i have a include at the top. this would allow me to have backend s3 config in root terragrunt.hcl:
as you know, i receive "Only one level of includes is allowed." haha and yes A key feature here is the use of the path_relative_to_include to monkey patch the S3 key of the parent config based on who is importing. |
@brikis98 To piggyback on @hotdawg789 's dilemma, how can I use your example layout and still use Terragrunt's backend configuration options and keep that DRY? Many of the examples available have inheriting the backend and provider configs from a root 'terraform.hcl' file, but in this layout, we cannot reliably use find_in_parent_folders(), if I'm understanding your example correctly. Cheers! |
As I alluded to above, a key building block for really making this work is the |
The original post is a good question, and unless I read of a good response from terragrunt, I see one answer as to simply stop using terragrunt. It does not fit this use case very well at all. I'd love to stop using terragrunt myself. Not a fan. |
We have now implemented enough of the Beyond that, I believe #759 is the next step to further DRY things up. Closing this as the original question has been answered. If there are any follow ups, please open a new ticket with updated context. Thanks! |
I came up with the usage of symbolic links to avoid duplicated |
We have a repo structure like so:
All 4 of these
terragrunt.hcl
files are identical. The only differences are in themain.yaml
which is used to feed the variables in theterragrunt.hcl
. If I want to change anything in theterragrunt.hcl
I have to do that in 4 different locations. There is a lot of logic in our hcl files where we generate a bunch of different variables inlocals {}
block and have a robustinputs
block as the downstream tf modules have a lot of variables.Ideally I would just have one generic DRY terragrunt.hcl file, in a generic location like
my-account/terragrunt_files/eks.hcl
and running terragrunt apply frommy-account/dev/eks/
has a file that points tomy-account/terragrunt_files/eks.hcl
That way I need to make the change just once then apply to the envs. For a slightly safer method, we would have some sort of versioning. As in it would first be pointing to
my-account/terragrunt_files/eks.hcl?ref=v1.0.0
and then we would update to point tomy-account/terragrunt_files/eks.hcl?ref=v2.0.0
This example is just using eks, we ofcourse have multiple components. For example extrapolating this example to namespace lets say. if we have 20 namespaces per env. we have 20*4(env) identical
terragrunt.hcl
s that source our namespace module. Upgrading our namespace module then requires us to make the exact same change in 80 different files.So other people run into this? how do you keep this dry?
The text was updated successfully, but these errors were encountered: