Query (SQL)
SQL queries let you explore your Terraform infrastructure across all states.
Basic Syntax
Stategraph SQL follows standard SQL SELECT syntax:
SELECT columns
FROM table
WHERE conditions
ORDER BY column [ASC|DESC]
LIMIT n
Tables
instances
All resource instances across all states:
| Column | Type | Description |
|---|---|---|
address |
string | Full instance address |
resource_address |
string | Resource address (without index) |
fq_address |
string | Fully-qualified instance address |
fq_resource_address |
string | Fully-qualified resource address |
attributes |
jsonb | Resource attributes |
dependencies |
text[] | List of dependency addresses |
state_id |
uuid | Parent state ID |
index_key |
jsonb | Instance index key (for count/for_each) |
status |
string | Instance status |
schema_version |
integer | Schema version |
create_before_destroy |
boolean | Lifecycle setting |
deposed |
string | Deposed key if applicable |
sensitive_attributes |
jsonb | Sensitive attribute paths |
identity |
jsonb | Identity information |
identity_schema_version |
integer | Identity schema version |
private |
string | Private data |
Note: To query by resource type or provider, query the resources table directly.
states
All Terraform states:
| Column | Type | Description |
|---|---|---|
id |
uuid | State ID |
name |
string | State name |
group_id |
uuid | Group identifier |
workspace |
string | Workspace name |
tenant_id |
uuid | Tenant ID |
created_at |
timestamp | Creation time |
updated_at |
timestamp | Last update time |
deleted_at |
timestamp | Deletion time (if deleted) |
deleted_by |
uuid | User who deleted it (if deleted) |
resources
Resource definitions (use this to query by type/provider):
| Column | Type | Description |
|---|---|---|
address |
string | Resource address |
fq_address |
string | Fully-qualified resource address |
type |
string | Resource type (e.g., aws_instance) |
name |
string | Resource name |
provider |
string | Provider name |
module |
string | Module path |
mode |
string | Resource mode (managed/data) |
state_id |
uuid | Parent state ID |
providers
Provider information:
| Column | Type | Description |
|---|---|---|
name |
string | Provider name |
state_id |
uuid | Parent state ID |
outputs
State outputs:
| Column | Type | Description |
|---|---|---|
address |
string | Output address |
name |
string | Output name |
value |
jsonb | Output value |
type |
jsonb | Output type |
sensitive |
boolean | Is sensitive |
state_id |
uuid | Parent state ID |
transactions
Transaction history:
| Column | Type | Description |
|---|---|---|
id |
uuid | Transaction ID |
state |
string | Transaction state |
tags |
jsonb | Transaction tags |
tenant_id |
uuid | Tenant ID |
created_at |
timestamp | Creation time |
created_by |
uuid | Creator user ID |
completed_at |
timestamp | Completion time |
completed_by |
uuid | Completing user ID |
params |
jsonb | Transaction parameters |
tenants
Tenant information:
| Column | Type | Description |
|---|---|---|
id |
uuid | Tenant ID |
name |
string | Tenant name |
created_at |
timestamp | Creation time |
users
User information:
| Column | Type | Description |
|---|---|---|
id |
uuid | User ID |
name |
string | User name |
type |
string | User type |
created_at |
timestamp | Creation time |
transaction_logs
Transaction log entries:
| Column | Type | Description |
|---|---|---|
id |
uuid | Log entry ID |
tx_id |
uuid | Parent transaction ID |
action |
string | Action type (e.g., state_set) |
object_type |
string | Type of object affected |
state_id |
uuid | Associated state ID |
user_id |
uuid | User who performed action |
data |
jsonb | Additional action data |
created_at |
timestamp | When action occurred |
check_results
Terraform check results:
| Column | Type | Description |
|---|---|---|
config_addr |
string | Check configuration address |
object_kind |
string | Kind of object checked |
state_id |
uuid | Parent state ID |
status |
string | Check status |
check_entries
Individual check entries:
| Column | Type | Description |
|---|---|---|
config_addr |
string | Check configuration address |
object_addr |
string | Object address being checked |
state_id |
uuid | Parent state ID |
status |
string | Entry status |
failure_messages |
text[] | Failure messages if any |
More tables: Beyond those above, these are also queryable: cost_snapshots and
cost_snapshot_resources (see Cost Analysis), plus hcl,
hcl_refs, files, tfvars, transaction_subgraphs, revision_hashes, and revision_tx_hashes.
Run stategraph sql schema for the authoritative list of tables and columns.
Query Examples
List All Resources
SELECT * FROM instances
Filter by Resource Type
SELECT * FROM resources
WHERE type = 'aws_instance'
Count Resources by Type
SELECT type, COUNT(*) as type_count
FROM resources
GROUP BY type
ORDER BY COUNT(*) DESC
Find Resources with Specific Attribute
SELECT address, attributes->>'instance_type' as instance_type
FROM instances
WHERE attributes->>'instance_type' = 't3.micro'
Search by Address
SELECT * FROM instances
WHERE address = 'module.database.aws_rds_cluster.main'
Cross-State Query
To analyze resources across states, query each table separately. For example, list all states and then query resources within a specific state:
SELECT id, name FROM states
SELECT * FROM resources
WHERE type = 'aws_rds_cluster'
Note: To correlate rows across tables in one query, use a JOIN (see Cross-Table Queries) on a shared column such as state_id.
Module Analysis
SELECT module, COUNT(*) as resources
FROM resources
WHERE module IS NOT NULL
GROUP BY module
ORDER BY resources DESC
Operators
Comparison
| Operator | Description | Example |
|---|---|---|
= |
Equal | type = 'aws_instance' |
<> |
Not equal | type <> 'null_resource' |
< |
Less than | count < 10 |
> |
Greater than | count > 100 |
<= |
Less or equal | count <= 50 |
>= |
Greater or equal | count >= 5 |
Logical
| Operator | Description | Example |
|---|---|---|
AND |
Both conditions | type = 'aws_instance' AND module IS NOT NULL |
OR |
Either condition | type = 'aws_s3_bucket' OR type = 'aws_s3_bucket_policy' |
NOT |
Negate condition | NOT type = 'null_resource' |
Set Membership
| Operator | Description | Example |
|---|---|---|
IN |
Value in list | type IN ('aws_instance', 'aws_ebs_volume') |
Pattern Matching
| Operator | Description | Example |
|---|---|---|
LIKE |
Case-sensitive pattern match | type LIKE 'aws_%' |
ILIKE |
Case-insensitive pattern match | name ILIKE '%prod%' |
NOT LIKE |
Negated pattern match | type NOT LIKE 'aws_%' |
Use % to match any sequence of characters and _ to match a single character.
NULL Handling
| Operator | Description | Example |
|---|---|---|
IS NULL |
Is null | module IS NULL |
IS NOT NULL |
Is not null | module IS NOT NULL |
JSON Operators
Access JSON attributes using PostgreSQL-style operators:
Arrow Operators
| Operator | Description | Example |
|---|---|---|
-> |
Get JSON value | attributes->'tags' |
->> |
Get JSON as text | attributes->>'instance_type' |
#>> |
Get nested path as text | attributes#>>'{tags,Name}' |
JSON Path Examples
-- Get instance type
SELECT address, attributes->>'instance_type' as instance_type
FROM instances
WHERE attributes->>'instance_type' IS NOT NULL
-- Get nested tag value
SELECT address, attributes#>>'{tags,Environment}' as env
FROM instances
-- Filter by tag
SELECT * FROM instances
WHERE attributes->'tags'->>'Environment' = 'production'
JSON Contains
-- Find instances with specific tags
SELECT * FROM instances
WHERE attributes->'tags' @> '{"Environment": "production"}'::jsonb
Aggregations
COUNT
SELECT count(*) FROM instances
SELECT type, count(*) FROM resources GROUP BY type ORDER BY type
With HAVING
SELECT type, count(*) as type_count
FROM resources
GROUP BY type
HAVING count(*) > 10
ORDER BY type
Cross-Table Queries
Stategraph SQL supports INNER, LEFT, and RIGHT joins, so you can correlate data across tables in a single query. Match rows with an ON clause — most tables share a state_id, and instances.resource_address matches resources.address.
-- Join instances to their resource definitions to filter by type
SELECT i.address, r.type, r.provider
FROM instances AS i
INNER JOIN resources AS r
ON i.resource_address = r.address
AND i.state_id = r.state_id
WHERE r.type = 'aws_instance'
-- Count resources by type within each state
SELECT s.name AS state, r.type, COUNT(*) AS type_count
FROM resources AS r
INNER JOIN states AS s ON r.state_id = s.id
GROUP BY s.name, r.type
ORDER BY COUNT(*) DESC
Ordering and Limits
ORDER BY
SELECT * FROM instances
ORDER BY address ASC, status DESC
LIMIT
SELECT * FROM instances
LIMIT 100
Common Patterns
AWS Resources by Type
SELECT type, COUNT(*) as type_count
FROM resources
WHERE type IN ('aws_instance', 'aws_s3_bucket', 'aws_lambda_function', 'aws_rds_cluster')
GROUP BY type
ORDER BY COUNT(*) DESC
Tip: Use IN for a set of exact types, or LIKE for prefix matches such as type LIKE 'aws_%'.
Find Untagged Resources
SELECT address, resource_address
FROM instances
WHERE attributes->'tags' IS NULL
OR attributes->>'tags' = '{}'
Resources by State
SELECT state_id, COUNT(*) as resources
FROM instances
GROUP BY state_id
Security Groups
SELECT address, attributes->>'name' as name
FROM instances
WHERE resource_address = 'aws_security_group.main'
Note: To filter instances by resource type, join instances to resources on resource_address (see Cross-Table Queries).
Tips
- Use LIMIT - Always add LIMIT when exploring large datasets
- Specific columns - Select only needed columns for faster queries
- Index usage - Filters on
typeandaddressare fast - JSON paths - Use
#>>for nested JSON access - Test incrementally - Build complex queries step by step
Error Messages
| Error | Cause | Solution |
|---|---|---|
| "Syntax error" | Invalid SQL syntax | Check query structure |
| "Unknown column" | Column doesn't exist | Check column names |
| "Type mismatch" | Wrong data type in comparison | Cast values appropriately |
Next Steps
- Instances - Browse resource instances
- SQL Syntax Reference - Complete grammar reference
- API Reference - Query via API