Cloud Query is a service part of the Plural Console ecosystem that provides cloud resource querying capabilities. It uses an embedded PostgreSQL database to cache and process data about cloud resources.
- Query cloud resources across multiple providers
- Embedded PostgreSQL database for data storage and retrieval through PostgreSQL FDW steampipe extension
- gRPC API for integration with other services
- Sandboxed execution for Lua and Monty's limited Python subset
- Containerized deployment for easy scaling
CloudQuery provider support includes AWS, Azure, GCP, and VMware vSphere.
- Go 1.26.6 or higher
- Docker (for containerized deployment)
- Make
Running the complete service locally requires a writable temporary directory. The Python worker extracts gomonty's embedded native library there on first use.
- Clone the repository:
git clone https://github.com/pluralsh/console.git
cd console/go/cloud-query- Build the binary:
make buildThis will create the binary in the bin directory.
- Run the server locally:
./bin/cloud-query- Build the Docker image:
make image- Run the container:
make runOr manually:
docker run --rm -p 9192:9192 cloud-query:latestThe service can be configured using command-line flags or environment variables.
For a comprehensive list of all available command-line arguments, their default values, and usage examples, see the Command-Line Arguments Reference.
Cloud-Query exposes a gRPC API for querying cloud resources. Client implementations can use this API to retrieve information about cloud resources across different providers.
For detailed information about the API endpoints, request/response schemas, and usage examples, see the API Reference Documentation.
CloudQuery vSphere connections require server, user, and password; allow_unverified_ssl can be set to "true" when connecting to vCenter endpoints with untrusted certificates.
Cloud-Query also exposes ToolQuery gRPC endpoints for observability tools (metrics, logs, traces). Compatibility is per operation:
| Tool | Metrics | Metric Label Search | Logs | Traces | Notes |
|---|---|---|---|---|---|
| Prometheus | Yes | Yes | No | No | Prometheus HTTP API via prometheus/client_golang with optional bearer token or basic auth |
| Datadog | Yes | Yes | Yes | Yes | Datadog API v1/v2 via datadog-api-client-go (requires API key + app key; site optional) |
| Elasticsearch | No | No | Yes | No | Elasticsearch typed client v9 Search API (API key required) |
| Loki | No | No | Yes | No | REST client to /loki/api/v1/query_range (bearer token; optional X-Scope-OrgID) |
| Splunk | No | No | Yes | No | Splunk export search API (token or basic auth) |
| Tempo | No | No | No | Yes | REST client to /api/search and /api/traces/{traceID} (bearer token; optional X-Scope-OrgID) |
| Jaeger | No | No | No | Yes | Jaeger Query v3 REST API (GET /api/v3/traces) with structured trace filters |
| Dynatrace | Yes | No | Yes | Yes | Dynatrace Grail Query API (DQL via /platform/storage/query/v1/query:*, bearer token required) |
| CloudWatch | Yes | Yes | Yes | No | AWS SDK v2 (GetMetricData, ListMetrics, Logs Insights) with optional assume-role |
| Azure | Yes | Yes | Yes | No | Azure Monitor Go SDK with Azure AD client credentials |
ToolQuery also supports cloud function invocation via InvokeLambda for AWS Lambda, GCP Cloud Run services (Gen2), and Azure Functions using canonical identifiers and cloud connection credentials.
RunPython executes Monty's limited Python subset in a crash-isolated cloud-query python-worker subprocess. The request's optional JSON object is available as input; output starts as an empty dictionary and must remain a JSON-serializable dictionary. The response returns that dictionary as result_json and standard-output text from print() separately as stdout.
This is not CPython. The sandbox has no host filesystem, environment, network, subprocess, shell, pip, third-party packages, or callback access. Two workers start with only TMPDIR=/tmp; each request gets a fresh gomonty REPL. Failed or canceled workers are killed and replaced, and healthy workers are periodically recycled. Source is limited to 64 KiB, input and result JSON to 1 MiB, stdout to 64 KiB, interpreter execution to 10 seconds, interpreter-managed memory to 64 MiB, and recursion to 200 frames. Two runs execute concurrently and up to 16 more wait in a FIFO queue. A full queue is rejected. A parent watchdog ends a run after 15 seconds or the caller's earlier deadline.
The image embeds gomonty v0.0.14 and its platform-specific glibc library, built against official Monty commit c9802b5f30d11fecf9f153feb1dfdab3abda070e. It contains no separate monty executable. Both pins are recorded in OCI labels. Monty's memory limit covers interpreter-managed allocations rather than total pod RSS; operators should measure the workload before reducing the default cloud-query memory allocation.
Dynatrace:- Use a Dynatrace Platform token (
platformToken). - Required scopes:
storage:logs:readstorage:metrics:readstorage:spans:readstorage:entities:readstorage:buckets:read
- Use a Dynatrace Platform token (
Datadog:- Requires
apiKeyandappKeyfor ToolQuery operations. - Metric label search returns indexed metric tags from the previous hour.
- Requires
Elasticsearch:- Requires URL + username/password + index.
Prometheus/Loki/Tempo:- Use bearer token and/or basic auth credentials when required by your backend.
- If multi-tenant, also configure
tenant_id(X-Scope-OrgID).
Jaeger:- Uses Jaeger stable v3 Query API (
GET /api/v3/traces). Traces.queryis interpreted as Jaegerservice_name.- Additional structured filters are provided via
Traces.options.jaeger(operation_name,duration_min,duration_max,attributes). - Handles both
application/jsonandtext/plainContent-Type responses with automatic fallback parsing. - Supports bearer token and basic authentication.
- Uses Jaeger stable v3 Query API (
Splunk:- Use bearer token or username/password.
CloudWatch:regionis required.- Supports optional static AWS credentials, optional
assume role(role_arn), and default AWS credential chain fallback (including pod identity). - For logs queries, configure
log_group_namesor useSOURCEin the query string. log_group_namesapplies toLogsonly (notMetrics/MetricsSearch).- Examples:
- Logs with provider log groups: set
"log_group_names": ["/aws/eks/prod/app"]and use query like"fields @timestamp, @message | sort @timestamp desc". - Logs without provider log groups: omit
log_group_namesand use query like"SOURCE logGroups(namePrefix: [\"/aws/eks/prod/app\"]) | fields @timestamp, @message | sort @timestamp desc". - Metrics: use CloudWatch metric math expression, for example
SEARCH("{AWS/EC2,InstanceId} MetricName=\"CPUUtilization\"", "Average", 300). - Metric label search: use the
MetricsSearchresult format<namespace>/<metric>, for example"AWS/EC2/CPUUtilization"; labels are CloudWatch dimensions.
- Logs with provider log groups: set
Azure:- Connection requires
subscription_id,tenant_id,client_id, andclient_secret. - Metrics use
azmetrics.QueryResources:queryis a comma-separated metric name list (for example"Percentage CPU,Network In Total").options.azure.resource_idandoptions.azure.metrics_namespaceare required.- Optional Azure metrics options:
aggregation,filter,order_by,roll_up_by,metrics_endpoint. options.azure.metrics_endpointoverrides the metrics endpoint per request. If omitted, Cloud Query falls back tohttps://global.metrics.monitor.azure.com.
- Metric label search:
- Azure Managed Prometheus (
options.azure.prometheus_url) delegates to Prometheus label APIs. - Native Azure Monitor label names come from metric-definition dimensions.
- Native Azure Monitor label values require
options.azure.metrics_namespaceand are inferred from recent metric time-series metadata.
- Azure Managed Prometheus (
- Logs use
azlogs.QueryResource:queryis Azure Log Analytics query syntax (KQL).
- Connection requires
AWS:- Caller needs permission to invoke the target Lambda function (
lambda:InvokeFunction).
- Caller needs permission to invoke the target Lambda function (
GCP:- Caller needs:
run.routes.invokerun.services.get
- These map to Cloud Run IAM roles such as
roles/run.invoker(invoke) androles/run.viewer(service read).
- Caller needs:
Azure:- Caller needs permissions to read function metadata/secrets and invoke the function endpoint.
For provider-specific request payloads, query formats, and examples, see the API Reference Documentation.
The project includes detailed documentation:
- API Reference - Comprehensive guide to the gRPC API
- Command-Line Arguments - All available command-line options
/api- API proto definitions for gRPC services/cmd- Command-line entry points/docs- Project documentation/internal- Internal packages/connection- Database connection management/extension- Database extensions management/pool- Connection pooling/server- gRPC server implementation
/hack- Scripts and tools for development
- Fork the project
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request