Argo CD integration¶
Everything here is configuration of a stock Argo CD; no Argo CD code or image
changes are involved. The pieces live in config/argocd
and are packaged as a Kustomize Component.
Install¶
As a component of your Argo CD kustomization:
# kustomization.yaml
namespace: argocd
resources:
- https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
components:
- https://github.com/redhat-developer/argocd-terraform-controller/config/argocd?ref=main
Or patch an existing installation:
kubectl patch configmap argocd-cm -n argocd --type merge --patch-file config/argocd/argocd-cm-patch.yaml
# The argocd-server container is named "argocd-server" (upstream install.yaml)
# or "server" (Helm chart); detect it so the strategic merge matches.
SERVER_CONTAINER=$(kubectl get deploy argocd-server -n argocd -o jsonpath='{.spec.template.spec.containers[0].name}')
sed "s/- name: argocd-server/- name: ${SERVER_CONTAINER}/" config/argocd/argocd-server-extension-patch.yaml \
| kubectl patch deployment argocd-server -n argocd --patch-file /dev/stdin
kubectl apply -n argocd -f config/argocd/argocd_repo_reader_binding.yaml
If the container name does not match, strategic merge appends a container with
no image and the API rejects the patch with
spec.template.spec.containers[0].image: Required value.
With the Argo CD Helm chart, the native way is to copy the two
resource.customizations.* keys of argocd-cm-patch.yaml into configs.cm
and the init container / volume of the server patch into server.initContainers,
server.volumeMounts, server.volumes (the chart already names the container
server, so no rename is needed there).
Health assessment¶
config/argocd/lua/health.lua maps a
Terraform object to an Argo CD health:
| Health | When |
|---|---|
| Healthy | Ready=True — applied, no drift, outputs written (or plan-only with a plan) |
| Progressing | no status yet, initialising, planning, applying, dependency not ready |
| Suspended | spec.suspend, or a plan is waiting for manual approval |
| Degraded | the last run failed, drift detected, state locked, stalled |
Suspended holds up later sync waves, so an Application can create a database with Terraform in wave 0 and deploy the workload that needs it in wave 1; the deployment waits for the approval and the apply.
Resource actions¶
config/argocd/lua/actions defines actions
available from the resource menu, the Argo CD CLI (argocd app actions run)
and the UI extension buttons:
| Action | Effect |
|---|---|
approve-plan |
spec.approvePlan = status.plan.pending |
replan |
sets the infra.argocd.io/reconcile annotation; the controller reconciles immediately and discards an unapproved pending plan |
suspend / resume |
toggles spec.suspend |
force-unlock |
spec.tfstate.forceUnlock = auto (only enabled while status.lock.pending is set) |
GitOps considerations¶
Actions mutate the live object. If the Application has automated sync with
selfHeal, Argo CD reverts the change on the next sync — which is often the
intended "approvals must come from git" policy. To allow approving from the UI
while keeping the object in git, ignore those fields:
spec:
ignoreDifferences:
- group: infra.argocd.io
kind: Terraform
jsonPointers:
- /spec/approvePlan
- /spec/suspend
- /spec/tfstate/forceUnlock
- /metadata/annotations/infra.argocd.io~1reconcile
syncPolicy:
syncOptions:
- RespectIgnoreDifferences=true
Approving from git is simply committing spec.approvePlan: <plan id>; the ID
is in the Ready condition message, the TerraformPlanSucceed event and the UI.
UI extension¶
The extension (ui/extension) registers three things:
- a Terraform entry in the sidebar (system-level extension at
/extensions/terraform): a dashboard of every Terraform object across all Applications you can see — counts by state, search and filters, and a detail page per object with a graph from the source, through the Terraform object and its Kubernetes objects (state, plan, outputs, runner), to every resource the state manages — including non-Kubernetes resources such as VPCs, buckets or databases, grouped by provider and type with their IDs (fromstatus.inventory, recorded after each apply whenspec.enableInventoryis true, the default). Plan output, outputs, conditions, recent events and the action buttons are on the same page. - a Terraform tab on
infra.argocd.io/Terraformresources inside an Application; - a Terraform tab in the Application details panel (next to Summary, Parameters, Manifest and Events) and a Terraform application view in the toolbar, both listing the Terraform objects of one Application.
Everything is read through the Argo CD API (/api/v1/applications,
…/{app}/resource, …/{app}/resource-tree, …/{app}/events) and actions run
through POST …/{app}/resource/actions; all of it is authorised by Argo CD RBAC
(applications, get and applications, action/infra.argocd.io/Terraform/<action>).
No backend service and no proxy extension are needed. Consequently the
dashboard shows Terraform objects that are managed by an Argo CD Application;
objects applied outside of Argo CD are not listed.
The human readable plan is stored in a ConfigMap (spec.storeReadablePlan: human,
the default) because Argo CD redacts Secret data in its API. The ConfigMap is
owned by the Terraform object and therefore appears in the Application tree.
The extension is installed by the
argocd-extension-installer
init container in argocd-server-extension-patch.yaml, downloading
argocd-terraform-extension.tar.gz from a GitHub release. The application view
appears when the Application's resources include a Terraform object or when the
Application has the label infra.argocd.io/has-terraform: "true"; the details
panel tab is always present (Argo CD has no per-Application filter for resource
tabs) and says so when the Application manages no Terraform objects.
Reusing Argo CD repository credentials¶
Set spec.source.git.useArgoCDRepoCredentials: true to let the controller look
up the credentials Argo CD already has for the URL (Secrets labelled
argocd.argoproj.io/secret-type: repository or repo-creds, plus
argocd-ssh-known-hosts-cm for SSH). This requires:
- the controller flag
--argocd-namespace(set fromARGOCD_NAMESPACE), and - read access to Secrets/ConfigMaps in that namespace
(
config/argocd/argocd_repo_reader_binding.yaml, orargocd.repoCredentials.enabledin the Helm chart).
RBAC for users¶
Argo CD users approve plans through the resource action, so grant it in the Argo CD RBAC policy:
p, role:platform, applications, action/infra.argocd.io/Terraform/approve-plan, */*, allow
p, role:platform, applications, action/infra.argocd.io/Terraform/replan, */*, allow