If we use Terraform to deploy Helm charts, we might be using one of these strategies to keep chart values:
- Values are in inline YAML string
- Values in separate .yaml file
- Values in separate YAML Template files (.yaml.tpl)
- Use Helm's set for Dynamic Values
- Multiple Values Files
(1) Values in inline YAML string
This is not ideal as problems with Inline YAML in Terraform include:
- No syntax highlighting or validation - Easy to break YAML formatting
- Hard to review in diffs - Changes are messy in PRs
- Can't use standard tooling - No yamllint, Pluto, or other YAML tools
- Mixing concerns - Infrastructure code mixed with application config
- Escaping nightmares - Terraform string interpolation conflicts with Helm templating
Example:
resource "helm_release" "app" {
values = [<<-EOT
replicaCount: ${var.replicas}
image:
repository: myapp
tag: ${var.tag}
service:
type: LoadBalancer
EOT
]
}
(2) Separate Values Files
Keep values in YAML files, reference them in Terraform.
This is a better approach because:
- Clean separation
- Easy to validate with standard tools
- Better diffs
- Can use Pluto directly: pluto detect-files -d .
Example:
main.tf:
resource "helm_release" "my_app" {
name = "my-app"
chart = "my-chart"
repository = "https://charts.example.com"
values = [
file("${path.module}/helm-values.yaml")
]
}
(3) Templated Values Files
Use Terraform's templatefile() to inject dynamic values:
helm-values.yaml.tpl:
replicaCount: ${replica_count}
image:
repository: ${image_repo}
tag: ${image_tag}
ingress:
enabled: ${enable_ingress}
host: ${hostname}
main.tf:
resource "helm_release" "my_app" {
name = "my-app"
chart = "my-chart"
values = [
templatefile("${path.module}/helm-values.yaml.tpl", {
replica_count = var.replica_count
image_repo = var.image_repository
image_tag = var.image_tag
enable_ingress = var.enable_ingress
hostname = var.hostname
})
]
}
Pros:
- Still gets variable injection
- Can be validated as YAML (with placeholders)
- Clean and readable
(4) Use Helm's set for Dynamic Values
Keep static config in files, override specific values:
resource "helm_release" "my_app" {
name = "my-app"
chart = "my-chart"
# Base values from file
values = [
file("${path.module}/helm-values.yaml")
]
# Override specific values dynamically
set {
name = "image.tag"
value = var.image_tag
}
set {
name = "replicaCount"
value = var.replica_count
}
set_sensitive {
name = "secret.password"
value = var.db_password
}
}
Pros:
- Clear what's dynamic vs static
- Base values file can be validated
- Sensitive values handled properly
Here is the example how we can migrate inline YAML from the above to templated file:
helm-values.yaml:
image:
repository: myapp
service:
type: LoadBalancer
main.tf:
resource "helm_release" "app" {
values = [
file("${path.module}/helm-values.yaml")
]
set {
name = "replicaCount"
value = var.replicas
}
set {
name = "image.tag"
value = var.tag
}
}
Now we can run:
% pluto detect-files -f helm-values.yaml
(5) Multiple Values Files
We can layer our configuration:
resource "helm_release" "my_app" {
name = "my-app"
chart = "my-chart"
values = [
file("${path.module}/helm-values-base.yaml"),
file("${path.module}/helm-values-${var.environment}.yaml")
]
}
---
No comments:
Post a Comment