Skip to main content

Kubernetes Operator

The DriftWise Kubernetes Operator reports live cluster resources to DriftWise for drift detection against Terraform-managed infrastructure. Resources discovered by the operator appear alongside cloud resources in your drift results.

How It Works​

  1. The operator runs in your cluster and watches for resources
  2. Periodically POSTs discovered resources to POST /api/v2/operator/report
  3. DriftWise stores them as live resources linked to the cluster
  4. Drift detection compares them against your Terraform state

Authentication​

The operator endpoint requires an org-scoped API key. OIDC authentication is rejected with 403 — this is a machine-to-machine path where human attribution would just be misleading.

Create an API key for the operator through the dashboard Settings page, then store it as a Kubernetes secret:

kubectl create secret generic driftwise-operator \
--from-literal=api-key=dw2_...

Reporting Resources​

The operator POSTs discovered resources to POST /api/v2/operator/report on its polling interval. DriftWise creates a synthetic cloud_accounts row per cluster (provider=kubernetes) so live_resources.cloud_account_id resolves; cloud scan workers skip those rows.

See the accounts tag of the API reference for the request body shape (OperatorReportRequest) and response (OperatorReportResponse). Key request fields summarized below.

Request fields​

FieldTypeRequiredDescription
cluster_idstringyesUnique identifier for the cluster
namespacestringyesKubernetes namespace being reported
resourcesResource[]yesArray of discovered resources

(The request body also accepts generate_iac, iac_format, and custom_prompt fields for wire-compatibility with older operator builds, but the server currently ignores them — the report response carries no generated IaC. Use the drift UI's IaC generation instead.)

Response​

{
"report_id": "scan-run-uuid",
"resource_count": 15
}

The report_id is a scan run ID — use it to query drift results via GET /api/v2/orgs/:id/scans/:scan_id/drift.

Resource Structure​

Each resource in the resources array:

FieldTypeDescription
IDstringUnique resource identifier within the cluster
NormalizedTypestringMust use the k8s/<kind> format (e.g., k8s/deployment, k8s/pod, k8s/service, k8s/statefulset). Unlike cloud resources, Kubernetes types are kept fine-grained — they are not flattened to broad categories by Cloud Discovery.
ProviderTypestringThe Kubernetes kind in lowercase (e.g., deployment, pod, service)
ProviderstringAlways "kubernetes"
NamestringResource name
RegionstringCluster region
TagsobjectLabels as key-value pairs
PropertiesobjectResource-specific attributes
Field casing

Resource fields use Go-style CamelCase keys on the wire (ID, NormalizedType, ProviderType, ...). JSON matching is case-insensitive, so id/name/provider also work — but snake_case compound names (normalized_type, provider_type) do not map and are silently dropped. Send NormalizedType and ProviderType exactly (or any casing without underscores).

Rate Limiting​

The operator endpoint allows 120 requests per minute per IP address, designed for the typical ~30-second polling interval.

Drift Detection with K8s Resources​

Once resources are reported, they participate in the normal drift detection flow:

  1. Operator reports resources → stored as live resources
  2. Terraform state sources provide IaC resources
  3. Drift computation matches live vs. IaC by resource ID
  4. Missing/extra/changed items appear in drift results

Resources are grouped by cluster using the cluster_id field — each cluster gets a synthetic provider = "kubernetes" cloud-account row whose external account ID is the cluster_id verbatim.