Terraform

Terraform Kubernetes Provider: YAMLとの関係と制限を正しく理解する

2026-04-19
NicheeLab編集部

Kubernetesの運用では、既存のYAML資産とTerraformのHCLによる一貫管理の両立がよく課題になります。本稿では、Terraform Kubernetes ProviderにおけるYAMLとの関係、実務での制限、そして試験で問われやすい判断ポイントをまとめます。

前提として、TerraformはKubernetes APIに対しCRUDを行うツールであり、kubectl applyの挙動(特にサーバーサイド適用のフィールド所有や競合解決)と同一ではありません。公式の安定仕様と一般的なパターンを中心に、バージョン差異が影響しやすい点は注意書きに留めます。

TerraformとYAMLの位置づけ

TerraformはHCLで記述したリソースをプロバイダ経由でKubernetes APIに反映します。一方、kube yamlはkubectl等のクライアントから適用されます。HelmはYAMLテンプレートをレンダリングし、最終的にはKubernetes APIにYAML(もしくは同等のJSON)を流し込みます。

つまり、最終的に到達する先は同じでも、経路と状態管理の思想が異なります。Terraformは状態管理と計画(plan)に強みがあり、kubectl/HelmはKubernetesの文脈での適用柔軟性に強みがあります。

  • HCLで型付きリソースを使う: YAML不要。Terraformの計画と状態管理を最大限活用。
  • YAMLを取り込む: yamldecodeで読んでマニフェスト系リソースに渡す(プロバイダの機能差は要確認)。
  • Helmを使う: values.yamlを渡し、Chartでまとまった配布物を管理。

Kubernetesへの適用経路(概念)

HCL (Terraform)Kubernetes ProviderYAML (manifests)kubectlHCL + values.yaml (Helm)Helm Provider / ClientKubernetes API ServerHCL / YAML / Helm の 3 経路はいずれも最終的に Kubernetes API Server に到達

最小構成のKubernetesプロバイダ(kubeconfigを利用)

terraform {
  required_providers {
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = ">= 2.0"
    }
  }
}

provider "kubernetes" {
  config_path    = var.kubeconfig_path
  config_context = var.kube_context
}

variable "kubeconfig_path" { type = string }
variable "kube_context" { type = string }

型付きリソースの利点と制限

Kubernetes Providerの型付きリソース(例: kubernetes_deployment_v1, kubernetes_service_v1)は、スキーマに沿った事前検証が効き、plan時点での差分可視化が明瞭です。Terraformの参照やfor_eachとの相性も良く、資格試験でも基本パターンとして押さえるべきです。

一方で、すべてのKubernetes APIやCRDが型付きでカバーされるわけではありません。Admission/Mutating Webhookやコントローラによるサーバー側のデフォルト付与・書き換えにより、Terraformが意図しない差分を検出する場合があります。特に、Podテンプレートの注釈やサイドカー自動注入などはplanで毎回差分になりがちです。その場合は、Terraformのlifecycle.ignore_changesで特定フィールドを無視する運用が一般的です。

  • 同一オブジェクトをTerraformとkubectl/他ツールで“併用管理”しない(ドリフトの主因)。
  • Admission/Mutatingで変わる可能性がある注釈やフィールドはignore_changesで緩和。
  • 順序依存するリソース(CRD→CRなど)はdepends_onや分割適用で制御。
  • リストの順序が差分要因にならないよう、可能ならset型や安定化したソートを利用。

DeploymentをHCLで管理し、注釈差分を抑制する例

resource "kubernetes_deployment_v1" "web" {
  metadata {
    name      = "web"
    namespace = "default"
    labels = { app = "web" }
    # Admissionやコントローラで変わりやすい注釈は無視
    annotations = { managed-by = "terraform" }
  }

  spec {
    replicas = 2

    selector { match_labels = { app = "web" } }

    template {
      metadata {
        labels = { app = "web" }
        annotations = { sidecar.istio.io/inject = "false" }
      }
      spec {
        container {
          name  = "nginx"
          image = "nginx:1.25"
          port { container_port = 80 }
        }
      }
    }
  }

  lifecycle {
    ignore_changes = [
      metadata[0].annotations,
      spec[0].template[0].metadata[0].annotations
    ]
  }
}

YAMLを直接扱うときの実装パターン

既存のYAMLをそのまま適用したい場合、Terraformのyamldecode関数でHCLのオブジェクトに変換し、汎用マニフェスト系のリソース(例: kubernetes_manifest など、提供可否や名称はプロバイダのバージョンに依存)に渡す方法があります。この方法はCRDや型未対応のAPIにも適用できるため、現実的な落としどころです。

注意点として、YAMLのマルチドキュメントを一括適用する際は分割し、for_eachで安定キー(kind/name/namespace)を付けて管理します。また、CRDとCRは明示的に順序制御(depends_on)してください。

  • 単一YAMLはyamldecode(file(path))でオブジェクト化して渡す。
  • 複数YAMLはfilesetで集めfor_eachで安定キー管理。
  • CRD→CRはdepends_onで順序を保証。
  • プロバイダの提供有無・挙動はドキュメントで確認(バージョン差異に注意)。

YAMLを読み込み、汎用マニフェストに渡す(単一ドキュメント)

locals {
  deploy = yamldecode(file("${path.module}/manifests/deploy.yaml"))
}

# プロバイダが提供する汎用マニフェスト系リソース名はバージョンで異なる場合があります
resource "kubernetes_manifest" "deploy" {
  manifest = local.deploy
}

Helm経由のYAMLとTerraform

Helm Providerのhelm_releaseは、Chartの配布物をTerraformのライフサイクルで管理します。values.yamlはそのまま文字列として渡すか、HCLからyamlencodeで生成して渡せます。ChartがCRDを含む場合、CR適用の前にCRDが存在するよう順序を設計します。

helm_releaseはKubernetesオブジェクトを個別リソースとしてTerraform状態に保持するのではなく、リリース単位での管理になります。個々のオブジェクト単位の詳細な差分管理が必要なら、Helmとマニフェスト系リソースを役割分担する設計が適しています。

  • valuesはfile("values.yaml")またはyamlencode({ ... })で渡す。
  • CRDを含むChartは、CRを適用する他リソースより前にapply(depends_onなど)。
  • Chartアップグレード時の破壊的変更はplanで検知しづらい場合があるため慎重に。

helm_releaseでChartを適用し、values.yamlを渡す

terraform {
  required_providers {
    helm = {
      source  = "hashicorp/helm"
      version = ">= 2.9"
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = ">= 2.0"
    }
  }
}

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"
  namespace  = "web"
  create_namespace = true

  values = [file("${path.module}/values.yaml")]
}

サーバー側のデフォルト・ドリフトの扱い

Kubernetes APIサーバーや各種コントローラは、受け取ったオブジェクトにデフォルト値を付与したり、注釈やフィールドを書き換えます。kubectl apply(特にサーバーサイド適用)とTerraformの適用はフィールド所有や競合解決の挙動が異なるため、混在運用はドリフトの原因になります。基本はどちらかに統一し、やむを得ず併用する場合も責務分離を明確にしてください。

CRDとCRの整合性、イベントチュアルコンシステンシ(適用直後に参照すると未反映のことがある)、リスト順序や生成フィールドの差分は、Terraformのdepends_on、time_sleep(必要な場合)、ignore_changes、for_eachの安定キー化などで抑制します。

  • 同一オブジェクトの多元管理を避ける(Terraformかkubectl/Helmかを決める)。
  • 生成・変異される注釈やstatusは管理対象にしない(ignore_changes等)。
  • CRD→CRは明示的に順序制御。適用直後の参照はリトライ設計。
  • リスト順序の不安定差分はキー化やソートで回避。

CRD→CRの順序保証(HelmでCRD、YAMLでCRを適用)

locals {
  cr_files = fileset("${path.module}/crs", "*.yaml")
  cr_objs  = [for f in local.cr_files : yamldecode(file("${path.module}/crs/${f}"))]
  cr_map   = { for m in local.cr_objs : "${m["kind"]}/${m["metadata"]["namespace"]}/${m["metadata"]["name"]}" => m }
}

# 例: CRDを含むChart
resource "helm_release" "crds" {
  name       = "operator-crds"
  repository = var.operator_repo
  chart      = var.operator_crd_chart
  namespace  = "operators"
}

# CRをYAMLから適用(汎用マニフェスト系リソース想定)
resource "kubernetes_manifest" "cr" {
  for_each = local.cr_map
  manifest = each.value
  depends_on = [helm_release.crds]
}

variable "operator_repo" { type = string }
variable "operator_crd_chart" { type = string }

試験と実務で押さえる要点と比較

Associate/Pro試験では、HCLでの型付き管理・YAML取り込み・Helmの使い分け、そしてドリフト抑制の判断が頻出です。実務でも、CRDや既存YAML資産との共存、Secretの取り扱い、順序制御が成功の鍵になります。

以下の比較表で、アプローチごとの特徴を整理します。

  • yamldecode/yamlencode、file/fileset、for_each・depends_onの基本を確実に。
  • SecretはTerraform状態に平文で残り得る。外部シークレット管理(例: Vault、External Secrets等)や暗号化されたリモートバックエンドを検討。
  • 同一リソースを複数ツールで変更しない。変更責務を分離。
アプローチYAMLの扱い適合ユースケース主な制限
Kubernetes型付きリソース(HCL)不要(HCLで宣言)Deployment/Serviceなど主要リソースを厳密に管理CRDや未対応APIは対象外。Webhook変異で差分が出やすい
汎用マニフェスト適用(yamldecode + manifest系)YAMLをそのまま適用(HCLにデコード)CRD/CRや型未対応API、既存YAML資産の流用プロバイダ機能・バージョン差異に注意。フィールド単位の厳密検証は弱め
Helm(helm_release)values.yamlでテンプレートへ注入複数オブジェクトをChartとして一括配布・更新状態はリリース単位。オブジェクト単位の微細制御は弱い
kubectlをlocal-execで実行(参考)YAMLを直接kubectl apply最小限の移行や一時的対応Terraformの計画・状態管理と乖離。再現性・ドリフトに弱い

マルチドキュメントYAMLを分割してfor_each適用するスニペット

locals {
  # --- 区切りで分割(改行を含む区切りを考慮)。空要素は除外
  raw  = file("${path.module}/bundle.yaml")
  docs = [for d in split("\n---\n", local.raw) : d if trimspace(d) != ""]
  objs = [for d in local.docs : yamldecode(d)]
  mp   = { for m in local.objs : "${m["kind"]}/${coalesce(try(m["metadata"]["namespace"], null), "default")}/${m["metadata"]["name"]}" => m }
}

resource "kubernetes_manifest" "multi" {
  for_each = local.mp
  manifest = each.value
}

問題で確認

Associate / Pro

問題 1

既存のYAMLで定義された複数のCustomResource(CR)をTerraformで管理したい。CRDはHelm Chartで提供され、まずCRDを適用し、その後CRを適用する必要がある。ドリフトを最小化し、計画可能性を確保するパターンとして最も適切なのはどれか。

  1. HelmでCRDのChartをhelm_releaseで適用し、CRはyamldecodeで読み込んだオブジェクトを汎用マニフェスト系リソースでfor_each適用。CR側はhelm_releaseにdepends_onを設定する
  2. すべてkubectl applyをnull_resource + local-execで順番に実行し、Terraformのstateには登録しない
  3. 型付きリソース(kubernetes_deployment_v1等)にYAMLを変換して無理やりマッピングする
  4. Helmのvalues.yamlにCRをすべて埋め込み、単一のhelm_releaseで同時適用する(依存関係は任せる)

正解: A

CRDはhelm_releaseで先に適用し、CRはYAMLをyamldecodeで読み込み汎用マニフェスト系リソースで管理、さらにdepends_onで順序を保証するのがTerraform的に計画可能でドリフトを抑えやすい。local-execでのkubectlは状態管理がなく不適。型付きへの無理なマッピングは非現実的。CRをvaluesに埋める方式は依存順制御や個別差分の可視性が弱い。

よくある質問

Terraformはkubectl apply(サーバーサイド適用)と同じ挙動ですか?

いいえ。TerraformはKubernetes APIに対してCRUDを行います。kubectlのサーバーサイド適用が持つフィールド所有や競合解決の挙動とは異なります。プロバイダの実装・バージョンによっては近いモードを提供する場合もありますが、前提にせず公式ドキュメントを確認してください。混在運用はドリフトの原因になります。

マルチドキュメントのYAML(---区切り)はTerraformでそのまま扱えますか?

そのまま1つのオブジェクトとしては扱えません。fileで読み込み、---で分割してからyamldecodeでオブジェクト化し、for_eachで個別に適用するのが一般的です。安定したキー(kind/namespace/name)を作ると計画と差分が安定します。

SecretをTerraformで管理しても安全ですか?

Terraformの状態ファイルにSecretの値が保存される可能性があります。Terraform Cloud/Enterpriseや暗号化されたリモートバックエンドの利用、またはVaultやExternal Secretsなど外部シークレット管理と組み合わせて、Terraform側には参照や束ねだけを任せる設計を推奨します。

この記事で学んだ内容を問題で確認しましょう

16,000問以上の問題で実力チェック

無料で問題を解いてみる
この記事の著者

NicheeLab編集部

データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。


関連記事
Terraform

Terraform HCL 構文の基礎:Block / Attribute / Expression を正しく使い分ける

Terraform Associate で頻出の HCL 構文を、ブロック・属性・式の3視点で整理。実務で迷いがちな書き...

Terraform

Terraform Authoring & Ops Pro: 上位資格の範囲と対策

上位レベルを想定したTerraformの設計・運用ドメインを整理し、実務で通用する対策を提示。モジュール設計、ステート運...

Terraform

Terraform Providers の基本: プラグイン型アーキテクチャを正しく使いこなす

Associate レベルで押さえるべき Provider の基礎、インストール、バージョニング、認証、エイリアス運用を...

Terraform

Terraform Resourceブロック徹底ガイド: 最小単位のリソース定義

Associateレベルで押さえるべきResourceブロックの構造、依存関係、メタ引数、ライフサイクル制御を実務目線で...

Terraform

Terraform Data Source徹底理解:既存リソースの参照で壊さず足す

Terraform Associate向けに、Data Sourceを用いた既存リソース参照の基本、選択基準、評価順序、...

Terraformの記事一覧 (101件)
© 2026 NicheeLab All rights reserved.