HCP Terraform Migration Implementation Plan¶
ARCHIVED: 2025-12-28 Reason: Migration completed (commits #436-#449) See:
docs/hcp-terraform.mdfor current stateFor Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Migrate Terraform execution from Windmill to HCP Terraform self-hosted agents with VCS-driven workflow and Vault OIDC dynamic credentials.
Architecture: Deploy HCP TF Operator in Kubernetes, create tf/hcp-terraform module to manage workspaces, add Vault OIDC auth for dynamic credentials, and deploy Cloudflare Worker for Discord notifications.
Tech Stack: Terraform (tfe provider), Kubernetes (HCP TF Operator), Vault (JWT auth), Cloudflare Workers
Design Doc: docs/plans/2025-12-26-hcp-terraform-migration-design.md
Phase 1: HCP Terraform Module¶
Create the tf/hcp-terraform module that manages workspaces, agent pool, and notifications.
Task 1.1: Create Module Structure¶
Files:
- Create: tf/hcp-terraform/versions.tf
- Create: tf/hcp-terraform/terraform.tf
- Create: tf/hcp-terraform/variables.tf
Step 1: Create versions.tf
// versions.tf - Required versions for Terraform and providers
terraform {
required_version = ">= 1.12.0"
required_providers {
tfe = {
source = "hashicorp/tfe"
version = "~> 0.62"
}
}
}
Step 2: Create terraform.tf
// terraform.tf - Provider and backend configuration
terraform {
cloud {
organization = "fzymgc-house"
workspaces {
tags = ["main-cluster", "hcp-terraform"]
}
}
}
provider "tfe" {
# Uses TFE_TOKEN environment variable
}
Step 3: Create variables.tf
// variables.tf - Input variables
variable "organization" {
description = "HCP Terraform organization name"
type = string
default = "fzymgc-house"
}
variable "github_repo" {
description = "GitHub repository identifier"
type = string
default = "fzymgc-house/selfhosted-cluster"
}
variable "discord_webhook_url" {
description = "Cloudflare Worker URL for Discord notifications"
type = string
sensitive = true
}
Step 4: Validate module structure
Run: cd /workspaces/selfhosted-cluster/.worktrees/hcp-terraform-migration/tf/hcp-terraform && terraform fmt -check
Expected: No formatting issues
Step 5: Commit
git add tf/hcp-terraform/
git commit -m "feat(hcp-terraform): add module structure with versions and variables"
Task 1.2: Create Agent Pool Configuration¶
Files:
- Create: tf/hcp-terraform/agent_pool.tf
- Create: tf/hcp-terraform/outputs.tf
Step 1: Create agent_pool.tf
// agent_pool.tf - Agent pool and token configuration
resource "tfe_agent_pool" "main" {
name = "fzymgc-house-k8s"
organization = var.organization
organization_scoped = true
}
resource "tfe_agent_token" "k8s" {
agent_pool_id = tfe_agent_pool.main.id
description = "Kubernetes cluster agent"
}
Step 2: Create outputs.tf
// outputs.tf - Output values
output "agent_pool_id" {
description = "Agent pool ID for workspace configuration"
value = tfe_agent_pool.main.id
}
output "agent_token" {
description = "Agent token for Kubernetes deployment (store in Vault)"
value = tfe_agent_token.k8s.token
sensitive = true
}
Step 3: Validate
Run: terraform fmt -check tf/hcp-terraform/
Expected: No formatting issues
Step 4: Commit
git add tf/hcp-terraform/agent_pool.tf tf/hcp-terraform/outputs.tf
git commit -m "feat(hcp-terraform): add agent pool and token configuration"
Task 1.3: Create Workspace Imports and Configuration¶
Files:
- Create: tf/hcp-terraform/imports.tf
- Create: tf/hcp-terraform/workspaces.tf
- Create: tf/hcp-terraform/data.tf
Step 1: Create data.tf for existing GitHub OAuth
// data.tf - Data sources for existing resources
data "tfe_oauth_client" "github" {
organization = var.organization
service_provider = "github"
}
Step 2: Create imports.tf for existing workspaces
// imports.tf - Import blocks for existing workspaces
import {
to = tfe_workspace.this["vault"]
id = "fzymgc-house/vault"
}
import {
to = tfe_workspace.this["authentik"]
id = "fzymgc-house/authentik"
}
import {
to = tfe_workspace.this["cloudflare"]
id = "fzymgc-house/cloudflare"
}
import {
to = tfe_workspace.this["cluster-bootstrap"]
id = "fzymgc-house/cluster-bootstrap"
}
Step 3: Create workspaces.tf
// workspaces.tf - Workspace configuration
locals {
all_workspaces = {
vault = {
dir = "tf/vault"
tags = ["main-cluster", "vault"]
}
authentik = {
dir = "tf/authentik"
tags = ["main-cluster", "authentik"]
}
grafana = {
dir = "tf/grafana"
tags = ["main-cluster", "grafana"]
}
cloudflare = {
dir = "tf/cloudflare"
tags = ["main-cluster", "cloudflared"]
}
core-services = {
dir = "tf/core-services"
tags = ["main-cluster", "core-services"]
}
cluster-bootstrap = {
dir = "tf/cluster-bootstrap"
tags = ["main-cluster", "bootstrap"]
}
}
}
resource "tfe_workspace" "this" {
for_each = local.all_workspaces
name = each.key
organization = var.organization
working_directory = each.value.dir
tag_names = each.value.tags
# Agent execution mode
execution_mode = "agent"
agent_pool_id = tfe_agent_pool.main.id
# VCS-driven workflow
auto_apply = true
speculative_enabled = true
file_triggers_enabled = true
vcs_repo {
identifier = var.github_repo
branch = "main"
oauth_token_id = data.tfe_oauth_client.github.oauth_token_id
}
}
Step 4: Validate
Run: terraform fmt -check tf/hcp-terraform/
Expected: No formatting issues
Step 5: Commit
git add tf/hcp-terraform/data.tf tf/hcp-terraform/imports.tf tf/hcp-terraform/workspaces.tf
git commit -m "feat(hcp-terraform): add workspace imports and VCS configuration"
Task 1.4: Create Notification Configuration¶
Files:
- Create: tf/hcp-terraform/notifications.tf
Step 1: Create notifications.tf
// notifications.tf - Discord notification configuration via Cloudflare Worker
resource "tfe_notification_configuration" "discord" {
for_each = tfe_workspace.this
workspace_id = each.value.id
name = "discord"
enabled = true
destination_type = "generic"
url = var.discord_webhook_url
triggers = [
"run:planning",
"run:applying",
"run:completed",
"run:errored",
]
}
Step 2: Validate
Run: terraform fmt -check tf/hcp-terraform/
Expected: No formatting issues
Step 3: Commit
git add tf/hcp-terraform/notifications.tf
git commit -m "feat(hcp-terraform): add Discord notification configuration"
Phase 2: Vault OIDC Configuration¶
Add JWT auth method and per-workspace roles to the Vault module.
Task 2.1: Create JWT Auth Backend¶
Files:
- Create: tf/vault/jwt-hcp-terraform.tf
Step 1: Create JWT auth backend configuration
// jwt-hcp-terraform.tf - HCP Terraform workload identity
resource "vault_jwt_auth_backend" "hcp_terraform" {
path = "jwt-hcp-terraform"
type = "jwt"
oidc_discovery_url = "https://app.terraform.io"
bound_issuer = "https://app.terraform.io"
description = "HCP Terraform workload identity for dynamic credentials"
}
Step 2: Validate
Run: terraform fmt -check tf/vault/
Expected: No formatting issues
Step 3: Commit
git add tf/vault/jwt-hcp-terraform.tf
git commit -m "feat(vault): add HCP Terraform JWT auth backend"
Task 2.2: Create Per-Workspace Vault Roles¶
Files:
- Modify: tf/vault/jwt-hcp-terraform.tf
Step 1: Add Vault role
Append to tf/vault/jwt-hcp-terraform.tf:
resource "vault_jwt_auth_backend_role" "tfc_vault" {
backend = vault_jwt_auth_backend.hcp_terraform.path
role_name = "tfc-vault"
bound_audiences = ["vault.workload.identity"]
bound_claims = {
sub = "organization:fzymgc-house:project:*:workspace:vault:run_phase:*"
}
user_claim = "terraform_workspace_name"
role_type = "jwt"
token_ttl = 1200
token_policies = ["terraform-vault-admin"]
}
resource "vault_jwt_auth_backend_role" "tfc_authentik" {
backend = vault_jwt_auth_backend.hcp_terraform.path
role_name = "tfc-authentik"
bound_audiences = ["vault.workload.identity"]
bound_claims = {
sub = "organization:fzymgc-house:project:*:workspace:authentik:run_phase:*"
}
user_claim = "terraform_workspace_name"
role_type = "jwt"
token_ttl = 1200
token_policies = ["terraform-authentik-admin"]
}
resource "vault_jwt_auth_backend_role" "tfc_grafana" {
backend = vault_jwt_auth_backend.hcp_terraform.path
role_name = "tfc-grafana"
bound_audiences = ["vault.workload.identity"]
bound_claims = {
sub = "organization:fzymgc-house:project:*:workspace:grafana:run_phase:*"
}
user_claim = "terraform_workspace_name"
role_type = "jwt"
token_ttl = 1200
token_policies = ["terraform-grafana-admin"]
}
resource "vault_jwt_auth_backend_role" "tfc_cloudflare" {
backend = vault_jwt_auth_backend.hcp_terraform.path
role_name = "tfc-cloudflare"
bound_audiences = ["vault.workload.identity"]
bound_claims = {
sub = "organization:fzymgc-house:project:*:workspace:cloudflare:run_phase:*"
}
user_claim = "terraform_workspace_name"
role_type = "jwt"
token_ttl = 1200
token_policies = ["terraform-cloudflare-admin"]
}
resource "vault_jwt_auth_backend_role" "tfc_core_services" {
backend = vault_jwt_auth_backend.hcp_terraform.path
role_name = "tfc-core-services"
bound_audiences = ["vault.workload.identity"]
bound_claims = {
sub = "organization:fzymgc-house:project:*:workspace:core-services:run_phase:*"
}
user_claim = "terraform_workspace_name"
role_type = "jwt"
token_ttl = 1200
token_policies = ["terraform-core-services-admin"]
}
Step 2: Validate
Run: terraform fmt -check tf/vault/
Expected: No formatting issues
Step 3: Commit
git add tf/vault/jwt-hcp-terraform.tf
git commit -m "feat(vault): add per-workspace OIDC roles for HCP Terraform"
Task 2.3: Create Vault Policies for Terraform Workspaces¶
Files:
- Create: tf/vault/policy-terraform-workspaces.tf
Step 1: Create policies
Note: These policies define what each HCP TF workspace can access. Adjust paths based on actual secret paths used by each module.
// policy-terraform-workspaces.tf - Policies for HCP Terraform workspace OIDC auth
# Vault workspace - manages Vault configuration
data "vault_policy_document" "terraform_vault_admin" {
rule {
path = "auth/*"
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
description = "Manage auth methods"
}
rule {
path = "sys/auth/*"
capabilities = ["create", "read", "update", "delete", "sudo"]
description = "Manage auth method configuration"
}
rule {
path = "sys/policies/*"
capabilities = ["create", "read", "update", "delete", "list"]
description = "Manage policies"
}
rule {
path = "identity/*"
capabilities = ["create", "read", "update", "delete", "list"]
description = "Manage identity entities and groups"
}
rule {
path = "secret/data/fzymgc-house/*"
capabilities = ["read", "list"]
description = "Read secrets for configuration"
}
}
resource "vault_policy" "terraform_vault_admin" {
name = "terraform-vault-admin"
policy = data.vault_policy_document.terraform_vault_admin.hcl
}
# Authentik workspace - manages Authentik secrets
data "vault_policy_document" "terraform_authentik_admin" {
rule {
path = "secret/data/fzymgc-house/cluster/authentik"
capabilities = ["read", "list"]
description = "Read Authentik secrets"
}
rule {
path = "secret/metadata/fzymgc-house/cluster/authentik"
capabilities = ["read", "list"]
description = "Read Authentik secret metadata"
}
}
resource "vault_policy" "terraform_authentik_admin" {
name = "terraform-authentik-admin"
policy = data.vault_policy_document.terraform_authentik_admin.hcl
}
# Grafana workspace - manages Grafana secrets
data "vault_policy_document" "terraform_grafana_admin" {
rule {
path = "secret/data/fzymgc-house/cluster/grafana"
capabilities = ["read", "list"]
description = "Read Grafana secrets"
}
rule {
path = "secret/metadata/fzymgc-house/cluster/grafana"
capabilities = ["read", "list"]
description = "Read Grafana secret metadata"
}
}
resource "vault_policy" "terraform_grafana_admin" {
name = "terraform-grafana-admin"
policy = data.vault_policy_document.terraform_grafana_admin.hcl
}
# Cloudflare workspace - manages Cloudflare secrets
data "vault_policy_document" "terraform_cloudflare_admin" {
rule {
path = "secret/data/fzymgc-house/cluster/cloudflare"
capabilities = ["read", "list"]
description = "Read Cloudflare secrets"
}
rule {
path = "secret/metadata/fzymgc-house/cluster/cloudflare"
capabilities = ["read", "list"]
description = "Read Cloudflare secret metadata"
}
}
resource "vault_policy" "terraform_cloudflare_admin" {
name = "terraform-cloudflare-admin"
policy = data.vault_policy_document.terraform_cloudflare_admin.hcl
}
# Core-services workspace - manages core service secrets
data "vault_policy_document" "terraform_core_services_admin" {
rule {
path = "secret/data/fzymgc-house/cluster/*"
capabilities = ["read", "list"]
description = "Read cluster secrets"
}
rule {
path = "secret/metadata/fzymgc-house/cluster/*"
capabilities = ["read", "list"]
description = "Read cluster secret metadata"
}
}
resource "vault_policy" "terraform_core_services_admin" {
name = "terraform-core-services-admin"
policy = data.vault_policy_document.terraform_core_services_admin.hcl
}
Step 2: Validate
Run: terraform fmt -check tf/vault/
Expected: No formatting issues
Step 3: Commit
git add tf/vault/policy-terraform-workspaces.tf
git commit -m "feat(vault): add Vault policies for HCP Terraform workspaces"
Phase 3: Agent Deployment (ArgoCD)¶
Deploy the HCP Terraform Operator and agent via ArgoCD.
Task 3.1: Create HCP Terraform Operator App Config¶
Files:
- Create: argocd/app-configs/hcp-terraform-operator/kustomization.yaml
- Create: argocd/app-configs/hcp-terraform-operator/namespace.yaml
- Create: argocd/app-configs/hcp-terraform-operator/values.yaml
- Create: argocd/app-configs/hcp-terraform-operator/external-secrets.yaml
Step 1: Create namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: hcp-terraform
labels:
app.kubernetes.io/name: hcp-terraform-operator
Step 2: Create external-secrets.yaml
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: hcp-terraform-agent-token
namespace: hcp-terraform
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: hcp-terraform-agent-token
creationPolicy: Owner
data:
- secretKey: token
remoteRef:
key: secret/fzymgc-house/cluster/hcp-terraform
property: agent_token
Step 3: Create values.yaml
# HCP Terraform Operator Helm values
# See: https://github.com/hashicorp/hcp-terraform-operator
replicaCount: 1
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
Step 4: Create kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: hcp-terraform
resources:
- namespace.yaml
- external-secrets.yaml
# Helm values will be referenced by ArgoCD application
Step 5: Commit
git add argocd/app-configs/hcp-terraform-operator/
git commit -m "feat(argocd): add HCP Terraform Operator app config"
Task 3.2: Create Agent Pool CRD¶
Files:
- Create: argocd/app-configs/hcp-terraform-operator/agent-pool.yaml
- Modify: argocd/app-configs/hcp-terraform-operator/kustomization.yaml
Step 1: Create agent-pool.yaml
apiVersion: app.terraform.io/v1alpha2
kind: AgentPool
metadata:
name: fzymgc-house-agents
namespace: hcp-terraform
spec:
organization: fzymgc-house
token:
secretKeyRef:
name: hcp-terraform-agent-token
key: token
agentTokens:
- name: k8s-agent
agentDeployment:
replicas: 1
spec:
containers:
- name: tfc-agent
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
Step 2: Update kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: hcp-terraform
resources:
- namespace.yaml
- external-secrets.yaml
- agent-pool.yaml
# Helm values will be referenced by ArgoCD application
Step 3: Commit
git add argocd/app-configs/hcp-terraform-operator/
git commit -m "feat(argocd): add AgentPool CRD for HCP Terraform"
Task 3.3: Create ArgoCD Application¶
Files:
- Create: argocd/cluster-app/templates/hcp-terraform-operator.yaml
Step 1: Create ArgoCD application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: hcp-terraform-operator
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
project: default
ignoreDifferences:
- group: apiextensions.k8s.io
kind: CustomResourceDefinition
jsonPointers:
- /metadata/annotations/kubectl.kubernetes.io~1last-applied-configuration
- /status
sources:
- repoURL: https://helm.releases.hashicorp.com
chart: hcp-terraform-operator
targetRevision: 0.6.0
helm:
valueFiles:
- $values/argocd/app-configs/hcp-terraform-operator/values.yaml
- repoURL: https://github.com/fzymgc-house/selfhosted-cluster
targetRevision: HEAD
ref: values
- repoURL: https://github.com/fzymgc-house/selfhosted-cluster
targetRevision: HEAD
path: argocd/app-configs/hcp-terraform-operator
destination:
server: https://kubernetes.default.svc
namespace: hcp-terraform
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
Step 2: Commit
git add argocd/cluster-app/templates/hcp-terraform-operator.yaml
git commit -m "feat(argocd): add HCP Terraform Operator ArgoCD application"
Phase 4: Cloudflare Worker for Discord¶
Create the webhook transformer.
Task 4.1: Create Cloudflare Worker¶
Files:
- Create: cloudflare/workers/hcp-terraform-discord/worker.js
- Create: cloudflare/workers/hcp-terraform-discord/wrangler.toml
Step 1: Create worker.js
// HCP Terraform to Discord webhook transformer
export default {
async fetch(request, env) {
// Only accept POST requests
if (request.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
try {
const payload = await request.json();
// Extract notification data
const notification = payload.notifications?.[0];
if (!notification) {
return new Response("No notification data", { status: 400 });
}
// Color mapping for run status
const colors = {
"planned": 0x3498db, // blue
"applied": 0x2ecc71, // green
"errored": 0xe74c3c, // red
"canceled": 0x95a5a6, // gray
"planning": 0xf39c12, // orange
"applying": 0xf39c12, // orange
"discarded": 0x95a5a6, // gray
};
const statusEmoji = {
"planned": "📋",
"applied": "✅",
"errored": "❌",
"canceled": "🚫",
"planning": "🔄",
"applying": "🔄",
"discarded": "🗑️",
};
const status = notification.run_status || "unknown";
const color = colors[status] || 0x7289da;
const emoji = statusEmoji[status] || "❓";
// Build Discord embed
const embed = {
embeds: [{
title: `${emoji} Terraform ${status.charAt(0).toUpperCase() + status.slice(1)}`,
description: [
`**Workspace:** ${payload.workspace_name || "unknown"}`,
`**Run:** [${notification.run_id}](${notification.run_url})`,
notification.run_message ? `**Message:** ${notification.run_message}` : null,
].filter(Boolean).join("\n"),
color: color,
timestamp: new Date().toISOString(),
footer: {
text: "HCP Terraform",
},
}],
};
// Send to Discord
const discordResponse = await fetch(env.DISCORD_WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(embed),
});
if (!discordResponse.ok) {
console.error("Discord error:", await discordResponse.text());
return new Response("Discord webhook failed", { status: 502 });
}
return new Response("OK", { status: 200 });
} catch (error) {
console.error("Error processing webhook:", error);
return new Response("Internal error", { status: 500 });
}
},
};
Step 2: Create wrangler.toml
name = "hcp-terraform-discord"
main = "worker.js"
compatibility_date = "2024-01-01"
# Secret: DISCORD_WEBHOOK_URL
# Set via: wrangler secret put DISCORD_WEBHOOK_URL
Step 3: Commit
git add cloudflare/workers/hcp-terraform-discord/
git commit -m "feat(cloudflare): add HCP Terraform Discord webhook worker"
Phase 5: Module Provider Updates¶
Update each module to use OIDC authentication.
Task 5.1: Update Vault Module Provider¶
Files:
- Modify: tf/vault/terraform.tf
- Modify: tf/vault/variables.tf
Step 1: Update terraform.tf
Replace the current provider configuration with OIDC support:
// terraform.tf - Provider and backend configuration
terraform {
cloud {
organization = "fzymgc-house"
workspaces {
tags = ["main-cluster", "vault"]
}
}
}
provider "vault" {
address = var.vault_addr
# Use OIDC when running in HCP TF, fallback to token for local dev
dynamic "auth_login_jwt" {
for_each = var.tfc_workload_identity_token_path != "" ? [1] : []
content {
role = "tfc-vault"
jwt = file(var.tfc_workload_identity_token_path)
}
}
}
provider "kubernetes" {
config_path = "~/.kube/configs/fzymgc-house-admin.yml"
config_context = "fzymgc-house"
}
Step 2: Update variables.tf
Add OIDC variable:
variable "vault_addr" {
description = "Vault server address"
type = string
default = "https://vault.fzymgc.house"
}
variable "tfc_workload_identity_token_path" {
description = "Path to HCP TF workload identity JWT (empty for local dev)"
type = string
default = ""
}
Step 3: Validate
Run: terraform fmt -check tf/vault/
Expected: No formatting issues
Step 4: Commit
git add tf/vault/terraform.tf tf/vault/variables.tf
git commit -m "feat(vault): add OIDC provider config for HCP Terraform"
Task 5.2: Update Grafana Module (Add Cloud Backend)¶
Files:
- Modify: tf/grafana/terraform.tf
- Modify: tf/grafana/variables.tf
Step 1: Update terraform.tf
Replace backend "local" {} with cloud and OIDC:
// terraform.tf - Provider and backend configuration
terraform {
cloud {
organization = "fzymgc-house"
workspaces {
tags = ["main-cluster", "grafana"]
}
}
}
provider "vault" {
address = var.vault_addr
dynamic "auth_login_jwt" {
for_each = var.tfc_workload_identity_token_path != "" ? [1] : []
content {
role = "tfc-grafana"
jwt = file(var.tfc_workload_identity_token_path)
}
}
}
provider "grafana" {
url = var.grafana_url
auth = data.vault_generic_secret.grafana.data["admin_password"]
}
Step 2: Update variables.tf
Add required variables:
variable "vault_addr" {
description = "Vault server address"
type = string
default = "https://vault.fzymgc.house"
}
variable "grafana_url" {
description = "Grafana server URL"
type = string
default = "https://grafana.fzymgc.house"
}
variable "tfc_workload_identity_token_path" {
description = "Path to HCP TF workload identity JWT (empty for local dev)"
type = string
default = ""
}
Step 3: Validate
Run: terraform fmt -check tf/grafana/
Expected: No formatting issues
Step 4: Commit
git add tf/grafana/terraform.tf tf/grafana/variables.tf
git commit -m "feat(grafana): migrate to HCP TF cloud backend with OIDC"
Task 5.3: Update Core-Services Module (Add Cloud Backend)¶
Files:
- Create: tf/core-services/terraform.tf (currently empty)
- Modify: tf/core-services/variables.tf
Step 1: Create terraform.tf
// terraform.tf - Provider and backend configuration
terraform {
cloud {
organization = "fzymgc-house"
workspaces {
tags = ["main-cluster", "core-services"]
}
}
}
provider "kubernetes" {
config_path = "~/.kube/configs/fzymgc-house-admin.yml"
config_context = "fzymgc-house"
}
provider "helm" {
kubernetes {
config_path = "~/.kube/configs/fzymgc-house-admin.yml"
config_context = "fzymgc-house"
}
}
Step 2: Validate
Run: terraform fmt -check tf/core-services/
Expected: No formatting issues
Step 3: Commit
git add tf/core-services/terraform.tf
git commit -m "feat(core-services): add HCP TF cloud backend configuration"
Task 5.4: Update Remaining Modules (Authentik, Cloudflare)¶
Files:
- Modify: tf/authentik/terraform.tf
- Modify: tf/authentik/variables.tf
- Modify: tf/cloudflare/terraform.tf
- Modify: tf/cloudflare/variables.tf
Apply the same OIDC pattern to each module. The key changes are:
1. Add tfc_workload_identity_token_path variable
2. Add dynamic auth_login_jwt block to vault provider
3. Use appropriate role name (tfc-authentik, tfc-cloudflare)
Step 1: Commit after each module update
git add tf/authentik/terraform.tf tf/authentik/variables.tf
git commit -m "feat(authentik): add OIDC provider config for HCP Terraform"
git add tf/cloudflare/terraform.tf tf/cloudflare/variables.tf
git commit -m "feat(cloudflare): add OIDC provider config for HCP Terraform"
Phase 6: Store Agent Token in Vault¶
Task 6.1: Store Agent Token¶
Manual step - Run after tf/hcp-terraform is applied:
# Get the agent token from terraform output
cd tf/hcp-terraform
AGENT_TOKEN=$(terraform output -raw agent_token)
# Store in Vault
vault kv put secret/fzymgc-house/cluster/hcp-terraform agent_token="$AGENT_TOKEN"
Phase 7: Documentation Updates¶
Task 7.1: Update docs/windmill.md¶
Files:
- Modify: docs/windmill.md
Add deprecation notice and reference to HCP TF:
> **⚠️ DEPRECATED**: Terraform execution has migrated to HCP Terraform. See the HCP Terraform migration design doc for details. Windmill flows are archived but not deleted for rollback capability.
Task 7.2: Create docs/hcp-terraform.md¶
Files:
- Create: docs/hcp-terraform.md
Document the new HCP Terraform setup for ongoing operations.
Execution Checklist¶
| Phase | Task | Status |
|---|---|---|
| 1 | Create tf/hcp-terraform module structure | ⬜ |
| 1 | Add agent pool configuration | ⬜ |
| 1 | Add workspace imports and configuration | ⬜ |
| 1 | Add notification configuration | ⬜ |
| 2 | Create JWT auth backend in Vault | ⬜ |
| 2 | Create per-workspace Vault roles | ⬜ |
| 2 | Create Vault policies for workspaces | ⬜ |
| 3 | Create ArgoCD app config for operator | ⬜ |
| 3 | Create AgentPool CRD | ⬜ |
| 3 | Create ArgoCD Application | ⬜ |
| 4 | Create Cloudflare Worker | ⬜ |
| 5 | Update Vault module provider | ⬜ |
| 5 | Update Grafana module (add cloud backend) | ⬜ |
| 5 | Update Core-Services module | ⬜ |
| 5 | Update Authentik and Cloudflare modules | ⬜ |
| 6 | Store agent token in Vault | ⬜ |
| 7 | Update documentation | ⬜ |