☝️ Here's a stack in our Spacelift account. Typical plans take ~10mins. Slooowwww.
I want to reduce the number of environment variable resources in our state.
We have several static env vars per stack, corresponding to inventory allocation tags.
resource "spacelift_stack" "myinfra1" {
# ...
}
resource "spacelift_environment_variable" "myinfra1_base_tags" {
for_each = { business_unit = "foo", product = "bar", service = "baz", environment = "quux" }
stack_id = spacelift.stack.myinfra1.id
name = each.key
value = each.value
}
Can we enhance the provider so that we can manage N environment variables with <N Terrafom resources?
I have written two proposals below. I am willing to implement this resource myself, but want to get approval on the design / API before proceeding 🙏 Please let me know your thoughts.
- Constraints:
- Preserve the clarity and simplicity of the provider. It's a thin wrapper over the API and doesn't try to add many "convenience" functions
- Don't break the declarative nature of Terraform / don't orphan anything. If you CRUD an env var from state, it MUST be CRUDed in Spacelift
Option 0: environment_variable blocks on spacelift_stack
resource "spacelift_stack" "myinfra1" {
# ...
environment_variable {
name = "business_unit"
value = "foo"
}
dynamic "environment_variable" {
for_each = { product = "bar", service = "baz", environment = "quux" }
content {
name = environment_variable.key
value = environment_variable.value
}
}
# Secrets supported too
environment_variable {
name = "API_KEY"
value_wo = var.api_key
value_wo_version = 1
write_only = true
description = "Upstream API key"
}
}
Pros:
- Fewest total resources — stack and its env vars coexist in one block
- Supports full attribute set:
value, value_wo/value_wo_version, write_only, description
- Declarative by default — removing a block removes the variable from Spacelift
Cons:
- Adds complexity to
spacelift_stack, which is already the largest resource in the provider
dynamic blocks are syntactically heavier than for_each on a dedicated resource
- Can't be shared across stacks (unlike a context); if ten stacks share the same vars, each stack block repeats them
- Mixing
environment_variable blocks with spacelift_environment_variable resources on the same stack risks confusion about which takes precedence
Option 1: spacelift_environment_variables
resource "spacelift_stack" "myinfra1" {
# ...
}
resource "spacelift_environment_variables" "myinfra1_base" {
stack_id = spacelift_stack.myinfra1.id
variable {
name = "business_unit"
value = "foo"
# All attributes are supported: value_wo, value_wo_version, write_only, description
}
dynamic "variable" {
for_each = { product = "bar", service = "baz", environment = "quux" }
content {
name = variable.key
value = variable.value
}
}
}
Pros:
- Simplest approach conceptually
- Adding many env vars to a stack is (IMO) a common occurrence, and this streamlines the process
- Still full support for adding/modifying/removing env vars without Terraform forgetting/orphaning env vars
Cons:
- Mixing
spacelift_environment_variable and spacelift_environment_variables resource blocks could be confusing and redundant (spacelift_environment_variable basically becomes a legacy resource since it's a subset of spacelift_environment_variables)
- Doesn't map 1:1 to the API
Option 2: spacelift_context_config_exclusive
I think there's some prior art to consider: aws_iam_role_policy_attachments_exclusive: https://registry.terraform.io/providers/hashicorp/awS/latest/docs/resources/iam_role_policy_attachments_exclusive.
Managing many underlying resources in one Terraform resource can be awkward, because it's unclear what you do in the "some of the underlying resources are modified/deleted" scenarios. Making the Terraform resource explicitly "exclusive" resolves these issues: Terraform IaC exactly matches the underlying configuration. No external management is allowed.
Example:
resource "spacelift_context" "myinfra1_base" {
name = "myinfra1-base"
}
resource "spacelift_context_config_exclusive" "myinfra1_base" {
context_id = spacelift_context.myinfra1_base.id
environment_variable {
name = "business_unit"
value = "foo"
# All attributes are supported: value_wo, value_wo_version, write_only, description
}
dynamic "environment_variable" {
for_each = { product = "bar", service = "baz", environment = "quux" }
content {
name = environment_variable.key
value = environment_variable.value
}
}
# mounted files are also supported
mounted_file {
relative_path = ".npmrc"
content_base64_wo = var.npmrc_base64
content_wo_version = 1
write_only = true
description = "Private registry config"
}
}
resource "spacelift_context_attachment" "myinfra1_base" {
context_id = spacelift_context.myinfra1_base.id
stack_id = spacelift_stack.myinfra1.id
}
In short, we add the ability to manage contexts items exclusively with a single resource. Then we attach the context to our stack.
Pros:
- Exclusive management, easy to understand
Cons:
- Doesn't map 1:1 to the API
- Needs three resources (context, config, attachment) rather than one (spacelift_environment_variables). Doesn't save any resources until you have >3 env vars to manage. In our case, we have ~6 env vars per stack, so this is only a moderately effective solution
☝️ Here's a stack in our Spacelift account. Typical plans take ~10mins. Slooowwww.
I want to reduce the number of environment variable resources in our state.
We have several static env vars per stack, corresponding to inventory allocation tags.
Can we enhance the provider so that we can manage N environment variables with <N Terrafom resources?
I have written two proposals below. I am willing to implement this resource myself, but want to get approval on the design / API before proceeding 🙏 Please let me know your thoughts.
Option 0:
environment_variableblocks onspacelift_stackPros:
value,value_wo/value_wo_version,write_only,descriptionCons:
spacelift_stack, which is already the largest resource in the providerdynamicblocks are syntactically heavier thanfor_eachon a dedicated resourceenvironment_variableblocks withspacelift_environment_variableresources on the same stack risks confusion about which takes precedenceOption 1:
spacelift_environment_variablesPros:
Cons:
spacelift_environment_variableandspacelift_environment_variablesresource blocks could be confusing and redundant (spacelift_environment_variablebasically becomes a legacy resource since it's a subset ofspacelift_environment_variables)Option 2:
spacelift_context_config_exclusiveI think there's some prior art to consider:
aws_iam_role_policy_attachments_exclusive: https://registry.terraform.io/providers/hashicorp/awS/latest/docs/resources/iam_role_policy_attachments_exclusive.Managing many underlying resources in one Terraform resource can be awkward, because it's unclear what you do in the "some of the underlying resources are modified/deleted" scenarios. Making the Terraform resource explicitly "exclusive" resolves these issues: Terraform IaC exactly matches the underlying configuration. No external management is allowed.
Example:
In short, we add the ability to manage contexts items exclusively with a single resource. Then we attach the context to our stack.
Pros:
Cons: