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

dependency mock_outputs workflow for output changes #940

Closed matiasbava3 closed 3 years ago

matiasbava3 commented 4 years ago

Hi everyone!

We have experienced the following error while trying to run terragrunt plan-all --terragrunt-source-update --terragrunt-non-interactive --terragrunt-include-external-dependencies in a lambda folder, which have dependency with sqs.

Unsupported attribute; This object does not have an attribute named “Sqs-Two_name”., and 1 other diagnostic(s) [terragrunt] 2019/11/01 17:53:59 Unable to determine underlying exit code, so Terragrunt will exit with error code 1

In the tfstate, we have the Sqs-One, but the plan is going to create a new resource (Sqs-Two).

Could there be a flag that always force you to use the mock_outputs in the plan? Something like this mock_outputs_forced_terraform_commands = [“plan”]

Our project has the following structure:

├── live
│   ├── lambda
│   │   ├── Lambda_Sqs-One_Dependent
│   │   │   └── terragrunt.hcl
│   │   └── Lambda_Sqs-Two_Dependent
│   │       └── terragrunt.hcl
│   └── sqs
│       └── terragrunt.hcl
└── terraform
    ├── lambda
    │   ├── Lambda_Sqs-One_Dependent
    │   │   └── main.tf
    │   └── Lambda_Sqs-Two_Dependent
    │       └── main.tf
    └── sqs
        ├── Sqs-One.tf
        └── Sqs-Two.tf

Sqs-One and Sqs-Two have:

module "Sqs-One" {
  source = "git@github.com:Enterprise/terraform-aws-sqs.git?ref=tags/2.1"
}

output "Sqs-One_name" {
  value       = module.Sqs-One.sqs_name
}

module "Sqs-Two" {
  source = "git@github.com:Enterprise/terraform-aws-sqs.git?ref=tags/2.1"
}

output "Sqs-Two_name" {
  value       = module.Sqs-Two.sqs_name
}

For each lambda terragrunt.hcl, we have:

terraform {
  source = "path_to_source"
}

dependency "sqs" {
  config_path = "../../sqs"

  mock_outputs = {
    Sqs-Two_name = "mock_name"
  }
}

inputs = {
  dlq_name  = dependency.sqs.outputs.Sqs-Two_name
}
yorinasub17 commented 4 years ago

That seems reasonable. Will be happy to review a PR!

barryib commented 4 years ago

Doing something like this won't help ?

dependency "sqs" {
  config_path = "../../sqs"

  mock_outputs = {
    Sqs-Two_name = "mock_name"
  }

  skip_outputs = true
  mock_outputs_allowed_terraform_commands = ["plan"]
}

With skip_outputs seted to true, Terragrunt won't pull outputs from states.

yorinasub17 commented 4 years ago

IIRC, setting skip_outputs = true will skip pulling outputs for terraform apply as well.

thomvaill commented 4 years ago

We have the same issue! When we add a new output, consumed by another dependent one, it breaks our CI/CD pipeline because mock_outputs are not taken into account (because the state already exists). We were thinking of another solution: create a partial_mock_outputs = true option. It would default to false to be compatible with the current behavior. When true, it would merge the actual outputs with the mocked ones. What do you think?

This problem should really be fixed because we have to do manual operations to fix our pipeline, which is not sustainable!

yorinasub17 commented 4 years ago

When we add a new output, consumed by another dependent one, it breaks our CI/CD pipeline because mock_outputs are not taken into account (because the state already exists). We were thinking of another solution: create a partial_mock_outputs = true option. It would default to false to be compatible with the current behavior. When true, it would merge the actual outputs with the mocked ones. What do you think?

I think this has the element of surprise. It feels like a smell to me to be depending on mock_outputs to workaround that issue, and it won't be immediately obvious when things are coming from mock_outputs and when things are coming from the actual outputs.

In general, plan-all is simply broken for day to day use for various reasons including this one and we generally discourage it's usage now, especially in a CI/CD pipeline. We are actively investigating an alternative solution for the use case, but that is not going to come any time soon. It is simply not an easy feat to have plans on multiple state files because terraform doesn't give us a lot of functionality to work with that (since that is not really the main use case they are focusing on).

Nevertheless, if you need this kind of functionality right now, there are two alternative workarounds to this that doesn't require any changes to terragrunt:

1.) Use read_terragrunt_config to read in the dependency, and "pack" it using merge in the inputs block. E.g., if you had the following:

dependency.hcl

locals {
  default_outputs = {
    Sqs-Two_name = "mock_name"
  }
}

dependency "sqs" {
  config_path = "../../sqs"
}

inputs = {
  final_out = merge(dependency.sqs.outputs, local.default_outputs)
}

You can read this in in your other configs as follows:

locals {
  sqs_dep = read_terragrunt_config("/path/to/dependency.hcl").inputs.final_out
}

2.) Use the lookup terraform function to provide a default on use. E.g.,

inputs = {
  dlq_name  = lookup(dependency.sqs.outputs, "Sqs-Two_name", "mock_name")
}
mbelang commented 3 years ago

@yorinasub17 The problem I see with the solution you proposed is that for whatever reason if you forgot the output, it will create a resource with mock_name in it and would be problematic to deploy.

I honestly think the solution should be 100% managed by TG with some options of the mock_outputs as it works well except during the apply. Otherwise, tt makes the feature a bit useless.

yorinasub17 commented 3 years ago

That is a fair criticism, and we would like to improve mock outputs too, but it is a bit hard to come up with a viable solution that is maintainable and makes sense. If you have suggestions on how to improve mock_outputs to fit the workflows described, an RFC + PR with suggested fixes are welcome!

FWIW, the original intention of mock outputs is to allow running validate without deployed resources. That is the primary use case of the original design, and so all the other use cases people want out of mock outputs is going to be awkward by design. We at Gruntwork internally don't use mock outputs beyond that, so it's a bit hard for us to come up with the solution (as we don't satisfy the use cases described).

gregorycuellar commented 3 years ago

Hi @yorinasub17 Is it possible to propose a PR for this ? does it need a RFC ?

For us, with this issue, it's impossible to use plan. I saw, the proposed workaround, I will try it and see if it works for our use case. Thanks for that ;-)

In our use case, it's clear in the data when mocks values are used. And we only activate it, on some commands, and never on applies. So for us, it's not confusing and it helps a lot to have the possibility to plan before apply. As I understood, with the workaround, we will loose the protection on apply command.

I was thinking on something like proposed by @thomvaill

I can propose a PR, but before working on it, I prefer check if it's something, you can be interested on. Thanks

yorinasub17 commented 3 years ago

@gregorycuellar I'm still not a fan of introducing another configuration option, but we also don't have anything to fulfill this need in the pipe, so open to accepting a PR to add this option as a temporary workaround if you are up for it.

gregorycuellar commented 3 years ago

Hi @yorinasub17 I made the PR #1765, in order to fix this. I think, I respected all the guideline and spirit of the project, if not, of course don't hesitate to point out what need to be fixed and I will do it. Thank you

lmayorga1980 commented 3 years ago

In my case I have the following scenario.

..<higher-level-tree>
                /apps
                     /app1
                        base/
                           terragrunt.hcl
                        top/
                           terragrunt.hcl
                     /app2

where /apps/app1/base/terragrunt.hcl looks for parent .hcls to inherit some configuration and /top/terragrunt.hcl depends on /base/terragrunt.hcl

From a CI/CD perspective we would like to be able to validate quickly using the mock_outputs feature but for the plan we really would like to exercise the values retrieved from the /base/terragrunt.hcl (after execution). I am not sure if there are other ways to make the plan as close to reality as possible without mocking.

yorinasub17 commented 3 years ago

https://github.com/gruntwork-io/terragrunt/releases/tag/v0.31.4 was released with a new feature flag (merge_state_and_mocks_outputs) on the dependency block which will force terragrunt to merge mock_outputs with the dependency output that was fetched from state.

mkotsbak commented 3 years ago

I have upgraded and just get the message Unsupported argument; An argument named "merge_state_and_mocks_outputs" is not expected here.:

dependency "root" {
  config_path = find_in_parent_folders()
  mock_outputs = {
    [...]
  }
  merge_state_and_mocks_outputs = true
  mock_outputs_allowed_terraform_commands = ["validate", "fmt"]
}
infraredgirl commented 3 years ago

It looks like the correct name for the parameter is mock_outputs_merge_with_state. It's correctly listed in the docs, but was wrong in the release notes - I've just fixed that.

mkotsbak commented 3 years ago

@infraredgirl Ah, thanks, using that works :)