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.