Examples¶
Complete, realistic objects you can adapt. All of them assume the controller is installed, the namespace has the runner identity, and Argo CD syncs the manifests (see Getting started). Replace repositories, names and ARNs with your own.
1. Kubernetes resources with the kubernetes provider¶
The getting-started sample: no cloud credentials, state in the cluster, auto approval, destroy on deletion. The runner's ServiceAccount needs RBAC for what the configuration manages (here a Namespace and a ConfigMap).
apiVersion: infra.argocd.io/v1alpha1
kind: Terraform
metadata:
name: terraform-sample
namespace: team-a
spec:
source:
git:
url: https://github.com/redhat-developer/argocd-terraform-controller
ref:
branch: main
path: ./examples/kube-namespace
interval: 10m
approvePlan: auto
runner:
binary: auto
writeOutputsToSecret:
name: terraform-sample-outputs
destroyResourcesOnDeletion: true
2. AWS stack with IRSA, manual approval, drift alerts¶
Production posture: credentials come from the ServiceAccount's IAM role, plans
wait for approval, drift makes the Application Degraded, outputs feed a health
check. The Terraform code pins profile = null (or omits it).
apiVersion: v1
kind: ServiceAccount
metadata:
name: tf-runner
namespace: platform
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/argocd-terraform-platform
---
apiVersion: infra.argocd.io/v1alpha1
kind: Terraform
metadata:
name: network
namespace: platform
spec:
source:
git:
url: git@github.com:acme/infra.git
ref:
tag: network-v1.4.2 # promote by moving the tag
secretRef:
name: infra-git-ssh # identity + known_hosts
path: ./stacks/network
interval: 30m
approvePlan: "" # manual
runner:
binary: tofu
podTemplate:
spec:
env:
- name: AWS_REGION
value: us-east-1
resources:
requests: { cpu: 250m, memory: 512Mi }
limits: { memory: 2Gi }
vars:
- name: environment
value: prod
- name: cidr
value: 10.42.0.0/16
writeOutputsToSecret:
name: network-outputs
healthChecks:
- name: nat
type: tcp
address: "{{ .nat_gateway_ip }}:443"
Approve a plan from Git by committing approvePlan: plan-network-v1.4.2-…
(the ID is in the Ready message), or from the Argo CD UI with the
ignoreDifferences shown in
Argo CD integration.
3. Two stacks in order, outputs flowing between them¶
The database stack waits for the network stack, reads its outputs, and exposes its own to a workload in the next sync wave.
apiVersion: infra.argocd.io/v1alpha1
kind: Terraform
metadata:
name: database
namespace: platform
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
source:
git:
url: git@github.com:acme/infra.git
ref: { branch: main }
secretRef: { name: infra-git-ssh }
path: ./stacks/database
interval: 1h
approvePlan: auto
dependsOn:
- name: network
varsFrom:
- kind: Secret
name: network-outputs
varsKeys:
- vpc_id
- private_subnet_ids:subnet_ids_json # list output arrives as JSON text
vars:
- name: db_password
valueFrom:
secretKeyRef: { name: database-master, key: password }
writeOutputsToSecret:
name: database-outputs
outputs:
- endpoint:DB_HOST
- port:DB_PORT
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: platform
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
selector: { matchLabels: { app: api } }
template:
metadata: { labels: { app: api } }
spec:
containers:
- name: api
image: ghcr.io/acme/api:1.8.0
envFrom:
- secretRef: { name: database-outputs }
- secretRef: { name: database-master }
In stacks/database:
variable "subnet_ids_json" { type = string }
locals { subnet_ids = jsondecode(var.subnet_ids_json) }
4. Existing S3 state, drift detection only¶
A stack that a CI pipeline applies today. The controller watches it and turns the Application Degraded on drift, without ever applying.
apiVersion: infra.argocd.io/v1alpha1
kind: Terraform
metadata:
name: legacy-dns
namespace: platform
spec:
source:
git:
url: https://github.com/acme/legacy-infra.git
ref: { branch: main }
useArgoCDRepoCredentials: true # reuse the credentials Argo CD already has
path: ./dns
interval: 1h
approvePlan: disable
backendConfig:
customConfiguration: |
backend "s3" {
bucket = "acme-tfstate"
key = "dns/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "acme-tfstate-locks"
}
runner:
podTemplate:
spec:
envFrom:
- secretRef: { name: aws-readonly } # read-only credentials are enough
5. Per-pull-request environments from an ApplicationSet¶
Each PR gets a namespace, a Terraform object with auto approval, and
everything is destroyed — state included — when the PR closes and Argo CD
prunes the Application.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: preview-envs
namespace: argocd
spec:
generators:
- pullRequest:
github: { owner: acme, repo: shop, labels: [preview] }
template:
metadata:
name: "preview-{{number}}"
spec:
project: previews
source:
repoURL: https://github.com/acme/shop.git
targetRevision: "{{head_sha}}"
path: deploy/preview
helm:
parameters:
- name: pr
value: "{{number}}"
destination:
server: https://kubernetes.default.svc
namespace: "preview-{{number}}"
syncPolicy:
automated: { prune: true, selfHeal: true }
syncOptions: [CreateNamespace=true]
deploy/preview renders the runner identity (ServiceAccount + RoleBinding to
argocd-terraform-controller-runner-role) and:
apiVersion: infra.argocd.io/v1alpha1
kind: Terraform
metadata:
name: preview-{{ .Values.pr }}
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
source:
git:
url: https://github.com/acme/shop.git
ref: { branch: main }
useArgoCDRepoCredentials: true
path: ./infra/preview
interval: 1h
approvePlan: auto
vars:
- name: pr_number
value: {{ .Values.pr }}
writeOutputsToSecret:
name: preview-outputs
destroyResourcesOnDeletion: true
tfstate:
deleteOnDestroy: true
runner:
podTemplate:
spec:
envFrom:
- secretRef: { name: preview-cloud-creds }
6. Terraform Cloud workspace¶
State and runs stay in TFC; the controller orchestrates and reports.
apiVersion: infra.argocd.io/v1alpha1
kind: Terraform
metadata:
name: billing
namespace: finance
spec:
source:
oci:
url: oci://ghcr.io/acme/tf-billing # configuration shipped as an OCI artifact
ref: { semver: "^2.0" }
secretRef: { name: ghcr-pull }
interval: 2h
approvePlan: ""
cloud:
organization: acme
workspaces: { name: billing-prod }
cliConfigSecretRef:
name: tfc-credentials # key *.tfrc with the credentials block
runner:
binary: terraform # TFC requires Terraform
7. Kubernetes provider against another cluster¶
A kubeconfig for the target cluster is mounted from a Secret with
fileMappings; the configuration's provider reads it from the runner home.
apiVersion: infra.argocd.io/v1alpha1
kind: Terraform
metadata:
name: edge-cluster-addons
namespace: platform
spec:
source:
git:
url: https://github.com/acme/infra.git
ref: { branch: main }
useArgoCDRepoCredentials: true
path: ./clusters/edge/addons
interval: 30m
approvePlan: auto
fileMappings:
- secretRef: { name: edge-kubeconfig, key: kubeconfig }
location: home
path: .kube/config # provider "kubernetes" { config_path = "~/.kube/config" }
More on each knob: Variables and credentials, Sources, Plan approval, Outputs, Dependencies and health checks, Destroy and deletion, Runner Pods, State.