Running Helm by hand or through ad-hoc CI scripts leaves gaps in value provenance, diff visibility, and rollback consistency. With Terraform's Helm Provider, you can manage Chart releases declaratively as part of your IaC.
This article focuses on stable features documented by HashiCorp, summarizing the design points you cannot skip in production operations and the angles certification exams tend to test.
Terraform's Helm Provider integrates Helm CLI-equivalent operations into the Terraform plan/apply workflow and manages state through the helm_release resource. This makes diffs visible in plan and automates upgrades on apply.
Charts can be fetched from a repository URL or a local path. The provider uses Kubernetes API connection information (kubeconfig or explicit credentials) to run Helm install and upgrade operations.
Exam questions tend to target the core helm_release attributes (chart, version, namespace, values, set, atomic, wait, timeout, create_namespace, and so on) and how each one influences plan, apply, and rollback behavior.
| Component | Role | Typical settings / examples |
|---|---|---|
| helm_release | Declares a Chart release | chart, version, namespace, values, set |
| Helm Provider | Backend for Helm operations | kubernetes connection info, registry/repository auth |
| Terraform State | Holds the desired state of the release | Diff and history management via plan/apply |
Terraform x Helm x Kubernetes flow
Minimal helm_release example
provider "helm" {
kubernetes {
config_path = var.kubeconfig_path
config_context = var.kube_context
}
}
resource "helm_release" "nginx" {
name = "nginx"
repository = "https://charts.bitnami.com/bitnami"
chart = "nginx"
version = "15.0.0"
namespace = "web"
create_namespace = true
values = [file("values-prod.yaml")]
wait = true
timeout = 600
atomic = true
}The Helm Provider needs a Kubernetes connection under the hood. Typically you either reference a kubeconfig or explicitly specify host, token, and CA. In multi-cluster operations, the standard play is to set up provider aliases and pass them into modules.
In CI, prefer short-lived tokens (e.g. obtained via OIDC) passed through environment variables and kept out of state. A two-tier setup — kubeconfig locally, explicit credentials in CI — is also common in practice.
| Auth method | Pros | Caveats |
|---|---|---|
| kubeconfig reference | Easy for local development; plays well with existing tools | Requires managing file distribution in CI |
| Explicit specification (host+token+CA) | No file required; short-lived tokens raise the security bar | Assumes variables are kept secret and rotated |
| In-cluster (future option) | Connects naturally from inside a Pod | Requires careful environment constraints and RBAC design |
Typical connection patterns (default and alias)
# ローカル: kubeconfig を参照
provider "helm" {
kubernetes {
config_path = var.kubeconfig_path
config_context = var.kube_context
}
}
# CI: 明示的に接続(別クラスタに alias で接続)
provider "helm" {
alias = "eks"
kubernetes {
host = var.eks_api_endpoint
token = var.eks_bearer_token
cluster_ca_certificate = var.eks_cluster_ca
}
}
# モジュール側では providers で注入
module "payments_release" {
source = "./modules/release"
providers = { helm = helm.eks }
# ... module inputs ...
}Helm values are provided through values (an array of YAML file strings) and set (individual key=value overrides). In Terraform, diffs of these surface in plan and are propagated to upgrades on apply. For secrets, set_sensitive keeps the value out of plan output.
A maintainable design uses templated values files for the bulk of the configuration and keeps per-environment differences minimal with set. When you need to specify arrays explicitly, set_list is also an option.
| Mechanism | Primary use | Pros | Caveats |
|---|---|---|---|
| values | Large configs and shared values across environments | Readable and reusable | Requires thought about array merge behavior and file split policy |
| set | Small diffs; tweaking numbers or booleans | Minimal, explicit diffs | A misspelled key shows up as a diff |
| set_sensitive | Secrets such as passwords and tokens | Keeps values out of plan output | Operational discipline is needed to avoid leaking via references or logs |
| set_list | Overriding or specifying arrays | Lets you pass arrays safely | Requires managing value types and ordering |
Practical example: combining values and set_sensitive
locals {
values_file = templatefile("${path.module}/values-${var.env}.yaml", {
image_tag = var.image_tag
})
}
resource "helm_release" "app" {
name = "app"
repository = var.chart_repository
chart = var.chart_name
version = var.chart_version
namespace = var.namespace
values = [
local.values_file
]
set {
name = "replicaCount"
value = tostring(var.replicas)
}
set_sensitive {
name = "secrets.DB_PASSWORD"
value = var.db_password
}
wait = true
timeout = 900
atomic = true
}Terraform keeps the desired state of helm_release in state and detects drift via plan. Running helm upgrade by hand causes drift that the next plan will surface. In operations, enforcing the principle that all changes go through Terraform minimizes drift.
Upgrade stability hinges on configuring wait=true and timeout appropriately. Enabling atomic=true triggers Helm's rollback on failure, avoiding half-applied states.
When taking an existing Helm release under Terraform management, use import. After importing, verify with plan that the resource declaration and the live release are aligned.
| Setting | Purpose | Field guidance / commentary |
|---|---|---|
| wait | Wait for resources to stabilize | true is the default for production. Watch out for job/hook behavior. |
| timeout | Upper bound on wait time | Tune to roughly a 10-20 minute upper bound in production |
| atomic | Auto-rollback on failure | Recommended true. Combine with logs when debugging. |
Flow for importing an existing release
# 1) 先にリソース宣言(最小)
resource "helm_release" "postgres" {
name = "postgres"
namespace = "data"
repository = "https://charts.bitnami.com/bitnami"
chart = "postgresql"
}
# 2) 既存の Helm リリースを import(namespace/name 形式の ID を利用可能)
# terraform import helm_release.postgres data/postgres
# 3) chart/version/values を合わせ、plan で差分を確認して整合を取るConfine per-environment differences to module inputs and values templates, and always pin Chart versions. That ensures reproducible plans and stable CI/CD applies.
For multi-cluster or multi-namespace setups, split modules per project and switch target clusters via provider aliases. Even when using workspaces, keep readability with an explicit var env.
| Split unit | Scope | Benefit |
|---|---|---|
| Per-chart module | Per app or middleware | Separation of concerns and clear ownership |
| Per-namespace stack | Permission and isolation boundary | Aligns with RBAC design |
| Per-cluster project | Fault tolerance / region | Failure domain isolation and reduced risk |
Module composition and provider alias injection
# ルート側
provider "helm" {
alias = "prod"
kubernetes {
host = var.prod_host
token = var.prod_token
cluster_ca_certificate = var.prod_ca
}
}
module "orders" {
source = "./modules/release"
providers = { helm = helm.prod }
name = "orders"
namespace = "app"
chart_repository= "oci://registry.example.com/helm"
chart_name = "orders"
chart_version = "1.2.3"
env = "prod"
}
# modules/release/main.tf(例)
resource "helm_release" "this" {
name = var.name
namespace = var.namespace
repository = var.chart_repository
chart = var.chart_name
version = var.chart_version
values = [templatefile("${path.module}/values-${var.env}.yaml", {})]
wait = true
timeout = 900
atomic = true
}Handle secret values by combining Terraform's sensitive variables with helm_release's set_sensitive. That minimizes exposure in plan/apply output and in state. Keep kubeconfigs and tokens out of the repository — pass them from short-lived sources for safety.
Follow the principle of least privilege for RBAC and scope permissions to the target namespace. When repository authentication is required, pass repository_username / repository_password and related parameters via variables.
| Risk | Recommended mitigation | Terraform / Helm feature |
|---|---|---|
| Plaintext exposure | Use sensitive variables and set_sensitive | variable.sensitive, set_sensitive |
| Leakage to logs | Suppress output and check during review | Masking of sensitive values |
| Credential distribution | Short-lived tokens and least privilege | Explicit provider auth and RBAC |
Safely passing in secrets
variable "db_password" {
type = string
sensitive = true
}
resource "helm_release" "db" {
name = "db"
repository = "https://charts.bitnami.com/bitnami"
chart = "postgresql"
version = "12.6.0"
namespace = "data"
set_sensitive {
name = "global.postgresql.auth.postgresPassword"
value = var.db_password
}
wait = true
timeout = 1200
atomic = true
}Pro
問題 1
You are deploying a PostgreSQL Helm Chart from Terraform in production. You want to pass the database password safely, keep its value out of terraform plan output, and still maintain diff management. Which approach is most appropriate?
正解: A
set_sensitive masks secrets while still participating in diff management. Plaintext values or set entries carry a high exposure risk, and base64 encoding is not secrecy.
Can I use a local Chart directory?
Yes. You can specify a local path (e.g. ./charts/app) for the chart argument of helm_release. The repository argument is not required in that case.
How do I migrate an existing Helm release under Terraform management?
First declare the matching helm_release resource, then run terraform import. The ID can be the release name (or namespace/name when a namespace is involved). After importing, align chart/version/values with the actual release and verify there is no drift via terraform plan.
How do I use Charts from authenticated repositories or registries?
Pass repository along with credentials such as repository_username / repository_password as variables. Use set_sensitive and sensitive variables for secrets to minimize exposure in plan output and state.
Practice with certification-focused question sets
Try free practice questionsNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
HCL Syntax: Terraform's Configuration Language (2026)
HCL2 fundamentals for Terraform — blocks, attributes, expres...
Terraform Authoring & Operations Pro: Complete Guide (2026)
Tactics for the Terraform Pro exam — module authoring, works...
Terraform Providers: Plugin Management Fundamentals (2026)
Provider mechanics — required_providers, versions, mirrors, ...
Terraform Resource Blocks: Declarative Infra Units (2026)
Resource block fundamentals — addresses, references, common ...
Terraform Data Sources: Read-Only External Data (2026)
Data source basics — declaration, refresh behavior, dependen...