Provisions and manages Neo4j Aura instances via CLI (aura-cli v1.7+) or REST API.
Install to Claude Code
npx -y skills add stanfish06/skillquarium --skill neo4j-aura-provisioning-skill --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Neo4j Aura Provisioning Skill?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/stanfish06-neo4j-aura-provisioning-skill)More formats (shields.io, HTML) on the badges page.
---
name: neo4j-aura-provisioning-skill
description: Provisions and manages Neo4j Aura instances via CLI (aura-cli v1.7+) or REST API.
Use when creating, pausing, resuming, resizing, or deleting AuraDB Free/Professional/Business Critical/VDC
instances; downloading credentials; scripting CI/CD pipelines; polling async status; or using the
Terraform neo4j/neo4j-aura provider. Covers auth setup (client credentials OAuth2), credential
lifecycle (download once — never recoverable), instance type selection, region codes, and Python
provisioning scripts. Does NOT handle Cypher queries — use neo4j-cypher-skill.
Does NOT cover Graph Data Science algorithms — use neo4j-gds-skill or neo4j-aura-graph-analytics-skill.
Does NOT cover neo4j-admin/cypher-shell — use neo4j-cli-tools-skill.
version: 1.0.4
allowed-tools: Bash WebFetch
---
## When to Use
- Creating an Aura instance (CLI, REST API, Python, Terraform)
- Pausing, resuming, resizing, or deleting an instance
- Downloading initial credentials from creation response
- Polling instance status: `creating` → `running`
- Setting up CI/CD provisioning or teardown pipelines
- Choosing instance tier (Free vs Professional vs Business Critical vs VDC)
## When NOT to Use
- **Cypher queries against running DB** → `neo4j-cypher-skill`
- **GDS algorithms on Aura** → `neo4j-gds-skill` (Pro with plugin) or `neo4j-aura-graph-analytics-skill` (serverless)
- **neo4j-admin / cypher-shell** → `neo4j-cli-tools-skill`
- **Application driver setup** → use a language driver skill (python, javascript, java, go, dotnet)
---
## Instance Tier Decision Table
| Tier | API type code | Memory | GDS | Replicas | Use when |
|---|---|---|---|---|---|
| AuraDB Free | `free-db` | 1 GB | ❌ | ❌ | Dev/demo; ≤200k nodes/400k rels |
| AuraDB Professional | `professional-db` | 2–64 GB | plugin available | ❌ | Production workloads |
| AuraDB Business Critical | `business-critical` | 4–384 GB | plugin available | ✅ | HA, multi-AZ, SLA |
| AuraDB VDC | `enterprise-db` | custom | ✅ | ✅ | Dedicated infra, compliance |
| AuraDS Professional | `professional-ds` | 2–64 GB | ✅ built-in | ❌ | Data science / GDS |
| AuraDS Enterprise | `enterprise-ds` | custom | ✅ | ✅ | Enterprise GDS |
**AuraDB Free limits**: 200k nodes, 400k rels; auto-pauses after 72 h inactivity; deleted if paused >30 days; no resize.
---
## Auth Setup
### CLI (aura-cli v1.7+)
Install (binary, not pip):
```bash
# macOS
curl -L https://github.com/neo4j/aura-cli/releases/latest/download/aura-cli-darwin-amd64.tar.gz | tar xz
sudo mv aura-cli /usr/local/bin/
aura-cli -v # verify
```
Add credentials (from console.neo4j.io → Account Settings → API Credentials):
```bash
aura-cli credential add \
--name "my-creds" \
--client-id "$AURA_CLIENT_ID" \
--client-secret "$AURA_CLIENT_SECRET"
aura-cli credential use --name "my-creds"
```
Verify:
```bash
aura-cli instance list --output table
```
### REST API — Get Bearer Token
Token endpoint: `POST https://api.neo4j.io/oauth/token`
Token expires: **3600 s (1 h)**. On 403 → refresh token.
```bash
TOKEN=$(curl -s --request POST 'https://api.neo4j.io/oauth/token' \
--user "${AURA_CLIENT_ID}:${AURA_CLIENT_SECRET}" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
| jq -r '.access_token')
echo "Token: ${TOKEN:0:20}..."
```
Use in all subsequent calls: `--header "Authorization: Bearer $TOKEN"`
---
## Step 1 — List Tenants (Projects)
CLI:
```bash
aura-cli tenants list --output table
# Copy TENANT_ID for create operations
```
REST:
```bash
curl -s https://api.neo4j.io/v1/tenants \
-H "Authorization: Bearer $TOKEN" | jq '.data[] | {id, name}'
```
---
## Step 2 — Create Instance
CRITICAL: **Capture output immediately.** Initial password shown ONCE — never retrievable again.
If lost: delete and recreate. Store `aura-creds.json` before doing anything else.
### CLI
```bash
aura-cli instance create \
--name "my-instance" \
--cloud-provider gcp \
--region europe-west1 \
--type professional-db \
--tenant-id "$TENANT_ID" \
--output json | tee aura-creds.json
# Extract for .env
INSTANCE_ID=$(jq -r '.id' aura-creds.json)
PASSWORD=$(jq -r '.password' aura-creds.json)
```
### REST API (full create)
```bash
RESPONSE=$(curl -s -X POST https://api.neo4j.io/v1/instances \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "my-instance",
"cloud_provider": "gcp",
"region": "europe-west1",
"type": "professional-db",
"tenant_id": "'"$TENANT_ID"'",
"memory": "4GB",
"version": "5"
}')
echo "$RESPONSE" | tee aura-creds.json
INSTANCE_ID=$(echo "$RESPONSE" | jq -r '.data.id')
PASSWORD=$(echo "$RESPONSE" | jq -r '.data.password')
```
Instance create request body fields:
| Field | Required | Values |
|---|---|---|
| `name` | ✅ | any string |
| `cloud_provider` | ✅ | `gcp` `aws` `azure` |
| `region` | ✅ | see region table |
| `type` | ✅ | see tier table |
| `tenant_id` | ✅ | from tenant list |
| `memory` | ✗ | `1GB` `2GB` `4GB` `8GB` … `384GB` |
| `version` | ✗ | `5` (default) |
---
## Step 3 — Poll Until RUNNING (CRITICAL — All Ops Are Async)
ALL lifecycle operations (create, pause, resume, resize) are async. Do NOT attempt connection or next operation until status = `running` (or `paused` for pause op).
```bash
poll_status() {
local INSTANCE_ID=$1 TARGET=$2 MAX_WAIT=${3:-600}
local ELAPSED=0 STATUS
echo "Polling for status=$TARGET (max ${MAX_WAIT}s)..."
while [ $ELAPSED -lt $MAX_WAIT ]; do
STATUS=$(aura-cli instance get --instance-id "$INSTANCE_ID" --output json \
| jq -r '.status' 2>/dev/null)
echo " [${ELAPSED}s] status=$STATUS"
[ "$STATUS" = "$TARGET" ] && echo "Ready." && return 0
[ "$STATUS" = "destroying" ] && echo "ERROR: instance is being destroyed" && return 1
sleep 10; ELAPSED=$((ELAPSED + 10))
done
echo "TIMEOUT after ${MAX_WAIT}s — last status: $STATUS" && return 1
}
poll_status "$INSTANCE_ID" "running" 600
```
REST equivalent:
```bash
while true; do
STATUS=$(curl -s "https://api.neo4j.io/v1/instances/$INSTANCE_ID" \
-H "Authorization: Bearer $TOKEN" | jq -r '.data.status')
[ "$STATUS" = "running" ] && break
sleep 10
done
```
Status lifecycle:
```
creating → running → pausing → paused → resuming → running
↘ destroying → (gone)
```
---
## Step 4 — Write .env and Verify
```bash
CONNECTION_URI="neo4j+s://${INSTANCE_ID}.databases.neo4j.io"
cat > .env <<EOF
NEO4J_URI=${CONNECTION_URI}
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=${PASSWORD}
NEO4J_DATABASE=neo4j
AURA_INSTANCE_ID=${INSTANCE_ID}
EOF
# Ensure .env never committed
grep -q '^\.env$' .gitignore 2>/dev/null || echo '.env' >> .gitignore
# Verify connectivity
cypher-shell -a "$CONNECTION_URI" -u neo4j -p "$PASSWORD" "RETURN 'connected' AS status"
```
---
## Step 5 — Lifecycle Operations
All operations require instance in the correct state. Wrong-state ops return 4xx error.
### Pause
Required state: `running`
```bash
aura-cli instance pause --instance-id "$INSTANCE_ID"
poll_status "$INSTANCE_ID" "paused" 600
```
### Resume
Required state: `paused`
```bash
aura-cli instance resume --instance-id "$INSTANCE_ID"
poll_status "$INSTANCE_ID" "running" 900 # resume can take longer
```
### Resize (Professional+ only — NOT Free)
Required state: `running`; instance remains available during resize.
```bash
# REST only — CLI resize not available in v1.7
curl -s -X PATCH "https://api.neo4j.io/v1/instances/$INSTANCE_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"memory": "8GB"}'
poll_status "$INSTANCE_ID" "running" 600
# Cannot reduce below current usage level
```
### Delete
IRREVERSIBLE. Export snapshots first if data needed.
```bash
aura-cli instance delete --instance-id "$INSTANCE_ID"
# No poll needed — immediate
```
REST:
```bash
curl -s -X DELETE "https://api.neo4j.io/v1/instances/$INSTANCE_ID" \
-H "Authorization: Bearer $TOKEN"
```
---
## Python CI/CD Provisioning Script
```python
import os, time, requests
CLIENT_ID = os.environ["AURA_CLIENT_ID"]
CLIENT_SECRET = os.environ["AURA_CLIENT_SECRET"]
BASE = "https://api.neo4j.io/v1"
def get_token() -> str:
r = requests.post(
"https://api.neo4j.io/oauth/token",
auth=(CLIENT_ID, CLIENT_SECRET),
data={"grant_type": "client_credentials"},
)
r.raise_for_status()
return r.json()["access_token"]
def auth_headers(token: str) -> dict:
return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
def create_instance(token: str, tenant_id: str, **kwargs) -> dict:
payload = {"tenant_id": tenant_id, "version": "5", **kwargs}
r = requests.post(f"{BASE}/instances", headers=auth_headers(token), json=payload)
r.raise_for_status()
return r.json()["data"] # contains id, password, connection_url
def poll_status(token: str, instance_id: str, target: str, timeout: int = 600) -> None:
deadline = time.time() + timeout
while time.time() < deadline:
r = requests.get(f"{BASE}/instances/{instance_id}", headers=auth_headers(token))
r.raise_for_status()
status = r.json()["data"]["status"]
print(f" status={status}")
if status == target:
return
if status == "destroying":
raise RuntimeError("Instance destroyed unexpectedly")
time.sleep(10)
raise TimeoutError(f"Instance {instance_id} did not reach '{target}' in {timeout}s")
# --- usage ---
token = get_token()
instance = create_instance(
token,
tenant_id = os.environ["AURA_TENANT_ID"],
name = "ci-test-instance",
cloud_provider = "aws",
region = "us-east-1",
type = "professional-db",
memory = "2GB",
)
# SAVE CREDENTIALS IMMEDIATELY — password never retrievable again
print(f"ID: {instance['id']}")
print(f"URI: neo4j+s://{instance['id']}.databases.neo4j.io")
print(f"Password: {instance['password']}") # log to secure vault NOW
poll_status(token, instance["id"], "running", timeout=600)
print("Instance ready.")
```
---
## Region Codes
### AWS
| Region code | Location |
|---|---|
| `us-east-1` | N. Virginia |
| `us-east-2` | Ohio |
| `us-west-2` | Oregon |
| `eu-west-1` | Ireland |
| `eu-west-3` | Paris |
| `eu-central-1` | Frankfurt |
| `ap-southeast-1` | Singapore |
| `ap-southeast-2` | Sydney |
| `ap-south-1` | Mumbai |
| `sa-east-1` | São Paulo |
### GCP
| Region code | Location |
|---|---|
| `europe-west1` | Belgium |
| `europe-west3` | Frankfurt |
| `europe-west4` | Netherlands |
| `us-central1` | Iowa |
| `us-east1` | S. Carolina |
| `us-east4` | N. Virginia |
| `asia-east1` | Taiwan |
| `asia-northeast1` | Tokyo |
| `asia-southeast1` | Singapore |
| `australia-southeast1` | Sydney |
### Azure
| Region code | Location |
|---|---|
| `eastus` | E. US |
| `eastus2` | E. US 2 |
| `westeurope` | Netherlands |
| `northeurope` | Ireland |
| `uksouth` | London |
| `southeastasia` | Singapore |
| `brazilsouth` | Brazil |
| `koreacentral` | Korea |
Enterprise tiers (Business Critical, VDC) add 20+ additional regions per provider. Check console for full list.
Free tier: GCP only; limited subset of regions.
---
## Terraform Provider
```hcl
terraform {
required_providers {
aura = {
source = "neo4j/neo4j-aura"
}
}
}
provider "aura" {
client_id = var.aura_client_id # or AURA_CLIENT_ID env var
client_secret = var.aura_client_secret # or AURA_CLIENT_SECRET env var
}
resource "aura_instance" "db" {
name = "prod-db"
type = "professional-db"
cloud_provider = "gcp"
region = "europe-west1"
memory = "4GB"
tenant_id = var.aura_tenant_id
}
output "neo4j_uri" {
value = "neo4j+s://${aura_instance.db.id}.databases.neo4j.io"
sensitive = false
}
output "neo4j_password" {
value = aura_instance.db.password
sensitive = true
}
```
After `terraform apply`: poll status before marking infra ready — Terraform resource creation returns when API call completes, not when DB is `running`.
---
## Common Errors
| Error | Cause | Fix |
|---|---|---|
| `403 Forbidden` after working | Token expired (1 h TTL) | Re-run `get_token()` |
| `409 Conflict` on create | Name already exists in tenant | Change name or delete existing |
| `422` on pause | Instance not `running` | Check status; wait for ongoing op to finish |
| `422` on resume | Instance not `paused` | Check status |
| `422` on resize | Below current usage | Reduce data first; can't shrink below usage |
| Region not found | Tier doesn't support that region | Use `Free` tier on GCP only; Pro/BC on all 3 clouds |
| Credentials lost after create | Password only returned at create time | Delete + recreate — no reset exists |
| `429 Too Many Requests` | Rate limit hit (25 req/min Free, 125 req/min Pro+) | Add `time.sleep(2)` between polling calls |
| `instance list` returns empty | Wrong credential active | `aura-cli credential use --name <name>` |
---
## API Rate Limits
| Tier | Requests/minute |
|---|---|
| Free / Pro Trial (no billing) | 25 |
| Pro with billing, BC, VDC | 125 |
Poll interval: ≥10 s to stay within limits on Free; 5 s safe on Pro+.
On `Retry-After` header in 5xx response: wait that many seconds before retry.
---
## Security Rules
- Write initial credentials to `.env`; verify `.env` in `.gitignore` before proceeding
- Never print `PASSWORD` in CI logs — write to secrets vault (AWS Secrets Manager, GitHub secret, Vault)
- Use `from_env()` / `os.environ` — never hardcode credentials
- If `.env` absent: `python-dotenv` `load_dotenv()` auto-loads; do NOT prompt user unless loading fails
---
## WebFetch — Current Docs
| Need | URL |
|---|---|
| REST API spec (OpenAPI) | `https://neo4j.com/docs/aura/platform/api/specification/` |
| CLI reference | `https://neo4j.com/docs/aura/aura-cli/` |
| Region list | `https://neo4j.com/docs/aura/managing-instances/regions/` |
| Auth details | `https://neo4j.com/docs/aura/api/authentication/` |
| Instance actions | `https://neo4j.com/docs/aura/managing-instances/instance-actions/` |
---
## Checklist
- [ ] `.env` created with URI/user/password; `.env` in `.gitignore`
- [ ] Initial credentials saved to secure storage immediately after create
- [ ] `poll_status` called after create — do NOT connect before status = `running`
- [ ] `poll_status` called after pause/resume
- [ ] Correct tier selected (Free for dev, Pro+ for production, BC for HA)
- [ ] Region confirmed available for chosen tier and cloud provider
- [ ] Tenant ID provided for all create/list operations (required in multi-tenant orgs)
- [ ] Token refreshed if > 1 h old (or 403 received)
- [ ] Delete confirmed by user — data loss is permanent, no recovery
Scanned 9/12/2026
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!