20 Oct 2026 07:29 PM
I wanted a repeatable way to create Dynatrace dashboards without hand-building every tile. Here is a tenant-neutral workflow: use GitHub Copilot to draft the document and DQL, use the open-source dtctl CLI to validate and deploy, and optionally use Rovo to locate your own documentation before deciding what to monitor. Review AI-generated code yourself before publishing.
Install dtctl from its official repository and authenticate to the intended Dynatrace environment. Replace YOUR_ENVIRONMENT_URL with your HTTPS Dynatrace apps URL:
dtctl auth login --context demo --environment YOUR_ENVIRONMENT_URL dtctl config current-context dtctl auth whoami --plain
Ensure your identity can query the chosen data and read/write dashboard documents. Do not store tokens or exported tenant data in a public repository.
Example prompt: "Create a Dynatrace dashboard JSON with a 24-column layout, a heading and an error-log count tile. Use the dashboard timeframe picker rather than hardcoded DQL time filters. Verify fields and metric keys, test each query, and match every tile ID to a layout ID. Do not include tenant URLs, tokens, or organization-specific names." Rovo can optionally help find relevant internal docs on data sources and ownership; confirm its findings in Dynatrace and keep private docs out of public posts.
{
"name": "Example log health",
"type": "dashboard",
"content": {
"version": 21,
"annotations": [],
"settings": {
"defaultTimeframe": {
"enabled": true,
"value": { "from": "now()-2h", "to": "now()" }
}
},
"layouts": {
"heading": { "x": 0, "y": 0, "w": 24, "h": 2 },
"errors": { "x": 0, "y": 2, "w": 24, "h": 6 }
},
"tiles": {
"heading": {
"type": "markdown",
"content": "# Example log health\nUse the dashboard timeframe selector."
},
"errors": {
"type": "data",
"title": "Error log count",
"query": "fetch logs\n| summarize errors = countIf(loglevel == \"ERROR\")",
"visualization": "singleValue",
"visualizationSettings": {
"autoSelectVisualization": false,
"singleValue": { "recordField": "errors" }
},
"querySettings": {},
"davis": {
"enabled": false,
"davisVisualization": { "isAvailable": true }
}
}
}
}
}The data tile and layout both use errors. The query produces one numeric value, which suits a single-value tile. The DQL does not override the dashboard time picker.
dtctl verify query 'fetch logs | summarize errors = countIf(loglevel == "ERROR")' dtctl query 'fetch logs | summarize errors = countIf(loglevel == "ERROR")' -o json --plain dtctl apply -f dashboard.json --dry-run --plain
The generic query and dashboard above passed verify query and apply --dry-run checks. Still check query results and data access in your environment: syntactically valid DQL alone does not guarantee a useful visualization.
dtctl apply -f dashboard.json --write-id --plain dtctl get dashboards --mine dtctl describe dashboard DASHBOARD_ID
On the first apply, --write-id writes the server-assigned ID to your file so subsequent applies update the same dashboard instead of creating duplicates. Open the URL returned by apply and inspect the rendered tile, selected timeframe, access permissions, and empty-data behavior. Commit the reviewed JSON if you manage it as code. Before updating an existing dashboard, reconcile any UI edits: applying an older definition may overwrite them. The dtctl dashboard guide covers round-tripping and update options.
In short: Copilot drafts, optional Rovo supplies context, dtctl validates and deploys, and a human verifies the result. Keep public examples generic; do not post real tenant URLs, entity IDs, exports, credentials, or private topology.
Featured Posts