Cloud configuration checks
Cloud configuration checks assess your AWS resources (S3, EBS, RDS, IAM, GuardDuty, CloudTrail, Backup vaults and more) against Elastio’s resilience rules, and map any failures to compliance frameworks. This guide covers how to read the score, list what’s failing, trigger a fresh assessment and export framework results for an audit.
Connect an AWS account
Section titled “Connect an AWS account”Cloud configuration data is collected by assuming a role in your AWS account. The preferred way to connect is POST /gateway/ingestion/v1/posture/credentials/role with role_arn and default_region (both required). You can also send an external_id (strongly recommended) and an aws_account_id. The role is assumed and verified before anything is saved, so a wrong trust policy fails at this step. To check a stored connection later, call GET /gateway/ingestion/v1/posture/connection-status, which returns connected: true or false and an error message.
Read the cloud configuration overview
Section titled “Read the cloud configuration overview”The overview gives the overall compliance score (0–100) and resource counts. It also breaks results down by service, severity, category and region, shows the 30-day score trend, and reports the latest sync. Resources the checkers couldn’t evaluate, usually because an AWS call was denied, are reported as unscored and left out of the score.
curl "https://acme.app.elastio.com/gateway/ingestion/v1/posture/overview" \ -H "Authorization: Bearer $ELASTIO_TOKEN"import os, requests
BASE = "https://acme.app.elastio.com"HEADERS = {"Authorization": f"Bearer {os.environ['ELASTIO_TOKEN']}"}
resp = requests.get(f"{BASE}/gateway/ingestion/v1/posture/overview", headers=HEADERS, timeout=30)resp.raise_for_status()overview = resp.json()print(f"score {overview['score']:.1f} non-compliant {overview['non_compliant']}/{overview['total_resources']}")print("open violations by severity:", overview.get("by_severity"))$Base = "https://acme.app.elastio.com"$Headers = @{ Authorization = "Bearer $env:ELASTIO_TOKEN" }$overview = Invoke-RestMethod -Uri "$Base/gateway/ingestion/v1/posture/overview" -Headers $Headers$overview | Select-Object score, total_resources, compliant, non_compliant, unscoredExample response (trimmed):
{ "total_resources": 47, "compliant": 31, "non_compliant": 16, "unscored": 0, "score": 65.96, "trend": [ { "score": 55, "captured_at": "2026-09-20T06:00:00Z" }, { "score": 65.96, "captured_at": "2026-10-12T06:00:00Z" } ], "by_service": { "s3_bucket": { "total": 8, "compliant": 3, "non_compliant": 5, "unscored": 0 }, "ebs_volume": { "total": 15, "compliant": 12, "non_compliant": 3, "unscored": 0 } }, "by_severity": { "critical": 4, "high": 8, "medium": 3 }, "by_category": { "Immutability": 6, "Cryptographic": 5, "RPO/RTO": 4 }, "regions": [ { "region": "us-east-1", "assets": 40, "gaps": 14, "confidence_score": 65 } ], "last_sync": { "id": "a1b2c3d4-0000-4000-8000-000000000001", "status": "completed", "trigger_type": "scheduled", "accounts_scanned": 3, "regions_scanned": 5, "resources_found": 47, "findings_found": 16, "coverage_complete": true }, …}List failing checks
Section titled “List failing checks”Cloud configuration findings are rule failures on specific resources, sorted critical first. By default only open findings are returned. Pass status=resolved or status=suppressed to see others. To filter by several severities or categories, repeat the parameter, for example severity=critical&severity=high. Pagination is limit (default 50, max 1000) and offset. Use resource_id to narrow to one cloud resource, such as vol-0abc… or a bucket name.
curl "https://acme.app.elastio.com/gateway/ingestion/v1/posture/findings?severity=critical&severity=high&limit=200" \ -H "Authorization: Bearer $ELASTIO_TOKEN"params = {"severity": ["critical", "high"], "limit": 200, "offset": 0}findings = []while True: resp = requests.get(f"{BASE}/gateway/ingestion/v1/posture/findings", headers=HEADERS, params=params, timeout=30) resp.raise_for_status() page = resp.json() findings.extend(page.get("findings") or []) params["offset"] += page["limit"] if params["offset"] >= page["total"]: break
for f in findings: controls = ", ".join(c["control"] for c in f.get("compliance", [])) print(f["severity"], f["rule_code"], f["resource_name"], controls)$page = Invoke-RestMethod -Uri "$Base/gateway/ingestion/v1/posture/findings?severity=critical&severity=high&limit=200" -Headers $Headers$page.findings | Format-Table severity, rule_code, resource_type, resource_nameExample response (trimmed):
{ "findings": [ { "id": "3d4e5f6a-7b8c-4d9e-8f0a-1b2c3d4e5f6a", "status": "open", "rule_code": "S3_VERSIONING", "rule_title": "…", "severity": "critical", "category": "Immutability", "resource_name": "production-data-bucket", "resource_type": "s3_bucket", "source": "posture", "compliance": [ { "framework": "ISO27001", "control": "…", "control_id": "…" } ], "created_at": "2026-10-01T06:00:00Z", "resolved_at": null, … } ], "total": 12, "limit": 200, "offset": 0}To see the assessment per resource instead of per rule, use GET /gateway/ingestion/v1/posture/resources. You can filter it by type (for example s3_bucket, ebs_volume, rds_instance), status (compliant, non_compliant, error, unknown), account_id and region. Each row includes open_findings_count and compliance_impact. The full list of rules is at GET /gateway/ingestion/v1/posture/rules.
Run a sync and wait for it
Section titled “Run a sync and wait for it”Cloud configuration syncs run on a schedule. You can start one on demand after changing infrastructure, for example. The call returns 202 Accepted with a sync_id right away. If a sync is already running, it returns 409. Poll GET /gateway/ingestion/v1/posture/sync/status until status is completed or failed.
curl -X POST "https://acme.app.elastio.com/gateway/ingestion/v1/posture/sync" \ -H "Authorization: Bearer $ELASTIO_TOKEN"
# Then poll:curl "https://acme.app.elastio.com/gateway/ingestion/v1/posture/sync/status" \ -H "Authorization: Bearer $ELASTIO_TOKEN"import time
resp = requests.post(f"{BASE}/gateway/ingestion/v1/posture/sync", headers=HEADERS, timeout=30)if resp.status_code == 409: print("a sync is already running; polling it instead")else: resp.raise_for_status() print("started", resp.json()["sync_id"])
while True: status = requests.get(f"{BASE}/gateway/ingestion/v1/posture/sync/status", headers=HEADERS, timeout=30).json() if status["status"] in ("completed", "failed", "none"): break print(f"{status['status']} {status.get('progress_percent', 0)}%") time.sleep(30)
if not status.get("coverage_complete", True): print(f"missed {status.get('missed_accounts')} accounts, {status.get('missed_regions')} regions")try { $start = Invoke-RestMethod -Method Post -Uri "$Base/gateway/ingestion/v1/posture/sync" -Headers $Headers "started $($start.sync_id)"} catch { if ($_.Exception.Response.StatusCode.value__ -ne 409) { throw }}do { Start-Sleep -Seconds 30 $status = Invoke-RestMethod -Uri "$Base/gateway/ingestion/v1/posture/sync/status" -Headers $Headers "$($status.status) $($status.progress_percent)%"} while ($status.status -in @("pending", "in_progress"))Example sync start response (202 Accepted):
{ "message": "posture sync started", "sync_id": "a1b2c3d4-0000-4000-8000-000000000002", "status": "pending" }Example status response once the sync has finished (trimmed):
{ "id": "a1b2c3d4-0000-4000-8000-000000000002", "status": "completed", "trigger_type": "manual", "started_at": "2026-10-12T10:00:00Z", "completed_at": "2026-10-12T10:06:40Z", "accounts_scanned": 3, "regions_scanned": 5, "expected_accounts": 3, "expected_regions": 5, "progress_percent": 100, "coverage_complete": true, "missed_accounts": 0, "missed_regions": 0, "duration_seconds": 400, …}If no sync has ever run, the status endpoint returns {"message": "…", "status": "none"} instead.
Export compliance framework results
Section titled “Export compliance framework results”This maps cloud configuration results to compliance frameworks. Known values include ISO 27001, NIST CSF, SOC 2, NYDFS and DORA. Each framework has a score, a status (Healthy, At Risk, Failing or Unscored), and a list of controls. Each control has a pass/fail status and the names of the assets that fail it. Controls whose checks couldn’t run are Unscored and left out of the score, so check unscored_controls before you present a high score as complete.
curl "https://acme.app.elastio.com/gateway/ingestion/v1/posture/compliance" \ -H "Authorization: Bearer $ELASTIO_TOKEN"resp = requests.get(f"{BASE}/gateway/ingestion/v1/posture/compliance", headers=HEADERS, timeout=30)resp.raise_for_status()for fw in resp.json()["frameworks"]: failed = [c for c in fw["controls"] if c["status"] == "Failed"] print(f"{fw['framework']}: {fw['score']:.0f} ({fw['status']}), {len(failed)} failed controls") for c in failed: print(" ", c["control_id"], c["control_title"], "->", ", ".join(c["assets"]))$result = Invoke-RestMethod -Uri "$Base/gateway/ingestion/v1/posture/compliance" -Headers $Headers$result.frameworks | Format-Table framework, score, status, unscored_controlsExample response (trimmed):
{ "frameworks": [ { "framework": "ISO27001", "score": 50, "status": "At Risk", "unscored_controls": 0, "controls": [ { "control_id": "A.10.1.1", "control_title": "Cryptographic Controls", "status": "Failed", "assets": ["production-data-bucket"] }, { "control_id": "A.12.4.1", "control_title": "Forensic Logging", "status": "Passed", "assets": [] } ] } ], "policies": [ … ], "total": 5, "limit": 50, "offset": 0}The Compliance tag has a separate endpoint, GET /gateway/api/v1/compliance/status, which takes an optional start_date and end_date (YYYY-MM-DD). It returns pass/fail controls for each framework (data[].framework, data[].controls[].name, description, isPassed) over that window.
Suppress an accepted configuration violation
Section titled “Suppress an accepted configuration violation”A suppression stops matching violations from being emitted in future syncs. reason is required. You must also give at least one scope field: rule_code, aws_account_id, region, resource_type, resource_id, resource_id_pattern (an SQL LIKE pattern) or tag_filters. expires_at is optional. The call returns 201 with the stored suppression.
curl -X POST "https://acme.app.elastio.com/gateway/ingestion/v1/posture/violation-suppressions" \ -H "Authorization: Bearer $ELASTIO_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "rule_code": "S3_VERSIONING", "aws_account_id": "123456789012", "resource_id_pattern": "scratch-%", "reason": "Scratch buckets hold reproducible build artifacts only.", "expires_at": "2027-03-31T00:00:00Z" }'resp = requests.post( f"{BASE}/gateway/ingestion/v1/posture/violation-suppressions", headers=HEADERS, json={ "rule_code": "S3_VERSIONING", "aws_account_id": "123456789012", "resource_id_pattern": "scratch-%", "reason": "Scratch buckets hold reproducible build artifacts only.", "expires_at": "2027-03-31T00:00:00Z", }, timeout=30,)resp.raise_for_status()print("suppression", resp.json()["id"])$body = @{ rule_code = "S3_VERSIONING" aws_account_id = "123456789012" resource_id_pattern = "scratch-%" reason = "Scratch buckets hold reproducible build artifacts only." expires_at = "2027-03-31T00:00:00Z"} | ConvertTo-JsonInvoke-RestMethod -Method Post -Uri "$Base/gateway/ingestion/v1/posture/violation-suppressions" ` -Headers $Headers -ContentType "application/json" -Body $bodyExample response (201 Created, trimmed):
{ "id": "8b9c0d1e-2f3a-4b4c-9d5e-6f7a8b9c0d1e", "rule_code": "S3_VERSIONING", "aws_account_id": "123456789012", "resource_id_pattern": "scratch-%", "suppressed_by": "admin@example.com", "reason": "Scratch buckets hold reproducible build artifacts only.", "effective_from": "2026-10-12T10:20:00Z", "expires_at": "2027-03-31T00:00:00Z", …}List active suppressions with GET /gateway/ingestion/v1/posture/violation-suppressions. Remove one with DELETE /gateway/ingestion/v1/posture/violation-suppressions/{id}.
AWS Elastic Disaster Recovery source servers
Section titled “AWS Elastic Disaster Recovery source servers”The DRS endpoints link Elastio assets to AWS Elastic Disaster Recovery source servers. GET /gateway/api/v1/assets/{asset_id}/drs_source_servers lists the DRS source servers for an asset (aws_account_id, aws_region, aws_source_server_id, policy_duration_in_minutes). GET /gateway/api/v1/drs_source_servers/{id} returns one of them.
All endpoints
Section titled “All endpoints”Cloud Configuration
19 operations in the reference →- GET
/gateway/ingestion/v1/posture/overviewGet the cloud configuration overview - GET
/gateway/ingestion/v1/posture/resourcesList cloud configuration resources - GET
/gateway/ingestion/v1/posture/findingsList cloud configuration findings (violations) - GET
/gateway/ingestion/v1/posture/complianceGet compliance framework scores - GET
/gateway/ingestion/v1/posture/lineageGet asset relationship graph - GET
/gateway/ingestion/v1/posture/lineage/countCount lineage resources (total and affected) - POST
/gateway/ingestion/v1/posture/syncRun configuration checks now - GET
/gateway/ingestion/v1/posture/sync/statusGet configuration check status - GET
/gateway/ingestion/v1/posture/connection-statusValidate stored AWS credentials for configuration checks - GET
/gateway/ingestion/v1/posture/syncsList configuration check history - POST
/gateway/ingestion/v1/posture/credentials/roleConnect a tenant's AWS account by assuming a role (preferred) - GET
/gateway/ingestion/v1/posture/credentialsGet stored AWS credential metadata - POST
/gateway/ingestion/v1/posture/credentialsSave AWS credentials for configuration checks - DELETE
/gateway/ingestion/v1/posture/credentialsDelete stored AWS credentials - GET
/gateway/ingestion/v1/posture/rulesList enabled configuration rules - GET
/gateway/ingestion/v1/posture/violation-suppressionsList active violation suppressions - POST
/gateway/ingestion/v1/posture/violation-suppressionsCreate a violation suppression - GET
/gateway/ingestion/v1/posture/violation-suppressions/{id}Get a single violation suppression - DELETE
/gateway/ingestion/v1/posture/violation-suppressions/{id}Delete a violation suppression