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.06k stars 979 forks source link

Terragrunt not running init with backend false when a module has a dependency #1552

Open abhinaba-chakraborty-by opened 3 years ago

abhinaba-chakraborty-by commented 3 years ago

I am having a project structure as follows: . ├── README.md ├── modules │ ├── function-app-consumption │ │ ├── locals.tf │ │ ├── main.tf │ │ ├── output.tf │ │ └── variables.tf │ └── storage-account │ ├── main.tf │ ├── output.tf │ └── variables.tf └── sandbox ├── eastus │ ├── regional.tfvars │ ├── functionapp-svc │ │ └── terragrunt.hcl │ └── storageaccount-function │ └── terragrunt.hcl ├── environment.tfvars └── terragrunt.hcl

I want to deploy the infrastructure in "sandbox" environment. the root terragrunt.hcl file for that env. looks like this:

generate "provider" {
  path      = "_provider.tf"
  if_exists = "overwrite"
  contents  = <<EOF

provider "azurerm" {
  features {

  }
}

EOF
}

remote_state {
  backend = "azurerm"

  disable_init = tobool(get_env("TERRAGRUNT_DISABLE_INIT", "false"))

  generate = {
    path      = "_backend.tf"
    if_exists = "overwrite"
  }

  config = {
    resource_group_name  = "my-sandbox-rg"
    storage_account_name = "mysbxtfbackend"
    container_name       = "tfstate"
    key                  = "${path_relative_to_include()}/terraform.tfstate"
  }
}

I am using the TERRAGRUNT_DISABLE_INIT environment variable to disable backend initialization while running the validate-all command. like this:

cd sanbox/
export TERRAGRUNT_DISABLE_INIT=true
terragrunt validate-all

It works fine when there is no dependency defined in any of the modules. But when a module has a dependency like this:

terraform {
  source = "../../../modules//function-app-consumption"

  extra_arguments "custom_vars" {
    commands = get_terraform_commands_that_need_vars()

    arguments = [
      "-var-file=${get_terragrunt_dir()}/../regional.tfvars",
      "-var-file=${get_terragrunt_dir()}/../../environment.tfvars"
    ]
  }

}

dependency "storage_account" {
  config_path                             = "../storageaccount-function"
  mock_outputs_allowed_terraform_commands = ["validate", "plan"]
  mock_outputs = {
    storage_account_name       = "mockstgacc"
    storage_primary_access_key = "mockaccesskey"
  }
}

dependencies {
  paths = ["../storageaccount-function"]
}

include {
  path = find_in_parent_folders()
}

inputs = {

  function_app_name          = "func-my-sbx-svc"
  function_version           = "~3"
  reserved                   = true
  os_type                    = "linux"
  storage_account_name       = dependency.storage_account.outputs.storage_account_name
  storage_account_access_key = dependency.storage_account.outputs.storage_primary_access_key

  app_settings = {
    "FUNCTIONS_WORKER_RUNTIME" = "java",
    "WEBSITE_RUN_FROM_PACKAGE" = "1"
  }
}

It starts failing , complaining that it requires backend initialization.

[terragrunt] [/Users/abhi/codes/infra/sandbox/eastus/functionapp-svc] 2021/02/17 16:28:38 Running module /Users/abhi/codes/infra/sandbox/eastus/functionapp-svc now
[terragrunt] [/Users/abhi/codes/infra/sandbox/eastus/functionapp-svc] 2021/02/17 16:28:38 Running command: terraform --version
[terragrunt] [/Users/abhi/codes/infra/sandbox/eastus/functionapp-svc] 2021/02/17 16:28:38 Terraform version: 0.14.4
[terragrunt] [/Users/abhi/codes/infra/sandbox/eastus/functionapp-svc] 2021/02/17 16:28:38 Reading Terragrunt config file at /Users/abhi/codes/infra/sandbox/eastus/functionapp-svc/terragrunt.hcl
[terragrunt] [/Users/abhi/codes/infra/sandbox/eastus/storageaccount-function] 2021/02/17 16:28:38 Generated file /Users/abhi/codes/infra/sandbox/eastus/storageaccount-function/.terragrunt-cache/644837049/_backend.tf.
[terragrunt] [/Users/abhi/codes/infra/sandbox/eastus/storageaccount-function] 2021/02/17 16:28:38 Running command: terraform init -get=false -get-plugins=false
[terragrunt] [/Users/abhi/codes/infra/sandbox/eastus/storageaccount-function] 2021/02/17 16:28:40 Running command: terraform output -json

Error: Initialization required. Please see the error message above.

As we can see from the logs, the command terraform init -backend=false is not run , instead, the command terraform init -get=false -get-plugins=false gets executed. This seems to be a bug.

yorinasub17 commented 3 years ago

Ah yes that does look like a bug, specifically, it should skip the direct remote state based dependency fetching when disable_init is true. We're a bit buried at the moment to implement this, but if anyone wants to submit a PR, the function in question is here: https://github.com/gruntwork-io/terragrunt/blob/579258163dd05878f2b3cfa53611c0f5fb8a728e/config/dependency.go#L427

As far as workarounds go, does it work if you add disable_dependency_optimization = tobool(get_env("TERRAGRUNT_DISABLE_INIT", "false")) to the remote_state block?

abhinaba-chakraborty-by commented 3 years ago

No @yorinasub17 , I tried adding disable_dependency_optimization = tobool(get_env("TERRAGRUNT_DISABLE_INIT", "false")) to the remote_state block , but it was of no help.

yorinasub17 commented 3 years ago

Ah upon further inspection, I see that we are handling errors from the output command and special casing when there is no output to return the mocks. That is sort of expected behavior given that the dependency handlers are assuming a clean output call, and output depends on being able to read the state properly (e.g. we don't want to silently fail and return mocks for auth issues).

Does it work if you have skip_outputs = tobool(get_env("TERRAGRUNT_DISABLE_INIT", "false")) on the dependency block?

abhinaba-chakraborty-by commented 3 years ago

Yep it worked!! Thanks a lot for the workaround :-) @yorinasub17

bitsofinfo commented 2 years ago

can something like this be fixed please?

mariadb-PlamenKovachev commented 2 years ago

I have fixed this issue by just adding init command in mock_outputs_allowed_terraform_commands:

dependency "network" {
  config_path = "../network"

  mock_outputs_allowed_terraform_commands = ["validate", "init"]
  mock_outputs = {
    private_subnets_ids = [
        "subnet-xxxxxxxxxxxxxxxxx"
    ]
    nat_public_ips      = [
        "xx.xxx.xxx.xxx"
    ]
  }
}

dependencies {
  paths = ["../network"]
}

inputs = {
  private_subnets_ids = dependency.network.outputs.private_subnets_ids
  nat_public_ips      = dependency.network.outputs.nat_public_ips
}
ThePinzon commented 1 year ago

Running into this issue too :(

lplazas commented 11 months ago

Happy to pick up on this if not assigned, btw is the preferred flow picking from the To Do pile?