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.07k stars 980 forks source link

Only one level of includes is allowed #303

Open cstavro opened 7 years ago

cstavro commented 7 years ago

I'm trying to enable a multi-include scenario where I can cascade includes at different points in my configurations. Given the below layout:

└── root
    ├── regions
    │   ├── us-east-1
    │   │   ├── regional_deployment(lambda)
    │   │   │      └── terraform.tfvars
    │   │   ├── config.tfvars
    │   │   └── terraform.tfvars
    │   └── us-east-2
    │       ├── regional_deployment(lambda)
    │       │      └── terraform.tfvars
    │       ├── config.tfvars
    │       └── terraform.tfvars
    ├── config.tfvars
    └── terraform.tfvars

I would like to be able to set the remote_state in the root terraform.tfvars while allowing for the notion of regional configurations. Some deployments are regional by nature (aws lambdas as an example) and so I think it makes sense to allow for this kind of modeling.

Right now I'm adding a separate extra_arguments block in each regional deployment's terraform.tfvars that includes required_var_files = ["${get_tfvars_dir()}/../config.tfvars]" . It works but it's not very DRY. Being able to have the regional terraform.tfvars leverage an include block to roll up to its parent would be even more so.

brikis98 commented 7 years ago

Could you add the extra_arguments block in the root too?

mike-schiller commented 7 years ago

We have a similar use case with regions, platforms deployed in regions, and then apps deployed on those platforms. I hadn't thought of @cstavro 's approach, but will see if I can adapt it for our 3 tier use case.

That said, support for multiple levels of includes would be ideal. This was also referenced (along with deep merging) in @jocgir 's comment on: https://github.com/gruntwork-io/terragrunt/issues/147 . It seems some work has been done in that area, though in a fork.

As for the suggestion of putting the extra_arguments in the root, would that have to apply identically to everything in the tree? If so, I don't think that would work for our use case with the additional tier. Also, we do have some resources that are only 1 level deep, so those would have issues if extra_arguments in the root are applied identically to everything.

brikis98 commented 7 years ago

As for the suggestion of putting the extra_arguments in the root, would that have to apply identically to everything in the tree? If so, I don't think that would work for our use case with the additional tier. Also, we do have some resources that are only 1 level deep, so those would have issues if extra_arguments in the root are applied identically to everything.

Yes, every child with an include would pull in all the extra_arguments in the root. That said, it doesn't mean every child gets identical behavior. Using some of the helper functions, the child nodes can have fairly dynamic behavior.

For example, in the root, we can define something like this:

terragrunt = {
  terraform {
    extra_arguments "common_vars" {
      commands = ["${get_terraform_commands_that_need_vars()}"]

      optional_var_files = [
        "${get_tfvars_dir()}/${find_in_parent_folders("account.tfvars", "skip-account-if-does-not-exist")}",
        "${get_tfvars_dir()}/${find_in_parent_folders("region.tfvars", "skip-region-if-does-not-exist")}",
        "${get_tfvars_dir()}/${find_in_parent_folders("env.tfvars", "skip-env-if-does-not-exist")}",
        "${get_tfvars_dir()}/terraform.tfvars"
      ]
    }
  }
}

Now every child will automatically find an account.tfvars, region.tfvars, and env.tfvars in its parent directory and skip that file if it doesn't exist.

dan-rose commented 7 years ago

Thanks for the above - Its just the answer I was looking for!!!

tomdavidson commented 7 years ago

im a bit green to terragrunt - can optional_var_files pull in a tf file that uses locals {} or only tfvars files?

can the optional_var_files contain terragrunt config such as all the regions use the dependencies defined in cross-region.tfvars which depend on root/terraform.tfvars?

└── root ├── regions │ ├── us-east-1 │ │ ├── regional_deployment(lambda) │ │ │ └── terraform.tfvars │ │ ├── config.tfvars │ │ └── terraform.tfvars │ ├── us-east-2 │ | ├── regional_deployment(lambda) │ | │ └── terraform.tfvars │ | ├── config.tfvars │ | └── terraform.tfvars | └── cross-region.tfvars ├── config.tfvars └── terraform.tfvars

brikis98 commented 7 years ago

im a bit green to terragrunt - can optional_var_files pull in a tf file that uses locals {} or only tfvars files?

optional_var_files simply sets the -var-file param when running terraform. AFAIK, the -var-file param only supports .tfvars files.

tomdavidson commented 7 years ago

Great, thank you. This might be obvious too, but can terragrunt config be brought in with the optional_var_files or just terraform vars?

brikis98 commented 7 years ago

optional_var_files currently only sets the -var-file flag and nothing else, so it cannot be used to bring in Terragrunt configuration.

mazzy89 commented 6 years ago

I have more or less the same scenario however I'm not able to get to the bottom of this. I've the following dir struct:

.
├── account.tfvars
├── terraform.tfvars
└── test
    ├── _global
    │   └── iam
    │       └── terraform.tfvars
    ├── terraform.tfvars
    ├── test-blue
    │   └── frontend
    │       └── terraform.tfvars
    └── test-green
        └── frontend
            └── terraform.tfvars

basically under the test environment i have multiple environments blue and green. Both of them must share the same IAM resources. I have the following error when I run terragrunt plan from the frontend dir

test/test-blue/frontend/terraform.tfvars includes ../../terraform.tfvars, which itself includes ../../terraform.tfvars. Only one level of includes is allowed.

basically under the test-blue/frontend/terraform.tfvars I have this:

  include = {
    path = "${find_in_parent_folders()}"
  }

so it is under also test/terraform.tfvars

Update - 1 I was able to get rid off the issue by removing the include under test/terraform.tfvars and have there just terragrunt {} however now I can't retrieve the remote state config set up in the terraform.tfvars file in the root dir

Update - 2

I've removed the intermediary terraform.tfvars located under test/terraform.tfvars and repeated the shared config in each terraform.tfvars of every envs. In this case I lose the DRY but it works. I mean the not nice thing is that I have to repeat the common test vars both under test-blue and test-green tfvars files.

So I could say that the use case with the intermediary terraform.tfvars is not supported?

Here the repositories

https://github.com/mazzy89/terragrunt-poc-live https://github.com/mazzy89/terragrunt-poc-modules

mazzy89 commented 6 years ago

All good after spent the entire afternoon reading the PR and all the issues around the terragrunt repo is now clear. This is my commit that implement the shared file across the test env. https://github.com/mazzy89/terragrunt-poc-live/commit/0b37b2a0033b444ff2ccfbc4549b86a2e1f5ee6c

brikis98 commented 6 years ago

Yes, as the error indicates, only one level of includes is currently supported. This is mainly to keep the code simpler & more maintainable. I'm not against multiple levels, but someone would have to roll up their sleeves for a PR. In the mean time, extra_arguments offers a workaround for many use cases. Looks like you got that figured out.

alberts-s commented 5 years ago

Hey @brikis98, Using the approach you showed above worked very well in TF 0.11, however in TF 0.12 when using the required_var_files attribute it expects for all variables to be defined in module inputs (hashicorp/terraform#19424).

One of the options I came up to workaround this would be to define them as inputs in terragrunt.hcl, (inputs are converted to environment variables by TG), however Terragrunt doesn't currently support nesting its configurations (#707, #723).

Do you have any suggestions on workarounds for this issue in TF 0.12?

ashald commented 5 years ago

We have the same need & issue so +1 to @Alberts00.

For now we workaround this with:

tree
.
├── prod
│   ├── step001
│   │   ├── root.tf
│   │   └── terragrunt.hcl
│   └── vars.yaml
└── terragrunt.hcl

2 directories, 4 files

where vars.yaml looks like:

foo: bar

while prod/step001/terragrunt.hcl is:

inputs = yamldecode(file(find_in_parent_folders("vars.yaml")))

include {
  path = find_in_parent_folders()
}

and root.tf has:

variable "foo" {}
brikis98 commented 5 years ago

You can even take @ashald's workaround a step further. The following is courtesy of @yorinasub17:

inputs = merge(
  yamldecode(file("${get_terragrunt_dir()}/${find_in_parent_folders("region.yaml", "empty.yaml")}")),
  {
      foo = "bar"
  },
)

The idea is that you can have configs defined in yaml files (or json if you use jsondecode instead) and you can read variables from there and merge them with other variables to create your inputs map.

geota commented 5 years ago

We have the same need & issue so +1 to @Alberts00.

The issue I see with this workaround is that inputs block needs to be provided in EVERY leaf/child terragrunt.hcl file.

I have a directory structure like this

.
├── accounts
│   ├── defaults.tfvars
│   ├── foo-bld
│   │   ├── account.tfvars
│   │   └── environments
│   │       └── bld
│   ├── foo-prd
│   │   ├── account.tfvars
│   │   └── environments
│   │       └── prd
│   │           ├── foo-website-cdn
│   │           │   └── terragrunt.hcl
│   │           ├── foo-website-route53
│   │           │   └── terragrunt.hcl
│   │           └── environment.tfvars
│   └── foo-stg
│       ├── account.tfvars
│       └── environments
│           └── stg
│               ├── foo-website-cdn
│               │   └── terragrunt.hcl
│               └── environment.tfvars

And a base/root terragrunt.hcl like this:

# terragrunt.hcl example
remote_state {
  backend = "s3"
  config = {
    bucket         = "foo-state-bucket"
    key            = "${path_relative_to_include()}/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    role_arn       = "foo-role"
    dynamodb_table = "foo-lock-table"
  }
}

terraform {
  # Force Terraform to not ask for input value if some variables are undefined.
  extra_arguments "disable_input" {
    commands  = get_terraform_commands_that_need_input()
    arguments = ["-input=false"]
  }

  # Force Terraform to keep trying to acquire a lock for up to 5 minutes if someone else already has the lock
  extra_arguments "retry_lock" {
    commands  = get_terraform_commands_that_need_locking()
    arguments = ["-lock-timeout=5m"]
  }

  # Pass var files to var commands
  extra_arguments "vars" {
    commands = get_terraform_commands_that_need_vars()

    # tfvars specified last take precedence
    optional_var_files = [
      "${get_terragrunt_dir()}/${find_in_parent_folders("defaults.tfvars", "ignore")}",
      "${get_terragrunt_dir()}/${find_in_parent_folders("account.tfvars", "ignore")}",
      "${get_terragrunt_dir()}/${find_in_parent_folders("region.tfvars", "ignore")}",
      "${get_terragrunt_dir()}/${find_in_parent_folders("environment.tfvars", "ignore")}",
      "${get_terragrunt_dir()}/terraform.tfvars",
      "${get_terragrunt_dir()}/${find_in_parent_folders("secrets.tfvars", "ignore")}",
      "${get_terragrunt_dir()}/secrets.tfvars",
    ]
  }
}

This allows me to keep my leaf/child terragrunt.hcl very DRY

include {
  path = find_in_parent_folders()
}

dependencies {
  paths = ["../foo-website-route53"]
}

terraform {
  source = "${get_parent_terragrunt_dir()}/modules/dsm-website-cdn"
}

The current workaround I would need to copy/paste a default inputs into every child... is there someway we can convey this merging of inputs and treat them as env vars in the ROOT terragrunt hcl files?

One path forward I could see is mimic the exact same extra var construct but provide an extra env-var version for it...

Something like this:

    optional_env_var_files = [
      "${get_terragrunt_dir()}/${find_in_parent_folders("defaults.env", "ignore")}",
      "${get_terragrunt_dir()}/${find_in_parent_folders("account.env", "ignore")}",
      "${get_terragrunt_dir()}/${find_in_parent_folders("region.env", "ignore")}",
      "${get_terragrunt_dir()}/${find_in_parent_folders("environment.env", "ignore")}",
      "${get_terragrunt_dir()}/terraform.env",
      "${get_terragrunt_dir()}/${find_in_parent_folders("secrets.env", "ignore")}",
      "${get_terragrunt_dir()}/secrets.env",
    ]
yorinasub17 commented 5 years ago

The current workaround I would need to copy/paste a default inputs into every child...

The inputs attribute is automatically merged from the root terragrunt HCL file, so you only need the yamldecode function calls in the root terragrunt.hcl in the workaround. You basically replace all the tfvars files you had in the directory tree with yaml, and replace the extra vars block with yamldecode calls and merge on the inputs attribute to get the same system as before.

geota commented 5 years ago

Awesome I did not know that! Thanks @yorinasub17

okgolove commented 5 years ago

@geota could you show your current configuration with yamldecode?

This doesn't work for me :(

geota commented 5 years ago

@okgolove you have to conditionalize what you pass into yamldecode... im sure you could clean this up, but this is whats currently working for me

secrets are SOPS encrypted normally and the decrypted secret.yamls are in our .gitignore

inputs = merge(
  yamldecode(fileexists("${get_parent_terragrunt_dir()}/defaults.yaml") ? file("${get_parent_terragrunt_dir()}/defaults.yaml") :  "{}"),
  yamldecode(fileexists("${get_parent_terragrunt_dir()}/secrets.yaml") ? file("${get_parent_terragrunt_dir()}/secrets.yaml") :  "{}"),
  yamldecode(fileexists("${get_terragrunt_dir()}/${find_in_parent_folders("account.yaml", "ignore")}") ? file("${get_terragrunt_dir()}/${find_in_parent_folders("account.yaml")}") :  "{}"),
  yamldecode(fileexists("${get_terragrunt_dir()}/${find_in_parent_folders("environment.yaml", "ignore")}") ? file("${get_terragrunt_dir()}/${find_in_parent_folders("environment.yaml")}") :  "{}"),
  yamldecode(fileexists("${get_terragrunt_dir()}/${find_in_parent_folders("secrets.yaml", "ignore")}") ? file("${get_terragrunt_dir()}/${find_in_parent_folders("secrets.yaml")}") :  "{}"),
  yamldecode(fileexists("${get_terragrunt_dir()}/${find_in_parent_folders("overrides.yaml", "ignore")}") ? file("${get_terragrunt_dir()}/${find_in_parent_folders("overrides.yaml")}") :  "{}"),
  yamldecode(fileexists("${get_terragrunt_dir()}/overrides.yaml") ? file("${get_terragrunt_dir()}/overrides.yaml") :  "{}"),
  yamldecode(fileexists("${get_terragrunt_dir()}/secrets.yaml") ? file("${get_terragrunt_dir()}/secrets.yaml") :  "{}")
)
okgolove commented 5 years ago

@geota Got it. Why don't you use find_in_parent_folders("region.yaml", "empty.yaml") function? The second argument means file to use in case of the first is missing.

geota commented 5 years ago

@okgolove I do use that function. Since we are manually loading the file using file function and then parsing yamldecode we need to guard against a file not existing. There is prob other ways to represent the intent.

This likely works as well and is a bit cleaner. find_in_parent_folders("region.yaml", "empty.yaml") == "empty.yaml" ? {} : yamldecode(file(find_in_parent_folders("region.yaml")))

You could clean this up further by having an actual file on disk called empty.yaml, but I dont really want to do this.

geota commented 5 years ago

@okgolove I remember why... when I tried to use the cleaner version I posted above I get this weird error:

Inconsistent conditional result types; The true and false result expressions must have consistent types. The given expressions are object and object, respectively.

yorinasub17 commented 5 years ago

Hi folks, I just opened a PR for the Terragrunt examples to include how we set up the yaml files in the Gruntwork Reference Architecture: https://github.com/gruntwork-io/terragrunt-infrastructure-live-example/pull/20

Hope this helps to show another example of how to use yamldecode.

geota commented 5 years ago

@yorinasub17 ty!

IMBurbank commented 4 years ago

@yorinasub17 provided what is currently the most effective solution I've found to organize configs for our large infrastructure. Unfortunately, we're seeing conflicts with terraform deciding to warn and likely soon error when inputing unused variables from a file (as opposed to the environment via TF_VAR_).

I've checked:

873 which references this issue as a current workaround. It also references,

744 where @rdebrandt discusses a solution with mid-level app.hcl files which would work similarly to rolled-up yaml files, but it didn't appear this would be adopted. This issue mentions,

814 which, along with #744, reference PR,

#858 Which still doesn't look like it will allow multi-level config files, and I'm not sure how it impacts the terraform issue with unused input variables.

I've pasted a simplified example of our directory structure below that demonstrates why we need to roll up intermediate config files instead of just passing down from a parent terragrunt.hcl or populate the env with TF_VAR_ args. Essentially, we need to support a grid-style of configs for multiple environments and markets that all have the same general infra with slight config-driven deviations.

.
├── common.yaml
├── env_dev
│   ├── env.yaml
│   ├── install_market1
│   │   ├── app1
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   ├── app2
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   ├── app3
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   └── install.yaml
│   ├── install_market2
│   │   ├── app1
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   ├── app2
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   ├── app3
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   └── install.yaml
│   └── install_market3
│       ├── app1
│       │   ├── module.yaml
│       │   └── terragrunt.hcl
│       ├── app2
│       │   ├── module.yaml
│       │   └── terragrunt.hcl
│       ├── app3
│       │   ├── module.yaml
│       │   └── terragrunt.hcl
│       └── install.yaml
├── env_prod
│   ├── env.yaml
│   ├── install_market1
│   │   ├── app1
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   ├── app2
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   ├── app3
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   └── install.yaml
│   ├── install_market2
│   │   ├── app1
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   ├── app2
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   ├── app3
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   └── install.yaml
│   └── install_market3
│       ├── app1
│       │   ├── module.yaml
│       │   └── terragrunt.hcl
│       ├── app2
│       │   ├── module.yaml
│       │   └── terragrunt.hcl
│       ├── app3
│       │   ├── module.yaml
│       │   └── terragrunt.hcl
│       └── install.yaml
├── env_sand
│   ├── env.yaml
│   ├── install_market1
│   │   ├── app1
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   ├── app2
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   ├── app3
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   └── install.yaml
│   ├── install_market2
│   │   ├── app1
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   ├── app2
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   ├── app3
│   │   │   ├── module.yaml
│   │   │   └── terragrunt.hcl
│   │   └── install.yaml
│   └── install_market3
│       ├── app1
│       │   ├── module.yaml
│       │   └── terragrunt.hcl
│       ├── app2
│       │   ├── module.yaml
│       │   └── terragrunt.hcl
│       ├── app3
│       │   ├── module.yaml
│       │   └── terragrunt.hcl
│       └── install.yaml
└── terragrunt.hcl

Is there a possible path forward using terragrunt to organize intermediate config files using a dry pattern based on @yorinasub17 's PR #20?

yorinasub17 commented 4 years ago

Unfortunately, we're seeing conflicts with terraform deciding to warn and likely soon error when inputing unused variables from a file (as opposed to the environment via TFVAR).

Terragrunt inputs uses TF_VAR to pass in the inputs to terraform, so there should be no issues if you are using the pattern introduced in https://github.com/gruntwork-io/terragrunt-infrastructure-live-example/pull/20. How are you passing in the inputs?

IMBurbank commented 4 years ago

Each leaf terragrunt.hcl is formatted similarly to below:

include {
  path = find_in_parent_folders()
}

locals {
  region  = "${get_env("TF_VAR_region", "us-east-1")}"
}

terraform {
  source = "${get_parent_terragrunt_dir()}/templates/foo"
}

inputs = merge(
  yamldecode(file("${get_terragrunt_dir()}/${find_in_parent_folders("common.yml", "empty.yml")}")),
  yamldecode(file("${get_terragrunt_dir()}/${find_in_parent_folders("install.yml", "empty.yml")}")),
  yamldecode(file("${get_terragrunt_dir()}/${find_in_parent_folders("env.yml", "empty.yml")}")),
  yamldecode(file("${get_terragrunt_dir()}/module.yml")),
  {
    aws_region  = local.region
  }
)
yorinasub17 commented 4 years ago

Hmm I have the exact same set up and I do not get that warning from terraform. Which version of terraform are you using (I am on 0.12.13)? It is possible newer versions of terraform are now warning for env vars too.

yorinasub17 commented 4 years ago

I wrote up an RFC that introduces import blocks. This has the potential to address the use case described here, so would love feedback from those following this issue to see if it makes sense.

Heiko-san commented 4 years ago

I would want to do the same thing: a global terragrunt.hcl , then a level for AWS regions where I want to override some inputs and then the actual terraform module/code to role out a specific part of infrastructure. I tried to add an additional terragrunt.hcl at the region level folder with another

include {
  path = find_in_parent_folders()
}

in it. The result was the error that only one include level is allowed. I think this should be possible, either by my apporach with multiple include levels, or by a list of inlcudes.

yorinasub17 commented 4 years ago

@Heiko-san , thanks for sharing your use case! We are actively working on import blocks, which will be the solution to this. Please refer to the RFC and the PR for more details.

bholzer commented 4 years ago

I am running into this with another use-case that I think hasn't been addressed here, unless I missed it.

I have a component of my infrastructure that is made up of other reusable subcomponents, not just configuration changes. Particularly, I have an AWS ECS task component and an ECS service component, but there doesn't seem to be a clear way to structure terragrunt configs in a multi-level hierarchy as can be done with pure terraform.

For example, I would like to have a structure like this:

├── terragrunt.hcl
├── common.yaml
└── production
    ├── env.yaml
    ├── web_application
    │   ├── terragrunt.hcl
    │   ├── ecs_task
    |   |   ├── task_definition.json
    │   │   └── terragrunt.hcl
    │   └── ecs_service
    │       └── terragrunt.hcl
    └── worker_app
        ├── terragrunt.hcl
        └── ecs_task
            ├── task_definition.json
            └── terragrunt.hcl

I would like to use intermediate terragrunt.hcl files to compose applications from ecs_task and ecs_service subcomponents, but I can't seem to find any way to do this.

yorinasub17 commented 4 years ago

I would like to use intermediate terragrunt.hcl files to compose applications from ecs_task and ecs_service subcomponents, but I can't seem to find any way to do this.

This use case will most likely be achievable with https://github.com/gruntwork-io/terragrunt/issues/759#issuecomment-585124357

With that said, I feel like you decomposed these too much in terragrunt. The goal of terragrunt is to make it easier to manage a world where you want multiple state files by DRY-ing things up, both at the regional and component levels. In general, if you want things to compose in a single state file, then that composition should be done in terraform. On the other hand, if you want to compose things across state files because you've separated things out (e.g VPC separated from EKS cluster), then that should be composed in terragrunt using dependency blocks.

Without looking at any of your code, something like ECS tasks and ECS services feel like they belong in the same state as terraform modules that should be composed in terraform, not terragrunt.

eug-maly commented 4 years ago

@yorinasub17 we use access layer to create iam rules\permissions and application layer to create ecs clusters etc. and ecs layer for configuring ecs tasks:

├── access
│   └── terragrunt.hcl
├── application
│   └── terragrunt.hcl
└── ecs
    ├── task1
    │   └── terragrunt.hcl
    ├── task2
    │   └── terragrunt.hcl
    └── task3
        └── terragrunt.hcl

In this example, ecs task should use iam ecs-service-role arn from access layer and loadbalancer arn from application layer. I don't see the way how to use some shared configuration for dependencies ecs layer.

yorinasub17 commented 4 years ago

I don't see the way how to use some shared configuration for dependencies ecs layer.

Do you mean you want to DRY up the dependency blocks, or that you can't use dependency blocks?

eug-maly commented 4 years ago

Do you mean you want to DRY up the dependency blocks, or that you can't use dependency blocks?

I can use dependency blocks in each task configuration. But it would be great if I could specify dependency configuration (and input) just in one place and include (or import) it in tasks.

So, if we add some additional iam role to access layer or, for example, add a new monitoring layer and need to use something from this layer - we specify dependencies\variables just in one config file, Instead of copy-paste it in all tasks config.

yorinasub17 commented 4 years ago

Gotcha. import block is the solution for this, but we haven't gotten around to implementing it yet. You COULD use read_terragrunt_config for this now, but that has a critical bug when you try to use it with dependency (see https://github.com/gruntwork-io/terragrunt/issues/1128).

ghost commented 4 years ago

We have the same problem:

root
- appa
  - terragrunt.hcl <-- include parent, input, terraform, locals, provider 
- vms
  - vm1
    - terragrunt.hcl <-- inputs, include parent
  - vm2
  - terragrunt.hcl <-- terraform, locals, inputs, provider (include parent does not work here!)
terragrunt.hcl <-- remote state, locals (this is never included from vm2)

i would wish to import multiple files from a child that are merged together. So terragrunt.hcl from child folder vm2 can include the terragrunt file from folder vms and root and merge all three together.

zx1986 commented 3 years ago

Is there a best practice for this situation?

smitthakkar96 commented 3 years ago

any update on this, it's been open for 3 years now there is a PR open too. @maintainers are you interested in getting this feature merged I am happy to help

yorinasub17 commented 3 years ago

The work to implement this is being tracked in https://github.com/gruntwork-io/terragrunt/issues/1566. Please follow that issue for further updates on when this is supported.

archenroot commented 3 years ago

I started using https://github.com/gruntwork-io/terragrunt-infrastructure-live-example which I extended as following:

terragrunt.hcl
non-prod
   |---ap-northeast-2
      |---account.hcl
      region
         |---region.hcl
         env
            |---env.hcl
            |--- aws_dms
                |---terragrunt.hcl
                |---aws_kms
                    |---terragrunt.hcl
                |---aws_iam_role
                    |---terragrunt.hcl

every hcl file includes only for base testing following block:

include "root" {
path = find_in_parent_folders()
}

but when run terragrunt run-all plan from aws_dms folder I get error:

[09:18:00] zangetsu@zeus  $    /data/proj/kidsloop/terragrunt-dms-demo/non-prod/ap-northeast-2/dev/dms   main  tg run-all plan ERRO[0000] Error processing module at '/data/proj/kidsloop/terragrunt-dms-demo/non-prod/ap-northeast-2/dev/dms/kms/terragrunt.hcl'. How this module was found: Terragrunt config file found in a subdirectory of /data/proj/kidsloop/terragrunt-dms-demo/non-prod/ap-northeast-2/dev/dms. Underlying error: /data/proj/kidsloop/terragrunt-dms-demo/non-prod/ap-northeast-2/dev/dms/kms/terragrunt.hcl includes /data/proj/kidsloop/terragrunt-dms-demo/non-prod/ap-northeast-2/dev/dms/terragrunt.hcl, which itself includes /data/proj/kidsloop/terragrunt-dms-demo/non-prod/ap-northeast-2/dev/dms/terragrunt.hcl. Only one level of includes is allowed. ERRO[0000] Unable to determine underlying exit code, so Terragrunt will exit with error code 1

archenroot commented 3 years ago

Ok, sry to spam, I see its not supported and is being worked on... at least thx @yorinasub17 for implementing multi include blocks on single level as starting point.

archenroot commented 3 years ago

So the isssue is triggered by this in level 2 or more terragrunt.hcl files:

include "root" {
  path = find_in_parent_folders()
}

I tried to use either:

But when I use a relative path, the issue dissapears, so this could be a potential workourund until its fully supported in future

include "common" {
  path   = "../../../../../terragrunt.hcl"
  expose = true
}

But this also works on layer 2 or more:

include "region" {
  path   = find_in_parent_folders("region.hcl")
  expose = true
}

which is strange its just same function but with parameter. without its failing

BUT!!!: it will report fail on destroy on root hcl file:

Error: Error in function call

  on /data/proj/kidsloop/terragrunt-dms-demo/terragrunt.hcl line 12, in locals:
  12:   region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl"))

Call to function "find_in_parent_folders" failed: ParentFileNotFound: Could not find a region.hcl in any of the parent folders of
/data/proj/kidsloop/terragrunt-dms-demo/terragrunt.hcl. Cause: Traversed all the way to the root..

Additionally, when you run your layer 1 terragrunt run-all plan with the dependency of layer 2 directory, the behavior is totally wrong, it will execute and apply (yeah, it starts creating layer 2 dir resources on plan execution ) instead :D So I would not recommend this path and rather wait for multilayer include support is in place.

circa10a commented 1 year ago

I had luck with using:

include "root" {
  path = "${get_path_to_repo_root()}/terragrunt.hcl"
}