New to Kestra?
Use blueprints to kickstart your first workflows.
Check an Argo CD application's sync and health status on a schedule, change nothing on the cluster, and alert Slack on drift or degraded health.
A GitOps application can drift out of sync with its source of truth, or quietly degrade on the cluster, long after the pipeline that deployed it has finished and stopped watching. This blueprint turns a scheduled, read-only argocd app get call into a Day 2 health gate: io.kestra.plugin.argocd.apps.Status reports the application's current sync and health state without changing anything on the cluster, a single Pebble expression classifies the combination, and the result routes to a quiet log, an informational Slack note, or a Slack page depending on severity.
check_app_status (io.kestra.plugin.argocd.apps.Status) reads argocd_application's sync status (Synced/OutOfSync) and health status (Healthy/Progressing/Degraded/Suspended/Missing/Unknown) from the Argo CD server, along with its conditions and managed resources. Setting refresh to "true" forces a live re-check instead of Argo CD's cached state, at the cost of an extra API call. allowFailure: true keeps a server or application lookup failure from aborting the flow outright.classify_app_drift (io.kestra.plugin.core.debug.Return) evaluates a single Pebble expression against both fields together: missing either entirely becomes UNREACHABLE; a health status of Degraded, Missing, or Unknown becomes CRITICAL regardless of sync state, since a broken application matters more than whether Git and the cluster currently agree; an out-of-sync application or a transitional health status (Progressing, Suspended) becomes WARNING; otherwise HEALTHY.route_by_drift (io.kestra.plugin.core.flow.Switch) branches on that classification: HEALTHY logs a quiet confirmation; WARNING posts an informational Slack note; CRITICAL and UNREACHABLE each page Slack with the reported sync and health status; a defaults fallback logs the raw value for any classification outside the four known cases.log_audit_result (io.kestra.plugin.core.log.Log) writes one audit line per run, independent of which Switch branch fired, so the check's own history stays queryable even if Slack delivery fails.errors block pages Slack separately if the flow fails outright, so a broken sentinel is never mistaken for a healthy application.app_drift_schedule trigger checks every 6 hours and ships disabled: true, with explicit inputs: overrides for argocd_application and force_refresh so the scheduled run's target and cache behavior are pinned regardless of this flow's own defaults.argocd CLI scripting.status output (HEALTHY / WARNING / CRITICAL / UNREACHABLE) other flows can gate on.Argo CD's own UI shows drift and health in real time, but nothing pages anyone when it happens unless someone is watching the dashboard. Kestra's scheduled, read-only Status check gives that page automatically: the schedule, the classification, and the Slack call are all one auditable execution history, with the Switch branch ready to grow into real remediation (triggering the existing Sync task on drift, opening a ticket) without rewriting the check itself.
ARGOCD_SERVER secret, with an existing Application named argocd_application.ARGOCD_SERVER: Argo CD server address, e.g. argocd.example.com or argocd-server.argocd.svc.cluster.local:443.ARGOCD_TOKEN: Argo CD API token with at least read access to the application.SLACK_WEBHOOK: Slack incoming webhook used for every alert branch and the flow-failure alert.In Kestra OSS (no Enterprise secrets backend), secrets are supplied as environment variables prefixed SECRET_, base64-encoded, and read back in flows with {{ secret('NAME') }}, for example SECRET_SLACK_WEBHOOK=$(echo -n 'https://hooks.slack.com/services/<placeholder>' | base64). This keeps credentials out of the flow YAML but, per Kestra's own documentation, offers no encryption at rest or access control beyond the host environment; use the Enterprise secrets backend for stronger guarantees.
docker run -d --name k3s-server --privileged -p 6443:6443 rancher/k3s:latest server --tls-san=localhostdocker network ls for Kestra's network and attach the cluster to it:
docker network connect YOUR_KESTRA_NETWORK k3s-serverdocker exec k3s-server kubectl create namespace argocd
docker exec k3s-server kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
docker exec k3s-server kubectl port-forward svc/argocd-server -n argocd 8443:443docker exec k3s-server kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}"
argocd login localhost:8443 --username admin
argocd account generate-tokenARGOCD_SERVER (localhost:8443), ARGOCD_TOKEN, and SLACK_WEBHOOK as secrets, create a sample Application, and run the flow once manually to confirm status reads HEALTHY.app_drift_schedule once pointed at a real server and application.WARNING branch into the existing io.kestra.plugin.argocd.apps.Sync task for automatic drift correction instead of a Slack note alone.argocd_application input over every application a team owns using io.kestra.plugin.core.flow.Loop, instead of checking one application per execution.outputs.check_app_status.conditions or outputs.check_app_status.resources to name the specific resource causing a Degraded health status in the Slack alert.status into a KV store and alert only on state transitions instead of every run.argocd_application: the Argo CD Application to check (type STRING, default my-argocd-app).force_refresh: type SELECT, values ["true", "false"], default "false"; forces a live cluster re-check instead of Argo CD's cached state.status: one of HEALTHY, WARNING, CRITICAL, UNREACHABLE.{{ outputs.classify_app_drift.value }}, also exposed as the flow output {{ outputs.status }}.refresh: "true", Argo CD can serve a cached sync/health status for a short window after a cluster-side change; set it to "true" if the check needs to see drift the moment it happens, at the cost of one extra API call per run.Progressing health is classified WARNING, not CRITICAL, because it is normal during any rollout; a WARNING that persists across several scheduled checks in a row is the real signal of a stuck rollout, not a single occurrence.argocd app get; it never calls argocd app sync, argocd app rollback, or argocd app delete, so there is nothing destructive to default to dry-run for.ARGOCD_TOKEN only needs read access to the target application; scope it narrowly rather than reusing an administrative token for a monitoring check.