The boundary between Terraform and Helm is a common source of confusion. Terraform provisions infrastructure. Helm deploys applications. Terraform’s helm_release resource bridges them, but the chart templates stay in the application repository.
For a runnable lab, see the helm-terraform-js-app directory in the IaC repository.
The Pattern
Terraform manages the Helm release with set blocks that inject environment-specific values:
resource "helm_release" "my_app" {
name = "my-app"
chart = "${path.module}/../helm/myapp"
namespace = kubernetes_namespace.my_app.metadata[0].name
set {
name = "image.repository"
value = var.docker_image_repository
}
set {
name = "image.tag"
value = var.docker_image_tag
}
set {
name = "replicaCount"
value = var.replica_count
}
}
The Helm chart stays portable. Environment-specific values live in Terraform variables.
Chart Structure
The Helm chart should follow the standard layout and expose the values Terraform needs to override:
helm/myapp/
Chart.yaml
values.yaml
templates/
deployment.yaml
service.yaml
values.yaml defines defaults:
replicaCount: 1
image:
repository: my-app
tag: latest
pullPolicy: Always
service:
type: ClusterIP
port: 80
The chart does not need to know about environments. Terraform overrides what changes per environment.
Validation Before Apply
Run helm template against the chart to validate it before Terraform applies:
helm template my-app helm/myapp --values helm/myapp/values.yaml | kubectl apply --dry-run=client -f -
This catches syntax errors, missing template variables, and Kubernetes API validation issues before the release is attempted.
Deployment Pipeline
The lab includes scripts for the full pipeline:
./docker_build.sh # build and tag the image
./docker_push.sh # push to ECR or registry
terraform apply # create or update the Helm release
The pipeline should:
- build and push the image first.
- run
helm templatevalidation. - run
terraform planand review. - apply with the new image tag.
Image Tag Strategy
Pass the image tag as a Terraform variable:
variable "docker_image_tag" {
description = "Docker image tag for the application"
type = string
}
Each deployment gets a unique tag. Avoid latest. Use commit SHAs, semantic versions, or build numbers so every release is identifiable and rollback is unambiguous.
Acceptance Criteria
- Terraform creates or updates the Helm release without modifying the chart.
- Image tag overrides are injected via
setblocks. helm templatevalidation passes before Terraform apply.- Rollback restores the previous image tag.
- Chart is versioned in the application repository, not the infrastructure repository.