hashicorp / terraform

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
https://www.terraform.io/
Other
42.12k stars 9.47k forks source link

Support for init-time constants #32041

Open valentindeaconu opened 1 year ago

valentindeaconu commented 1 year ago

Terraform Version

Terraform v1.3.2
on darwin_amd64

Use Cases

Working with Terraform on large-scale projects or mono-repositories with a lot of duplicates can become really overwhelming. For example, updating a module version or refactoring the name of the AWS profiles. In this feature request, I propose an idea of introducing init-time constants (values that can be evaluated at init-step) which can help us (Terraform developers) to follow the DRY principles and also can reduce a lot of hardcoded values.

Attempted Solutions

For example, to define a module, the only possible way is:

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "3.15.0"
}

If we have two instances of the same module:

module "vpc-1a" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "3.15.0"

  # ...
}
module "vpc-1b" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "3.15.0"

  # ...
}

Of course, we can use a for_each = toset(["1a", "1b"]) to avoid repeating ourselves, but this problem can become more complex: what if we have 2 different Terraform projects, with two different backends, hitting different accounts? The for_each workaround no longer works. So we will end up with two actual module blocks copied in both places. The problem occurs when we want to bump the module version. We will have to update both places.

This problem can be solved outside of Terraform by introducing a wrapper (e.g. Terragrunt) or by using a dependency updater (e.g. RenovateBot) (for this use-case only).

There are a lot of use cases where we can use those constants to avoid hardcoding values - provider blocks, terraform blocks (providers versions, Terraform version, backends), modules, etc.

Proposal

My suggestion to overcome this issue is to introduce init-time constants. The constants will be defined totally the same as the variables:

constant "vpc_module_version" {
  type        = string
  description = "The VPC module version."
  default     = "3.15.0"
}

constant "vpc_module_name_suffix" {
  type        = string
  description = "The suffix for the VPC module name."
  default     = "1b"
}

And then used in code as:

module "vpc-${const.vpc_module_name_suffix}" {
  source  = "terraform-aws-modules/vpc/aws"
  version = const.vpc_module_version

  # ...
}

Of course, the main point here is supporting them exactly the same as the variables:

More explicitly, they will behave like variables, the only difference here being the time when they are evaluated (pre-init, instead of post-init).

References

crw commented 1 year ago

Thanks for this request!

valentindeaconu commented 1 year ago

Here is a list of currently open issues that can be approached or even solved (at least partially) with this feature:

Also, I found an old discussion regarding a similar implementation for this feature in the issue https://github.com/hashicorp/terraform/issues/8002.

valentindeaconu commented 1 year ago

@crw is there any news on this feature request?

crw commented 1 year ago

No updates. We prioritize based on upvotes, although the list of linked issues also helps when considering the impact of the feature. We also categorize issues and attempt to address them when working on those areas of the code.