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.12k stars 986 forks source link

remote_state.disable_init leads to auth error for modules with dependencies #2603

Open c33s opened 1 year ago

c33s commented 1 year ago

Describe the bug running terragrunt validate or terragrunt run-all validate while using disable_init=true leads to an auth error for the http backend.

the disable_init works well if there are no dependencies in the terragrunt file but errors if the module has a dependency.

Expected behavior

disable_init should be used also for dependencies.

Versions

Additional context/questions isn't is simply possible to pass the -backend=false to terraform if provided for terragrunt? am i doing something wrong? i even tried to use mock_outputs, sadly terraform still wants to init the backend.

is there a way to let terragrunt only "build" all the terraform files and manually change in the cache directory and run terraform validate -backend=false without requiring a working backend?

is there a way to mock the backend? i really don't want to use/access the production backend only to validate the syntax in the ci.

i also tried to hardcode disable_init=true to check if there is a problem with piping the TERRAGRUNT_DISABLE_INIT to the dependency. no luck.

also tried to use skip_outputs, same result.

code snippets parent file, remote_state section:

...
remote_state {
  disable_init = true
  backend = "http"
  generate = {
    path      = "_backend.tf"
    if_exists = "overwrite_terragrunt"
  }
  config = {
    address        = "https://gitlab.com/api/v4/projects/${local.environment_vars.gitlab_project_id}/terraform/state/${replace(path_relative_to_include(), "/", "-")}"
    lock_address   = "https://gitlab.com/api/v4/projects/${local.environment_vars.gitlab_project_id}/terraform/state/${replace(path_relative_to_include(), "/", "-")}/lock"
    unlock_address = "https://gitlab.com/api/v4/projects/${local.environment_vars.gitlab_project_id}/terraform/state/${replace(path_relative_to_include(), "/", "-")}/lock"
    username       = "<redacted>"
    password       = "<redacted>"
    lock_method    = "POST"
    unlock_method  = "DELETE"
    retry_wait_min = "5"
    retry_max      = "4"
  }
}
...

working module (with this module everything works fine and i get no backend error):

terraform {
  source = "${get_repo_root()}/modules/hetzner//dns"
}

include "root" {
  path = find_in_parent_folders()
  expose = true
}

locals {
  config_dns = yamldecode(file("${get_repo_root()}/config/dns.yaml"))
}

generate "main_provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
terraform {
  required_version = ">= 1.3.1"
  required_providers {
    ...
  }
}
...
EOF
}

inputs = {
  zones           = local.config_dns.zones
  records         = local.config_dns.records
}

but as soon as i have a dependency in the module i get the following error:

│ Error: Failed to load state: HTTP remote state endpoint requires auth

failing module:

terraform {
  source = "${get_repo_root()}/modules/hetzner//standard-server"
}

include "root" {
  path = find_in_parent_folders()
  expose = true
}

locals {
  config_servers = yamldecode(file("${get_repo_root()}/config/servers.yaml"))
}

dependency "dns" {
  config_path = "../dns"
#  skip_outputs = true
  mock_outputs = {
    internal_zone    = {
      zone_id = "abc"
      name = "example.com"
      ttl = 300
    }
  }
}

generate "main_provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
terraform {
  required_version = ">= 1.3.1"
  required_providers {
   ...
  }
}
...
EOF
}

inputs = {
  servers          = local.config_servers.standard_servers
  internal_zone    = dependency.dns.outputs.internal_zone
}

workaround

my workaround is the following hack in the parent file:

locals {
  ...

  is_ci = tobool(get_env("CI", "false"))
  backend = {
    type = local.is_ci == true ? "local" : "http"
    config = local.is_ci == true ? {} : {
      address        = "https://gitlab.com/api/v4/projects/${local.environment_vars.gitlab_project_id}/terraform/state/${replace(path_relative_to_include(), "/", "-")}"
      lock_address   = "https://gitlab.com/api/v4/projects/${local.environment_vars.gitlab_project_id}/terraform/state/${replace(path_relative_to_include(), "/", "-")}/lock"
      unlock_address = "https://gitlab.com/api/v4/projects/${local.environment_vars.gitlab_project_id}/terraform/state/${replace(path_relative_to_include(), "/", "-")}/lock"
      username       = "<redacted>"
      password       = "<redacted>"
      lock_method    = "POST"
      unlock_method  = "DELETE"
      retry_wait_min = "5"
      retry_max      = "4"
    }
  }
}
...
remote_state {
  disable_init = tobool(get_env("TERRAGRUNT_DISABLE_INIT", "false"))
  backend = local.backend.type
  generate = {
    path      = "_backend.tf"
    if_exists = "overwrite_terragrunt"
  }
  config = local.backend.config
}

while using the following in the child file:

...
dependency "dns" {
  config_path = "../dns"
  mock_outputs = {
    internal_zone    = {
      zone_id = "abc"
      name = "example.com"
      ttl = 300
    }
  }
}
...

another workaround would be to allow terragrunt to use the http backend but prefix the state files with ci_

github-actions[bot] commented 1 month ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for raising this issue.

c33s commented 1 month ago

unstale