30 Dec 2025 02:42 AM
We have created some charts which display the data from another tenant. In gen2 version, it can be configured like this:
When I try to convert the chart to gen3 version, I am not sure how to use DQL to achieve this. Any suggestions?
Solved! Go to Solution.
30 Dec 2025 07:50 AM
Hi Hillman,
I haven't done this myself yet but I think you can achieve this by querying the data from another tenant via API in a code tile on a gen3 dashboard.
30 Dec 2025 08:37 AM - edited 30 Dec 2025 08:43 AM
Hi @Hillman ,
In Dynatrace 3rd Gen, a flexible approach to query data from another environment is to use a Dashboard/Notebook Code tile (external/remote environment data). The idea is to create a Platform Token in the remote environment with the required storage read scopes, store it in the local environment Credential Vault, and then call the remote query endpoint from the Code tile. You can use the sample below and simply replace the remote URL, Credential Vault, and adjust the DQL as needed.
import { credentialVaultClient } from "@dynatrace-sdk/client-classic-environment-v2";
async function fetchRemoteDql(credentialId, url, query) {
const { token } = await credentialVaultClient.getCredentialsDetails({ id: credentialId });
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
query,
requestTimeoutMilliseconds: 60000,
enablePreview: true,
}),
});
if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
return (await response.json()).result;
}
export default async function () {
const credentialId = "CREDENTIALS_VAULT-XXXXXXX"; // your vault entry on the *local* env
const url =
"https://XXXXXXXX.apps.dynatrace.com/platform/storage/query/v1/query:execute";
const query = `
timeseries usage = avg(dt.host.cpu.usage), by: { dt.entity.host }
| fieldsAdd entityName(dt.entity.host)
| sort arrayAvg(usage) desc
| limit 20
`.trim();
return await fetchRemoteDql(credentialId, url, query);
}Note: make sure the remote environment url is added to the allowlist if it is configured to limit external requests from the environment.
https://developer.dynatrace.com/develop/app-functions/allow-outbound-connections/
Regards,
Hamdy
30 Dec 2025 09:07 AM
Hi @Mohamed_Hamdy,
I have tried with the DQL but I have found that it throws the following error.
{
"error": {
"code": 540,
"message": "Execution crashed.",
"details": {
"logs": "403: The requested credential details cannot be accessed by the current user or entity.\n at CredentialVaultClient.getCredentialsDetails (file:///opt/sdk_modules/@dynatrace-sdk/client-classic-environment-v2@4/esm/index.js:6983:19)\n at eventLoopTick (ext:core/01_core.js:177:7)\n at async fetchRemoteDql (file:///script.ts:4:21)\n at async default (file:///script.ts:34:10)\n",
"type": "UNCAUGHT_EXCEPTION",
"message": "Uncaught (in promise) 403: The requested credential details cannot be accessed by the current user or entity.",
"details": {
"lineNumber": 6983,
"startColumn": 19,
"stack": "403: The requested credential details cannot be accessed by the current user or entity.\n at CredentialVaultClient.getCredentialsDetails (file:///opt/sdk_modules/@dynatrace-sdk/client-classic-environment-v2@4/esm/index.js:6983:19)\n at eventLoopTick (ext:core/01_core.js:177:7)\n at async fetchRemoteDql (file:///script.ts:4:21)\n at async default (file:///script.ts:34:10)"
}
}
}
}
30 Dec 2025 10:22 AM
Hello @Hillman ,
It looks like the failure is related to Credential Vault access (the code tile can’t read the credential)
Could you please confirm the following:
Did you create a Platform token in the remote environment (not an Environment API access token)? If yes, what scopes/permissions are enabled on that token?
How was the Credential Vault entry created on the local environment?
For more details on Platform Tokens, please refer to Dynatrace documentation: https://docs.dynatrace.com/docs/shortlink/platform-tokens
06 Jan 2026 03:45 AM
Hi @Mohamed_Hamdy ,
Oh, I have mixed up the Platform token with the Environment API access token. Let me create one and try again. Regarding the scope, is it granting all storage:*:read?
08 Jan 2026 03:23 AM - edited 08 Jan 2026 06:19 AM
@Mohamed_Hamdy I have created a Platform Token with all "storage:*:read" scopes in remote tenant, then create a credential vault entry in local tenant. However, it still returns the following error:
{
"error": {
"code": 540,
"message": "Execution crashed.",
"details": {
"logs": "403: The requested credential details cannot be accessed by the current user or entity.\n at CredentialVaultClient.getCredentialsDetails (file:///opt/sdk_modules/@dynatrace-sdk/client-classic-environment-v2@4/esm/index.js:6983:19)\n at eventLoopTick (ext:core/01_core.js:177:7)\n at async fetchRemoteDql (file:///script.ts:4:21)\n at async default (file:///script.ts:34:10)\n",
"type": "UNCAUGHT_EXCEPTION",
"message": "Uncaught (in promise) 403: The requested credential details cannot be accessed by the current user or entity.",
"details": {
"lineNumber": 6983,
"startColumn": 19,
"stack": "403: The requested credential details cannot be accessed by the current user or entity.\n at CredentialVaultClient.getCredentialsDetails (file:///opt/sdk_modules/@dynatrace-sdk/client-classic-environment-v2@4/esm/index.js:6983:19)\n at eventLoopTick (ext:core/01_core.js:177:7)\n at async fetchRemoteDql (file:///script.ts:4:21)\n at async default (file:///script.ts:34:10)"
}
}
}
}
08 Jan 2026 09:32 AM
@Mohamed_Hamdy I have found out the root cause now. I have to enable the following options in credential vault entry to make it work:
13 Jan 2026 09:23 AM
@Mohamed_Hamdy Sorry that I have got one more question. If the original DQL contains "`". How can I embed it into the code?
19 Jan 2026 01:14 PM
Hello @Hillman ,
If your original DQL contains a backtick ` and you’re embedding it in the code using a JavaScript template literal (also delimited by backticks), you just need to escape the backtick inside the DQL.
Use (backslash + backtick).
Example:
const query = `
fetch logs
| filter contains(content, \`some-value\`)
| limit 20
`.trim();
21 Jan 2026 07:36 PM - edited 21 Jan 2026 08:11 PM
follow the question, can you help me with the query? there is lookup to join two table in this. I have result when i run two query separate but when I combine them with lookup, there is no result. but they support have results as
export default async function() {
const credentialId = "CREDENTIALS_VAULT-A80F208D9C83954A"; // Replace with your credential vault ID.
const url = "https://{environmentid}.apps.dynatrace.com/platform/storage/query/v1/query:execute"; // Replace with API URL.
const query =
load "/lookups/2026Wave1-4"
| fieldsAdd Servers=lower(Name)
| fieldsAdd tenant= if(IN(Environment,{"Production", "DisasterRecovery"}), "Production"
,else: if(In(Environment,{"Test","QualityAssurance","QAEnvironment"}),"Non-Prod"))
| filterout In(Environment,{"Production", "DisasterRecovery"})
|lookup [fetch dt.entity.host
|fieldsAdd hostname = entity.name, hostGroupName, installerVersion
| parse hostname, """LD:Host '.'"""
| fields Host = lower(Host), entity.name, hostGroupName], sourceField:Servers,lookupField:Host, prefix:"DT-"
| fieldsAdd Status = if(`Servers` == `DT-Host`, "Dynatrace", else:"need onboard")
//|filter contains(`AppCode`,"FDP")
| filter isNotNull(tenant)
// |filter contains(`Wave`,"")
| summarize by:{AppCode, Environment, Tier,Wave, tenant, SRE}, {
Plan=countif(Status=="need onboard"),
Installed=countif(Status == "Dynatrace"),
total=count()
}
//| fieldsAdd Need_Onboard = (toDouble(Plan) / toDouble(total)) * 100
| fieldsAdd Onboard_status = (toDouble(Installed) / toDouble(total)) * 100
| fieldsAdd Status = if(Onboard_status == 100, "Done",
else: if(Onboard_status > 0, "In-Progress",
else: "Not_started"))
";
21 Jan 2026 09:28 PM
Try this, I’ve only corrected the backticks/field reference to avoid parsing issues. Please let me know if it works on your side.
const query = `
load "/lookups/2026Wave1-4"
| fieldsAdd Servers=lower(Name)
| fieldsAdd tenant= if(IN(Environment,{"Production", "DisasterRecovery"}), "Production"
,else: if(In(Environment,{"Test","QualityAssurance","QAEnvironment"}),"Non-Prod"))
| filterout In(Environment,{"Production", "DisasterRecovery"})
|lookup [fetch dt.entity.host
|fieldsAdd hostname = entity.name, hostGroupName, installerVersion
| parse hostname, """LD:Host '.'"""
| fields Host = lower(Host), entity.name, hostGroupName
], sourceField:Servers, lookupField:Host, prefix:"DT-"
| fieldsAdd Status = if(Servers == \`DT-Host\`, "Dynatrace", else:"need onboard")
| filter isNotNull(tenant)
| summarize by:{AppCode, Environment, Tier, Wave, tenant, SRE}, {
Plan=countif(Status=="need onboard"),
Installed=countif(Status == "Dynatrace"),
total=count()
}
| fieldsAdd Onboard_status = (toDouble(Installed) / toDouble(total)) * 100
| fieldsAdd Status = if(Onboard_status == 100, "Done",
else: if(Onboard_status > 0, "In-Progress",
else: "Not_started"))
`.trim();
22 Jan 2026 01:43 AM - edited 22 Jan 2026 01:43 AM
@Mohamed_Hamdy I have tried to use backslash to escape but the query still fails. ☹️
Featured Posts