Skip to content

Pilot quickstart

One goal: get Cloudkeel-DD reading your own infrastructure and show you your first real drift finding. Everything here is on the path to that moment; anything else is linked, not inlined.

Cloudkeel-DD running in your cluster, reading your Terraform state, independently verifying it against live Azure, and showing you what drifted, what nobody declared, and what violates policy — from read-only credentials, without ever writing to your cloud.

Our own measured run, on a cold single-node AKS cluster, took 15 minutes from helm install to the first finding. Budget an hour for your first: the workload comes up in under two minutes, but gathering a cloud credential depends on your org’s access, not on Cloudkeel-DD.

Get all of these in hand before you start — the cloud credential is the long pole.

You needVerify it
A Kubernetes cluster (1.24+) you can create namespaces inkubectl auth can-i create namespaceyes
Helm 3helm version --shortv3.x
A Terraform state source — Terraform Cloud/Enterprise or raw .tfstate in cloud storageyou can reach the TFC UI, or list the state bucket
One read-only cloud credential to verify against — the Azure happy path is inlined in step 4az account show → your subscription

You do not need: a cloud account for Cloudkeel-DD itself, an agent on your nodes, an Ingress controller (the frontend proxies /api itself), or any change to how you run Terraform. The chart and images are public on Docker Hub — no repo access, no pull secret.

Generate the three secrets, then install straight from Docker Hub. The generation commands sit directly above the install so it’s one copy-paste:

Terminal window
# Needs Python 3 with the `cryptography` package (pip install cryptography).
# No Python locally? See the Docker variant below.
FERNET=$(python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
DATAKEY=$(python3 -c "from cryptography.fernet import Fernet; print(Fernet(b'$FERNET').encrypt(Fernet.generate_key()).decode())")
JWT=$(openssl rand -base64 48)
helm install dd oci://registry-1.docker.io/driftdetective/d-detective --version 0.3.0 \
--namespace ddetective --create-namespace \
--set secrets.fernetKey="$FERNET" \
--set secrets.dataKeyWrapped="$DATAKEY" \
--set secrets.jwtSecret="$JWT" \
--set postgresql.auth.password="$(openssl rand -hex 16)"

You should see all seven workloads reach Running within ~90 seconds:

Terminal window
kubectl -n ddetective get pods -w

Want a stable hostname/TLS for a longer-lived pilot, or a managed database? That’s the production installation path; the default here needs neither.

Terminal window
kubectl -n ddetective port-forward svc/dd-frontend 3000:3000

Open http://localhost:3000 and choose Need a tenant? Register. The first account you create owns the workspace.

You should see the empty dashboard. There is no seeded demo login in a real deployment — that exists only in the local dev stack.

Cloudkeel-DD needs to know what should exist. Point it at Terraform Cloud (the default, marked Recommended).

  1. Organization slug — it’s in your workspace URL: https://app.terraform.io/app/<organization>/workspaces/...
  2. API token — Terraform Cloud → Settings → Tokens → create a user or team token. Read access is enough; Cloudkeel-DD never queues runs. Copy it — it’s shown once.

In Cloudkeel-DD: Connect → Terraform Cloud, enter app.terraform.io, the org slug, and the token. Click Test connection, then Save and start scanning.

You should see the connection test report how many workspaces it found, then a first scan start automatically.

4. Connect Azure (the credential that does the work)

Section titled “4. Connect Azure (the credential that does the work)”

At this point Cloudkeel-DD can see what Terraform claims. It cannot yet check whether reality agrees. The Azure cross-check credential is what makes it do its actual job. It’s read-only — the built-in Reader role, nothing custom.

One command creates the app registration, generates a secret, and assigns Reader. The first line fills in your subscription ID so there’s nothing to hand-edit:

Terminal window
SUB=$(az account show --query id -o tsv)
az ad sp create-for-rbac --name "d-detective" --role Reader --scopes "/subscriptions/$SUB"

It prints three values — the password is shown once:

{ "appId": "...", // → Client ID
"password": "...", // → Client secret (copy now)
"tenant": "..." } // → Tenant ID

In Cloudkeel-DD: Settings → Cross-check integrations → Azure, enter the Tenant ID, Client ID, and Client secret, then click Test connection. Once the test passes, that same button becomes Save and start scanning - click it. Scanning several subscriptions? Assign Reader at the management-group level instead — see Azure cross-check setup.

You should see a green connection test.

Open the Scopes panel on the Azure integration, click Discover, then Enable the subscription you want scanned.

You should see the subscription flip from discovered to enabled. If a scan later shows no cloud drift, re-check this first — see the don’t-skip checklist.

Drift Events → Scan now (or wait for the schedule). Results usually appear within a minute or two.

A drift event is not just “something changed” — it’s the exact field, scored:

! DRIFT critical network-security-group nsg/web-nsg
ingress rule "allow-https":
expected (Terraform): source = 10.0.0.0/16 ← internal only
actual (live Azure): source = 0.0.0.0/0 ← open to the internet

Read it left to right:

  • Severity (critical) — from your per-tenant severity rules. Drift in a dev scope can be scored lower than the same change in production.
  • Category (network-security-group) — the resource type that drifted.
  • The field diff — the one property that changed, expected vs. actual, so you know exactly what to fix, not just that the resource is dirty.

Alongside drift, the same scan surfaces unmanaged resources (running in Azure, in no Terraform state — most teams find something here on the first scan) and policy violations (OPA-evaluated, e.g. open ingress from anywhere).

Then decide, per event: Accept (reality is right — update your IaC), Suppress (known noise; requires a reason, supports an expiry), Revert plan (a suggested plan to push reality back), or Create remediation PR (a real, human-approved GitHub/GitLab PR). Accept and revert don’t close the event immediately — it becomes awaiting re-scan and only resolves when a later scan confirms the drift is gone.

You have real findings. Where to go from here covers connecting more clouds and Kubernetes, routing findings to a team, CI/CD gates, tuning noise, and what this pilot deliberately leaves out.

Hitting an error at any step? The connection troubleshooting guide maps every Test connection failure to its fix.