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.
What you’ll have at the end
Section titled “What you’ll have at the end”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.
Prerequisites
Section titled “Prerequisites”Get all of these in hand before you start — the cloud credential is the long pole.
| You need | Verify it |
|---|---|
| A Kubernetes cluster (1.24+) you can create namespaces in | kubectl auth can-i create namespace → yes |
| Helm 3 | helm version --short → v3.x |
A Terraform state source — Terraform Cloud/Enterprise or raw .tfstate in cloud storage | you 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 4 | az 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.
1. Install
Section titled “1. Install”Generate the three secrets, then install straight from Docker Hub. The generation commands sit directly above the install so it’s one copy-paste:
# 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:
kubectl -n ddetective get pods -wWant a stable hostname/TLS for a longer-lived pilot, or a managed database? That’s the production installation path; the default here needs neither.
2. Open the app and register
Section titled “2. Open the app and register”kubectl -n ddetective port-forward svc/dd-frontend 3000:3000Open 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.
3. Connect Terraform (your desired state)
Section titled “3. Connect Terraform (your desired state)”Cloudkeel-DD needs to know what should exist. Point it at Terraform Cloud (the default, marked Recommended).
- Organization slug — it’s in your workspace URL:
https://app.terraform.io/app/<organization>/workspaces/... - 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:
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 IDIn 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.
5. Enable the subscription scope
Section titled “5. Enable the subscription scope”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.
6. Run your first scan
Section titled “6. Run your first scan”Drift Events → Scan now (or wait for the schedule). Results usually appear within a minute or two.
7. Read your first finding
Section titled “7. Read your first finding”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 internetRead 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.
Next steps
Section titled “Next steps”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.