Skip to main content

Atlantis Integration

DriftWise integrates with Atlantis via a run step in your workflow. No plugins, no sidecars.

Setup

Add the DriftWise step after plan in your atlantis.yaml. The analyze endpoint enqueues the job and returns 202 immediately — poll GET /llm-jobs/{job_id} until status is done (or error):

atlantis.yaml
workflows:
default:
plan:
steps:
- init
- plan
- run: |
terraform show -json $PLANFILE > /tmp/dw-plan.json
JOB_ID=$(curl -sfX POST https://api.driftwise.ai/api/v2/orgs/$ORG_ID/analyze \
-H "x-api-key: $DRIFTWISE_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"plan_json\": $(cat /tmp/dw-plan.json | jq -Rs .)}" | jq -r '.job_id')

for i in $(seq 1 36); do
BODY=$(curl -sf https://api.driftwise.ai/api/v2/orgs/$ORG_ID/llm-jobs/$JOB_ID \
-H "x-api-key: $DRIFTWISE_API_KEY")
STATUS=$(echo "$BODY" | jq -r '.status')
if [ "$STATUS" = "done" ]; then RESULT=$(echo "$BODY" | jq '.result'); break; fi
if [ "$STATUS" = "error" ]; then echo "analysis failed: $(echo "$BODY" | jq -r '.error')" >&2; exit 1; fi
sleep 5
done
[ "$STATUS" = "done" ] || { echo "timed out waiting for analysis" >&2; exit 1; }
echo "$RESULT" | jq -r '.narrative'
Why the conversion step?

Atlantis's $PLANFILE is a binary Terraform plan. The terraform show -json step converts it to JSON, then jq -Rs wraps it as an escaped JSON string inside the plan_json field that the API expects.

Environment Variables

Set these in your Atlantis server environment:

VariableDescription
DRIFTWISE_API_KEYYour DriftWise API key (dw2_...)
ORG_IDYour DriftWise organization ID (UUID)

How It Works

  1. Atlantis runs terraform plan as usual
  2. The run step converts the binary plan to JSON and sends it to DriftWise
  3. DriftWise enqueues the analysis and returns a job handle; the run step polls llm-jobs/{job_id} until the risk score and narrative are ready
  4. Atlantis includes the output in the plan result shown on the PR

Optional: CI Metadata

You can pass CI context so DriftWise can link the analysis back to the PR:

- run: |
terraform show -json $PLANFILE > /tmp/dw-plan.json
JOB_ID=$(curl -sfX POST https://api.driftwise.ai/api/v2/orgs/$ORG_ID/analyze \
-H "x-api-key: $DRIFTWISE_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"plan_json\": $(cat /tmp/dw-plan.json | jq -Rs .),
\"ci\": {
\"repo_owner\": \"$BASE_REPO_OWNER\",
\"repo_name\": \"$BASE_REPO_NAME\",
\"pr_number\": $PULL_NUM,
\"commit_sha\": \"$HEAD_COMMIT\",
\"branch\": \"$HEAD_BRANCH_NAME\"
}
}" | jq -r '.job_id')

for i in $(seq 1 36); do
BODY=$(curl -sf https://api.driftwise.ai/api/v2/orgs/$ORG_ID/llm-jobs/$JOB_ID \
-H "x-api-key: $DRIFTWISE_API_KEY")
STATUS=$(echo "$BODY" | jq -r '.status')
if [ "$STATUS" = "done" ]; then RESULT=$(echo "$BODY" | jq '.result'); break; fi
if [ "$STATUS" = "error" ]; then echo "analysis failed: $(echo "$BODY" | jq -r '.error')" >&2; exit 1; fi
sleep 5
done
[ "$STATUS" = "done" ] || { echo "timed out waiting for analysis" >&2; exit 1; }
echo "$RESULT" | jq -r '.narrative'