Terraform Helm Provider: Deploy Helm Charts to Kubernetes with OpenTofu

· 2 min read · Terraform & OpenTofu Tutorials

Manage Helm releases as code #

Helm is the package manager of Kubernetes. The Terraform and OpenTofu helm provider installs and upgrades charts as helm_release resources, so the cluster and its base components (ingress controller, certificate manager, monitoring) are created in the same pipeline. Compare with managing plain manifests in Kubernetes with Terraform and with GitOps.

Providers #

The Helm provider needs access to the cluster. This example uses an EKS cluster created in the same configuration:

providers.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    helm = {
      source  = "hashicorp/helm"
      version = "~> 2.17"
    }
  }
}

data "aws_eks_cluster" "this" {
  name = var.cluster_name
}

data "aws_eks_cluster_auth" "this" {
  name = var.cluster_name
}

provider "helm" {
  kubernetes {
    host                   = data.aws_eks_cluster.this.endpoint
    cluster_ca_certificate = base64decode(data.aws_eks_cluster.this.certificate_authority[0].data)
    token                  = data.aws_eks_cluster_auth.this.token
  }
}

For a local cluster (K3s), use config_path = "~/.kube/config" inside the kubernetes block. Version 3 of the provider changes this block syntax to an attribute, so check the documentation of the version you install.

Install a chart #

ingress.tf
resource "helm_release" "ingress_nginx" {
  name             = "ingress-nginx"
  repository       = "https://kubernetes.github.io/ingress-nginx"
  chart            = "ingress-nginx"
  version          = "4.11.3"
  namespace        = "ingress-nginx"
  create_namespace = true

  set {
    name  = "controller.replicaCount"
    value = "2"
  }

  set {
    name  = "controller.service.annotations.service\\.beta\\.kubernetes\\.io/aws-load-balancer-type"
    value = "nlb"
  }
}

Always pin version. The set block overrides single values, and the dots in annotation names must be escaped.

Use a values file #

For many settings, a values file is clearer. Use templatefile to inject Terraform values:

cert-manager.tf
resource "helm_release" "cert_manager" {
  name             = "cert-manager"
  repository       = "https://charts.jetstack.io"
  chart            = "cert-manager"
  version          = "v1.16.1"
  namespace        = "cert-manager"
  create_namespace = true

  values = [
    templatefile("${path.module}/values/cert-manager.yaml.tftpl", {
      replicas = var.environment == "pro" ? 2 : 1
    })
  ]
}
values/cert-manager.yaml.tftpl
crds:
  enabled: true
replicaCount: ${replicas}

Sensitive values #

secrets.tf
resource "helm_release" "app" {
  name       = "app"
  chart      = "./charts/app"
  namespace  = "default"

  set_sensitive {
    name  = "database.password"
    value = var.db_password
  }
}

set_sensitive hides the value in the plan, but it is still stored in the state. See secrets management: prefer secrets that the application reads from Secrets Manager or an operator.

Dependencies and CRDs #

Charts that define custom resources (cert-manager issuers, Prometheus rules) cannot be planned until their CRDs exist. Install them in separate steps with depends_on, or in separate configurations, and use kubernetes_manifest or a second Helm chart for the custom resources.

app.tf
resource "helm_release" "app" {
  # ...
  depends_on = [helm_release.cert_manager, helm_release.ingress_nginx]
}

Upgrades and rollbacks #

  • Changing version or values and applying runs a helm upgrade.
  • atomic = true rolls back automatically when the release fails, and timeout sets the wait time.
  • helm_release can leave a release in a failed state. Fix it with helm rollback or tofu apply -replace=helm_release.app.

When to use it #

Use the Helm provider for the platform components that you install once per cluster. For application deployments, a GitOps tool such as Argo CD offers continuous reconciliation and visibility that Terraform runs do not. Related: Terraform with Kubernetes and Helm installation.

#Terraform #OpenTofu #Kubernetes #Helm