From 07bcd4b105a028dc42dfd9528691e236f0b2a32a Mon Sep 17 00:00:00 2001 From: xiangtin <36420037+xiangtin@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:52:38 -0800 Subject: [PATCH] Add RTT view to the User page (#2838) * Add RTT view to User page * Remove Available session for personal host pool view * Update to use case-insensitive comparison * Revert unintention changes * Apply changes to AtScale User report * Update view customWidth * Change the view width to 100% for feed refresh * Remove tolower function --- .../AtScale/User Report/User Report.workbook | 730 +++++++++-------- .../User Report/User report.workbook | 763 +++++++++--------- .../Host Pool Overview.workbook | 287 ++++++- .../Template Versioning Footer.workbook | 2 +- 4 files changed, 1023 insertions(+), 759 deletions(-) diff --git a/Workbooks/Windows Virtual Desktop/AtScale/User Report/User Report.workbook b/Workbooks/Windows Virtual Desktop/AtScale/User Report/User Report.workbook index a5724686a8..12be8e6ba3 100644 --- a/Workbooks/Windows Virtual Desktop/AtScale/User Report/User Report.workbook +++ b/Workbooks/Windows Virtual Desktop/AtScale/User Report/User Report.workbook @@ -393,362 +393,49 @@ "type": 3, "content": { "version": "KqlItem/1.0", - "query": "WVDConnections\r\n| where UserName == '{UserName}'\r\n| as SourceData\r\n| where State==\"Started\"\r\n| project-rename StartTime=TimeGenerated\r\n| join kind=fullouter\r\n(\r\n SourceData\r\n | where State == \"Completed\"\r\n | project CorrelationId, SessionHostName, EndTime=TimeGenerated, _ResourceId\r\n) on CorrelationId\r\n| extend HostPool=extract_all(\"(?i:resourceGroups/([^/]+)/providers/microsoft.desktopvirtualization/hostpools/([^/]+))\",coalesce(_ResourceId, _ResourceId1))[0]\r\n| project CorrelationId=coalesce(CorrelationId, CorrelationId1), StartTime=coalesce(StartTime, {TimeRange:start}), EndTime=coalesce(EndTime, {TimeRange:end}), HostPool=strcat(HostPool[0],\"-\",HostPool[1])\r\n| extend ProbeTime = range(bin_at(StartTime, {TimeRange:grain}, {TimeRange:start}), bin_at(EndTime + {TimeRange:grain}, {TimeRange:grain}, {TimeRange:end}), {TimeRange:grain})\r\n| mv-expand ProbeTime to typeof(datetime)\r\n| extend ConnectionContainsProbe = (StartTime < ProbeTime and EndTime > ProbeTime),\r\n ConnectionEndedInPreviousBucket = (StartTime < ProbeTime and EndTime + {TimeRange:grain} >= ProbeTime) \r\n| make-series Connections=dcountif(CorrelationId, ConnectionContainsProbe or ConnectionEndedInPreviousBucket) on ProbeTime from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by HostPool", - "size": 1, - "title": "Connections over time for {UserName}", - "noDataMessage": "No connections detected for this user in the specified time frame.", - "showExportToExcel": true, - "queryType": 0, - "resourceType": "microsoft.desktopvirtualization/hostpools", - "crossComponentResources": [ - "{HostPools}" - ], - "visualization": "areachart", - "chartSettings": { - "showMetrics": false, - "showLegend": true - } - }, - "customWidth": "50", - "name": "ConnectionsbyUser" - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDFeeds\r\n| where UserName == '{UserName}'\r\n| make-series FeedCount = dcount(CorrelationId) default=long(0) on TimeGenerated from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain}", + "query": "let GetDaysInTimeRange = (Period:timespan) {\r\n // Get the number of days in the period, note the period MUST be longer than 1 day, and in full days\r\n max_of(toint(round(Period/1d, 0)), 1);\r\n};\r\nlet GetTimespanAverage = (Time:timespan, Period:timespan) {\r\n // Get the number of days in the period, note the period MUST be longer than 1 day, and in full days\r\n let Days = GetDaysInTimeRange(Period);\r\n // Total hours is the number of days*24, plus the hours\r\n let TotalHours = toint(format_timespan(Time, \"d\")) * 24 + toint(format_timespan(Time, \"H\"));\r\n // Calculate the average hours\r\n let AvgHours = TotalHours / Days;\r\n // We're rounding down for the average, figure out fractional hours left \r\n let MinuteResidue = toint(((1.0*TotalHours/Days) - AvgHours) * 60);\r\n // Total minutes is the number of minutes plus the residue from above\r\n let TotalMinutes = toint(format_timespan(Time, \"m\")) + MinuteResidue;\r\n // Calculate average minutes\r\n let AvgMinutes = toint(round(todouble(TotalMinutes) / Days));\r\n // Pad with zero if necessary\r\n let MinuteStr = iif(AvgMinutes < 10, strcat(\"0\", AvgMinutes), tostring(AvgMinutes));\r\n let Result = strcat(\"~ \", AvgHours, \":\", MinuteStr, \" h\");\r\n iif(Time/1s > 0, Result, '');\r\n};\r\nlet ProbeRange = range ProbeDay from {TimeRange:start} to {TimeRange:end} step 1d | extend JoinKey=1;\r\nWVDConnections\r\n| where UserName == \"{UserName}\"\r\n| as UserConnections\r\n| where State == \"Started\" and TimeGenerated <= {TimeRange:end}\r\n| extend StartTime = TimeGenerated\r\n| join kind=rightouter\r\n(\r\n UserConnections\r\n | where State == \"Completed\"\r\n | extend EndTime = TimeGenerated\r\n) on CorrelationId\r\n| extend StartTime = min_of(coalesce(StartTime, {TimeRange:start}), EndTime), UserName=coalesce(UserName,UserName1) // If the start time has fallen out of retention, it should not be set to after the EndTime\r\n| where StartTime between ({TimeRange:start} .. {TimeRange:end}) or // Connections starting in our date range\r\n EndTime between ({TimeRange:start} .. {TimeRange:end}) or // Connections ending in our date range\r\n (StartTime <= {TimeRange:start} and EndTime >= {TimeRange:end}) // Connections that started before and ended after the range\r\n| extend EndTime=min_of(EndTime, {TimeRange:end})-1s, StartTime=max_of(StartTime,{TimeRange:start})+1s // Crop connections to within the report boundary\r\n| extend Duration = EndTime-StartTime\r\n| sort by ClientType asc // Sort for Clients string to be homogenous\r\n| as UserUsage\r\n| join kind=leftouter\r\n(\r\n WVDCheckpoints\r\n | where (Name == \"LaunchExecutable\" and Parameters.connectionStage == \"RdpShellAppExecuted\") or Name==\"RdpShellAppExecuted\"\r\n | project CorrelationId, Parameters=tostring(Parameters.filename)\r\n) on CorrelationId\r\n| summarize AppsToStartConnection=dcount(ResourceAlias),\r\n AppsUsed=dcount(Parameters),\r\n Connections=dcount(CorrelationId),\r\n Time=sum(Duration),\r\n ClientCount=dcount(ClientType)\r\n by UserName\r\n| join kind=leftouter \r\n(\r\n // Count days with usage\r\n UserUsage\r\n | extend JoinKey = 1\r\n | join kind=fullouter ProbeRange on JoinKey\r\n | make-series ConnectedToday=dcountif(CorrelationId, bin(ProbeDay, 1d) == bin(StartTime, 1d) or \r\n bin(ProbeDay, 1d) == bin(EndTime, 1d) or \r\n bin(ProbeDay, 1d) between (bin(StartTime, 1d)..bin(EndTime, 1d)))\r\n on ProbeDay from {TimeRange:start} to {TimeRange:end} step 1d by UserName\r\n | mv-expand ProbeDay to typeof(datetime), ConnectedToday to typeof(int)\r\n | summarize Days=countif(ConnectedToday > 0) by UserName\r\n) on UserName\r\n| project\r\n ['Days in time range']=GetDaysInTimeRange({TimeRange:end} - {TimeRange:start}),\r\n ['Days with usage']=coalesce(Days, 0),\r\n ['Apps to start connection']=coalesce(AppsToStartConnection, 0),\r\n ['Apps used']=coalesce(AppsUsed, 0),\r\n ['Connection count']=coalesce(Connections, 0),\r\n ['Connection time']=strcat(\"~ \", toint(format_timespan(Time, 'd'))*24+toint(format_timespan(Time, 'H')), \":\", format_timespan(Time, 'mm'), \" h\"),\r\n ['Average daily time']=GetTimespanAverage(Time,{TimeRange:end} - {TimeRange:start}),\r\n ['Client count']=coalesce(ClientCount, 0)\r\n| evaluate narrow()\r\n| project Metric=replace(@\"\\[|'|\\]\", \"\", Column), Value", "size": 1, - "title": "Feed refreshes over time for {UserName}", - "noDataMessage": "No feed refreshes for this user in the specified time frame.", - "timeContextFromParameter": "TimeRange", - "showExportToExcel": true, - "queryType": 0, - "resourceType": "microsoft.desktopvirtualization/workspaces", - "crossComponentResources": [ - "{wvdworkspaces}" - ], - "visualization": "barchart", - "chartSettings": { - "showMetrics": false - } - }, - "customWidth": "50", - "name": "FeedRefreshesByUser" - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDConnections\r\n| where UserName == \"{UserName}\"\r\n| summarize LastSeen=max(TimeGenerated), Connections=dcount(CorrelationId) by ClientType\r\n| project ClientType=ClientType, Subtitle=\"Connections\", LastSeen, Connections\r\n| extend ClientTypeShort=replace(\"com.microsoft.rdc.\", \"\", ClientType)\r\n| sort by Connections desc", - "size": 0, - "title": "Select a client", - "timeContextFromParameter": "TimeRange", - "exportFieldName": "ClientType", - "exportParameterName": "SelectedClientType", - "exportDefaultValue": "*", + "title": "Key usage numbers", "queryType": 0, "resourceType": "microsoft.desktopvirtualization/hostpools", "crossComponentResources": [ "{HostPools}" ], "visualization": "tiles", - "gridSettings": { - "sortBy": [ - { - "itemKey": "LastSeen", - "sortOrder": 2 - } - ] - }, - "sortBy": [ - { - "itemKey": "LastSeen", - "sortOrder": 2 - } - ], - "tileSettings": { - "titleContent": { - "columnMatch": "ClientTypeShort", - "formatter": 1 - }, - "subtitleContent": { - "columnMatch": "Subtitle" - }, - "leftContent": { - "columnMatch": "Connections", - "formatter": 12, - "formatOptions": { - "min": -1, - "max": 0, - "palette": "blue" - } - }, - "rightContent": { - "columnMatch": "FeedRefreshes", - "formatter": 12, - "formatOptions": { - "min": -1, - "max": 0, - "palette": "orange" - }, - "numberFormat": { - "unit": 0, - "options": { - "style": "decimal", - "maximumSignificantDigits": 2 - } - } - }, - "secondaryContent": { - "columnMatch": "LastSeen", - "formatter": 6, - "dateFormat": { - "formatName": "shortDateTimePattern" - }, - "tooltipFormat": { - "tooltip": "Last seen time" - } - }, - "showBorder": true - } - }, - "customWidth": "16", - "name": "ClientTiles", - "styleSettings": { - "maxWidth": "250px" - } - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDConnections\r\n| where UserName == '{UserName}' and (\"{SelectedClientType}\" == \"*\" or ClientType == \"{SelectedClientType}\")\r\n| as SourceData\r\n| where State==\"Started\"\r\n| project-rename StartTime=TimeGenerated\r\n| join kind=fullouter\r\n(\r\n SourceData\r\n | where State == \"Completed\"\r\n | project CorrelationId, ClientVersion, ClientType, EndTime=TimeGenerated\r\n) on CorrelationId\r\n| project CorrelationId=coalesce(CorrelationId, CorrelationId1), StartTime=coalesce(StartTime, {TimeRange:start}), EndTime=coalesce(EndTime, {TimeRange:end}), ClientType=coalesce(ClientType, ClientType1), ClientVersion=coalesce(ClientVersion, ClientVersion1)\r\n| extend ClientTypeShort=replace(\"com.microsoft.rdc.\", \"\", ClientType), ProbeTime = range(bin_at(StartTime, {TimeRange:grain}, {TimeRange:start}), bin_at(EndTime + {TimeRange:grain}, {TimeRange:grain}, {TimeRange:end}), {TimeRange:grain})\r\n| mv-expand ProbeTime to typeof(datetime)\r\n| extend ConnectionContainsProbe = (StartTime < ProbeTime and EndTime > ProbeTime),\r\n ConnectionEndedInPreviousBucket = (StartTime < ProbeTime and EndTime + {TimeRange:grain} >= ProbeTime)\r\n| make-series Connections=dcountif(CorrelationId, ConnectionContainsProbe or ConnectionEndedInPreviousBucket) on ProbeTime from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by Client=strcat(ClientTypeShort, \" v\", ClientVersion)\r\n\r\n", - "size": 0, - "title": "Connections by client and version", - "noDataMessage": "No connections found for the configured filters.", - "timeContextFromParameter": "TimeRange", - "showExportToExcel": true, - "queryType": 0, - "resourceType": "microsoft.desktopvirtualization/hostpools", - "crossComponentResources": [ - "{HostPools}" - ], - "visualization": "areachart", - "sortBy": [], - "chartSettings": { - "showMetrics": false, - "showLegend": true - } - }, - "customWidth": "42", - "name": "ConnectionsByClientAndVersion", - "styleSettings": { - "margin": "6px" - } - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDFeeds\r\n| where UserName == \"{UserName}\" and (\"{SelectedClientType}\" == \"*\" or ClientType == \"{SelectedClientType}\")\r\n| extend ClientTypeShort=replace(\"com.microsoft.rdc.\", \"\", ClientType)\r\n| make-series FeedRefreshes=dcount(CorrelationId) on TimeGenerated from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by strcat(ClientTypeShort, \" v\", ClientVersion)\r\n", - "size": 0, - "title": "Feed refreshes by client and version", - "timeContextFromParameter": "TimeRange", - "showExportToExcel": true, - "queryType": 0, - "resourceType": "microsoft.desktopvirtualization/workspaces", - "crossComponentResources": [ - "{wvdworkspaces}" - ], - "visualization": "areachart", "gridSettings": { "formatters": [ { - "columnMatch": "ClientType", - "formatter": 5 - }, - { - "columnMatch": "ClientVersion", - "formatter": 5 - }, - { - "columnMatch": "LastSeen", - "formatter": 6, - "formatOptions": { - "aggregation": "Max", - "customColumnWidthSetting": "30ch" - } - }, - { - "columnMatch": "FeedRefreshes", - "formatter": 4, - "formatOptions": { - "min": 0, - "palette": "gray", - "aggregation": "Sum", - "customColumnWidthSetting": "128px" + "columnMatch": "Days", + "formatter": 0, + "numberFormat": { + "unit": 27, + "options": { + "style": "decimal", + "useGrouping": false + } } }, { - "columnMatch": "Connections", - "formatter": 4, - "formatOptions": { - "min": 0, - "palette": "blue", - "aggregation": "Sum", - "customColumnWidthSetting": "115px" + "columnMatch": "ConnectionTime", + "formatter": 0, + "numberFormat": { + "unit": 24, + "options": { + "style": "decimal", + "useGrouping": false, + "maximumSignificantDigits": 2 + } } } - ], - "hierarchySettings": { - "treeType": 1, - "groupBy": [ - "ClientType" - ], - "expandTopLevel": true, - "finalBy": "ClientVersion" - } + ] }, - "sortBy": [] - }, - "customWidth": "42", - "name": "FeedRefreshesByClientAndVersion", - "styleSettings": { - "margin": "6px" - } - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDConnections\r\n| where UserName == '{UserName}'\r\n| as Connections\r\n| where State == \"Connected\" and TimeGenerated <= {TimeRange:end}\r\n| join kind=inner\r\n(\r\n WVDCheckpoints\r\n | where (Name == \"LaunchExecutable\" and Parameters.connectionStage == \"RdpShellAppExecuted\") or Name==\"RdpShellAppExecuted\"\r\n | project CorrelationId, Parameters=tostring(Parameters.filename)\r\n | extend Parameters=trim('\"', Parameters) // remove quotes for paths with spaces in them\r\n | extend Parameters=replace(@\"^(.*[\\\\\\/])\", \"\", tolower(Parameters)) // remove binary path to only keep file name\r\n | extend Parameters=replace(@\"microsoft.windows(.+)_8wekyb3d8bbwe!app\", @\"\\1\", Parameters) //inbox apps to readable format\r\n) on CorrelationId\r\n| summarize TotalLaunches=dcount(CorrelationId), DaysLaunched=dcount(bin(TimeGenerated, 1d)), Clients=dcount(ClientType) by Parameters\r\n| extend Subtitle=\"Launches / Days with usage\", Clients=strcat(\"Used on \", Clients, iif(Clients > 1, \" different \", \" \"), \"client\", iif(Clients > 1, \"s\", \"\"))\r\n| sort by TotalLaunches desc", - "size": 1, - "title": "Remote apps used", - "queryType": 0, - "resourceType": "microsoft.desktopvirtualization/hostpools", - "crossComponentResources": [ - "{HostPools}" - ], - "visualization": "tiles", "tileSettings": { "titleContent": { - "columnMatch": "Parameters", + "columnMatch": "Metric", "formatter": 1 }, - "subtitleContent": { - "columnMatch": "Clients" - }, "leftContent": { - "columnMatch": "TotalLaunches", - "formatter": 12, - "formatOptions": { - "palette": "auto" - }, - "numberFormat": { - "unit": 17, - "options": { - "style": "decimal", - "maximumFractionDigits": 2, - "maximumSignificantDigits": 3 - } - } - }, - "rightContent": { - "columnMatch": "DaysLaunched", - "formatter": 12, - "formatOptions": { - "palette": "auto" - } - }, - "secondaryContent": { - "columnMatch": "Subtitle", - "formatter": 1 - }, - "showBorder": false - } - }, - "customWidth": "50", - "name": "PopularResourceByUser" - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDConnections\r\n| where UserName == '{UserName}'\r\n| as Connections\r\n| where State == \"Started\" and TimeGenerated <= {TimeRange:end}\r\n| extend StartTime = TimeGenerated\r\n| join kind=leftouter\r\n(\r\n Connections\r\n | where UserName == \"{UserName}\" and State == \"Completed\"\r\n | extend EndTime = TimeGenerated\r\n)\r\non CorrelationId\r\n| extend EndTime = coalesce(EndTime, {TimeRange:end})\r\n| where EndTime >= {TimeRange:start}\r\n// limit duration hours to our timeframe even if connection spanned the edges...\r\n| extend Duration = min_of(EndTime, {TimeRange:end}) - max_of(StartTime, {TimeRange:start})\r\n| summarize Duration = sum(Duration) by Host=trim_end(@\"\\..*\", SessionHostName)\r\n| project Host, UsageHours = Duration / 1h\r\n", - "size": 1, - "title": "Connection time by host", - "queryType": 0, - "resourceType": "microsoft.desktopvirtualization/hostpools", - "crossComponentResources": [ - "{HostPools}" - ], - "visualization": "piechart", - "chartSettings": { - "yAxis": [ - "UsageHours" - ], - "ySettings": { - "numberFormatSettings": { - "unit": 26, - "options": { - "style": "decimal", - "useGrouping": true, - "maximumFractionDigits": 1 - } - } - } - } - }, - "customWidth": "50", - "name": "HostUsageByUser" - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "let GetDaysInTimeRange = (Period:timespan) {\r\n // Get the number of days in the period, note the period MUST be longer than 1 day, and in full days\r\n max_of(toint(round(Period/1d, 0)), 1);\r\n};\r\nlet GetTimespanAverage = (Time:timespan, Period:timespan) {\r\n // Get the number of days in the period, note the period MUST be longer than 1 day, and in full days\r\n let Days = GetDaysInTimeRange(Period);\r\n // Total hours is the number of days*24, plus the hours\r\n let TotalHours = toint(format_timespan(Time, \"d\")) * 24 + toint(format_timespan(Time, \"H\"));\r\n // Calculate the average hours\r\n let AvgHours = TotalHours / Days;\r\n // We're rounding down for the average, figure out fractional hours left \r\n let MinuteResidue = toint(((1.0*TotalHours/Days) - AvgHours) * 60);\r\n // Total minutes is the number of minutes plus the residue from above\r\n let TotalMinutes = toint(format_timespan(Time, \"m\")) + MinuteResidue;\r\n // Calculate average minutes\r\n let AvgMinutes = toint(round(todouble(TotalMinutes) / Days));\r\n // Pad with zero if necessary\r\n let MinuteStr = iif(AvgMinutes < 10, strcat(\"0\", AvgMinutes), tostring(AvgMinutes));\r\n let Result = strcat(\"~ \", AvgHours, \":\", MinuteStr, \" h\");\r\n iif(Time/1s > 0, Result, '');\r\n};\r\nlet ProbeRange = range ProbeDay from {TimeRange:start} to {TimeRange:end} step 1d | extend JoinKey=1;\r\nWVDConnections\r\n| where UserName == \"{UserName}\"\r\n| as UserConnections\r\n| where State == \"Started\" and TimeGenerated <= {TimeRange:end}\r\n| extend StartTime = TimeGenerated\r\n| join kind=rightouter\r\n(\r\n UserConnections\r\n | where State == \"Completed\"\r\n | extend EndTime = TimeGenerated\r\n) on CorrelationId\r\n| extend StartTime = min_of(coalesce(StartTime, {TimeRange:start}), EndTime), UserName=coalesce(UserName,UserName1) // If the start time has fallen out of retention, it should not be set to after the EndTime\r\n| where StartTime between ({TimeRange:start} .. {TimeRange:end}) or // Connections starting in our date range\r\n EndTime between ({TimeRange:start} .. {TimeRange:end}) or // Connections ending in our date range\r\n (StartTime <= {TimeRange:start} and EndTime >= {TimeRange:end}) // Connections that started before and ended after the range\r\n| extend EndTime=min_of(EndTime, {TimeRange:end})-1s, StartTime=max_of(StartTime,{TimeRange:start})+1s // Crop connections to within the report boundary\r\n| extend Duration = EndTime-StartTime\r\n| sort by ClientType asc // Sort for Clients string to be homogenous\r\n| as UserUsage\r\n| join kind=leftouter\r\n(\r\n WVDCheckpoints\r\n | where (Name == \"LaunchExecutable\" and Parameters.connectionStage == \"RdpShellAppExecuted\") or Name==\"RdpShellAppExecuted\"\r\n | project CorrelationId, Parameters=tostring(Parameters.filename)\r\n) on CorrelationId\r\n| summarize AppsToStartConnection=dcount(ResourceAlias),\r\n AppsUsed=dcount(Parameters),\r\n Connections=dcount(CorrelationId),\r\n Time=sum(Duration),\r\n ClientCount=dcount(ClientType)\r\n by UserName\r\n| join kind=leftouter \r\n(\r\n // Count days with usage\r\n UserUsage\r\n | extend JoinKey = 1\r\n | join kind=fullouter ProbeRange on JoinKey\r\n | make-series ConnectedToday=dcountif(CorrelationId, bin(ProbeDay, 1d) == bin(StartTime, 1d) or \r\n bin(ProbeDay, 1d) == bin(EndTime, 1d) or \r\n bin(ProbeDay, 1d) between (bin(StartTime, 1d)..bin(EndTime, 1d)))\r\n on ProbeDay from {TimeRange:start} to {TimeRange:end} step 1d by UserName\r\n | mv-expand ProbeDay to typeof(datetime), ConnectedToday to typeof(int)\r\n | summarize Days=countif(ConnectedToday > 0) by UserName\r\n) on UserName\r\n| project\r\n ['Days in time range']=GetDaysInTimeRange({TimeRange:end} - {TimeRange:start}),\r\n ['Days with usage']=coalesce(Days, 0),\r\n ['Apps to start connection']=coalesce(AppsToStartConnection, 0),\r\n ['Apps used']=coalesce(AppsUsed, 0),\r\n ['Connection count']=coalesce(Connections, 0),\r\n ['Connection time']=strcat(\"~ \", toint(format_timespan(Time, 'd'))*24+toint(format_timespan(Time, 'H')), \":\", format_timespan(Time, 'mm'), \" h\"),\r\n ['Average daily time']=GetTimespanAverage(Time,{TimeRange:end} - {TimeRange:start}),\r\n ['Client count']=coalesce(ClientCount, 0)\r\n| evaluate narrow()\r\n| project Metric=replace(@\"\\[|'|\\]\", \"\", Column), Value", - "size": 1, - "title": "Key usage numbers", - "queryType": 0, - "resourceType": "microsoft.desktopvirtualization/hostpools", - "crossComponentResources": [ - "{HostPools}" - ], - "visualization": "tiles", - "gridSettings": { - "formatters": [ - { - "columnMatch": "Days", - "formatter": 0, - "numberFormat": { - "unit": 27, - "options": { - "style": "decimal", - "useGrouping": false - } - } - }, - { - "columnMatch": "ConnectionTime", - "formatter": 0, - "numberFormat": { - "unit": 24, - "options": { - "style": "decimal", - "useGrouping": false, - "maximumSignificantDigits": 2 - } - } - } - ] - }, - "tileSettings": { - "titleContent": { - "columnMatch": "Metric", - "formatter": 1 - }, - "leftContent": { - "columnMatch": "Value", + "columnMatch": "Value", "formatter": 12, "formatOptions": { "palette": "auto" @@ -779,37 +466,6 @@ }, "name": "KeyUsageNumbersTiles" }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDConnections\r\n| where UserName == \"{UserName}\"\r\n| as ConnectionData\r\n| where State == \"Started\"\r\n| join kind = leftsemi\r\n(\r\n // Only include connections that actually reached the host to prevent short (failed) attempts from skewing the data\r\n WVDCheckpoints\r\n | where Source == \"RDStack\" and Name =~ \"RdpStackConnectionEstablished\"\r\n) on CorrelationId\r\n\r\n| join kind = inner\r\n(\r\n WVDCheckpoints\r\n | where Name =~ \"LoadBalancedNewConnection\"\r\n | extend LoadBalanceOutcome=Parameters.LoadBalanceOutcome\r\n | project CorrelationId, ConnectionBackground=case(LoadBalanceOutcome == \"NewSession\", \"New session\", LoadBalanceOutcome in (\"Active\", \"Disconnected\", \"Pending\"), \"Existing session\", LoadBalanceOutcome)\r\n) on CorrelationId\r\n| join kind = inner\r\n(\r\n WVDCheckpoints // new session\r\n | where Name =~ \"ShellReady\" or (Name =~ \"LaunchExecutable\" and Parameters.connectionStage == \"RdpShellAppExecuted\") or Name=~\"RdpShellAppExecuted\"\r\n | project TimeNewSessions=TimeGenerated, CorrelationId\r\n | summarize TimeNewSessions=min(TimeNewSessions) by CorrelationId\r\n\r\n) on CorrelationId\r\n| join kind = inner ( \r\n ConnectionData // existent Sessions\r\n | where State == \"Connected\"\r\n | project TimeConnected=TimeGenerated, CorrelationId\r\n) on CorrelationId\r\n| extend ProductiveTime = iff(ConnectionBackground==\"New session\",TimeNewSessions, TimeConnected)\r\n| join kind = leftouter\r\n( \r\n //OnCredentialsAcquisitionCompleted\t2021-09-13T16:48:50.4440000Z\tClient\t{\"CredentialType\":\"SavedPassword\",\"DurationMS\":\"9\",\"Success\":\"True\"}\r\n WVDCheckpoints\r\n | where Name =~ \"OnCredentialsAcquisitionCompleted\"\r\n | project CorrelationId, credaquire = Parameters.DurationMS \r\n) on CorrelationId\r\n| join kind = leftouter\r\n( \r\n WVDCheckpoints\r\n | where Name =~ \"SSOTokenRetrieval\"\r\n | project CorrelationId, ssotokeretrieval = Parameters.DurationMS\r\n) on CorrelationId\r\n| extend ProductiveTime = (ProductiveTime - TimeGenerated) / 1s - (coalesce(credaquire,0)/1000) - (coalesce(ssotokeretrieval,0)/1000)\r\n| make-series Median=percentile(ProductiveTime, 50) on TimeGenerated from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by ConnectionBackground\r\n\r\n", - "size": 0, - "aggregation": 5, - "title": "Time to connect", - "timeContextFromParameter": "TimeRange", - "showExportToExcel": true, - "queryType": 0, - "resourceType": "microsoft.desktopvirtualization/hostpools", - "crossComponentResources": [ - "{HostPools}" - ], - "visualization": "unstackedbar", - "chartSettings": { - "ySettings": { - "numberFormatSettings": { - "unit": 24, - "options": { - "style": "decimal", - "useGrouping": true, - "maximumSignificantDigits": 2 - } - } - } - } - }, - "name": "TimeToConnectGraph" - }, { "type": 12, "content": { @@ -1562,6 +1218,346 @@ "styleSettings": { "showBorder": true } + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDConnections\r\n| where UserName == \"{UserName}\"\r\n| summarize LastSeen=max(TimeGenerated), Connections=dcount(CorrelationId) by ClientType\r\n| project ClientType=ClientType, Subtitle=\"Connections\", LastSeen, Connections\r\n| extend ClientTypeShort=replace(\"com.microsoft.rdc.\", \"\", ClientType)\r\n| sort by Connections desc", + "size": 0, + "title": "Select a client", + "timeContextFromParameter": "TimeRange", + "exportFieldName": "ClientType", + "exportParameterName": "SelectedClientType", + "exportDefaultValue": "*", + "queryType": 0, + "resourceType": "microsoft.desktopvirtualization/hostpools", + "crossComponentResources": [ + "{HostPools}" + ], + "visualization": "tiles", + "gridSettings": { + "sortBy": [ + { + "itemKey": "LastSeen", + "sortOrder": 2 + } + ] + }, + "sortBy": [ + { + "itemKey": "LastSeen", + "sortOrder": 2 + } + ], + "tileSettings": { + "titleContent": { + "columnMatch": "ClientTypeShort", + "formatter": 1 + }, + "subtitleContent": { + "columnMatch": "Subtitle" + }, + "leftContent": { + "columnMatch": "Connections", + "formatter": 12, + "formatOptions": { + "min": -1, + "max": 0, + "palette": "blue" + } + }, + "rightContent": { + "columnMatch": "FeedRefreshes", + "formatter": 12, + "formatOptions": { + "min": -1, + "max": 0, + "palette": "orange" + }, + "numberFormat": { + "unit": 0, + "options": { + "style": "decimal", + "maximumSignificantDigits": 2 + } + } + }, + "secondaryContent": { + "columnMatch": "LastSeen", + "formatter": 6, + "dateFormat": { + "formatName": "shortDateTimePattern" + }, + "tooltipFormat": { + "tooltip": "Last seen time" + } + }, + "showBorder": true + } + }, + "customWidth": "16", + "name": "ClientTiles", + "styleSettings": { + "maxWidth": "250px" + } + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDConnections\r\n| where UserName == '{UserName}' and (\"{SelectedClientType}\" == \"*\" or ClientType == \"{SelectedClientType}\")\r\n| as SourceData\r\n| where State==\"Started\"\r\n| project-rename StartTime=TimeGenerated\r\n| join kind=fullouter\r\n(\r\n SourceData\r\n | where State == \"Completed\"\r\n | project CorrelationId, ClientVersion, ClientType, EndTime=TimeGenerated\r\n) on CorrelationId\r\n| project CorrelationId=coalesce(CorrelationId, CorrelationId1), StartTime=coalesce(StartTime, {TimeRange:start}), EndTime=coalesce(EndTime, {TimeRange:end}), ClientType=coalesce(ClientType, ClientType1), ClientVersion=coalesce(ClientVersion, ClientVersion1)\r\n| extend ClientTypeShort=replace(\"com.microsoft.rdc.\", \"\", ClientType), ProbeTime = range(bin_at(StartTime, {TimeRange:grain}, {TimeRange:start}), bin_at(EndTime + {TimeRange:grain}, {TimeRange:grain}, {TimeRange:end}), {TimeRange:grain})\r\n| mv-expand ProbeTime to typeof(datetime)\r\n| extend ConnectionContainsProbe = (StartTime < ProbeTime and EndTime > ProbeTime),\r\n ConnectionEndedInPreviousBucket = (StartTime < ProbeTime and EndTime + {TimeRange:grain} >= ProbeTime)\r\n| make-series Connections=dcountif(CorrelationId, ConnectionContainsProbe or ConnectionEndedInPreviousBucket) on ProbeTime from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by Client=strcat(ClientTypeShort, \" v\", ClientVersion)\r\n\r\n", + "size": 0, + "title": "Connections by client and version", + "noDataMessage": "No connections found for the configured filters.", + "timeContextFromParameter": "TimeRange", + "showExportToExcel": true, + "queryType": 0, + "resourceType": "microsoft.desktopvirtualization/hostpools", + "crossComponentResources": [ + "{HostPools}" + ], + "visualization": "areachart", + "sortBy": [], + "chartSettings": { + "showMetrics": false, + "showLegend": true + } + }, + "customWidth": "42", + "name": "ConnectionsByClientAndVersion", + "styleSettings": { + "margin": "6px" + } + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDConnections\r\n| where UserName == '{UserName}'\r\n| as SourceData\r\n| where State==\"Started\"\r\n| project-rename StartTime=TimeGenerated\r\n| join kind=fullouter\r\n(\r\n SourceData\r\n | where State == \"Completed\"\r\n | project CorrelationId, SessionHostName, EndTime=TimeGenerated, _ResourceId\r\n) on CorrelationId\r\n| extend HostPool=extract_all(\"(?i:resourceGroups/([^/]+)/providers/microsoft.desktopvirtualization/hostpools/([^/]+))\",coalesce(_ResourceId, _ResourceId1))[0]\r\n| project CorrelationId=coalesce(CorrelationId, CorrelationId1), StartTime=coalesce(StartTime, {TimeRange:start}), EndTime=coalesce(EndTime, {TimeRange:end}), HostPool=strcat(HostPool[0],\"-\",HostPool[1])\r\n| extend ProbeTime = range(bin_at(StartTime, {TimeRange:grain}, {TimeRange:start}), bin_at(EndTime + {TimeRange:grain}, {TimeRange:grain}, {TimeRange:end}), {TimeRange:grain})\r\n| mv-expand ProbeTime to typeof(datetime)\r\n| extend ConnectionContainsProbe = (StartTime < ProbeTime and EndTime > ProbeTime),\r\n ConnectionEndedInPreviousBucket = (StartTime < ProbeTime and EndTime + {TimeRange:grain} >= ProbeTime) \r\n| make-series Connections=dcountif(CorrelationId, ConnectionContainsProbe or ConnectionEndedInPreviousBucket) on ProbeTime from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by HostPool", + "size": 0, + "title": "Connections over time for {UserName}", + "noDataMessage": "No connections detected for this user in the specified time frame.", + "showExportToExcel": true, + "queryType": 0, + "resourceType": "microsoft.desktopvirtualization/hostpools", + "crossComponentResources": [ + "{HostPools}" + ], + "visualization": "areachart", + "chartSettings": { + "showMetrics": false, + "showLegend": true + } + }, + "customWidth": "42", + "name": "ConnectionsbyUser" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "//aligning to this: https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/rds-rdsh-performance-counters\r\nWVDConnectionNetworkData\r\n| join kind=inner (WVDConnections\r\n | where UserName == \"{UserName}\"\r\n | project UserName, GatewayRegion, CorrelationId\r\n | where GatewayRegion != \"<>\"\r\n) on CorrelationId\r\n| project-away CorrelationId1\r\n| make-series Median=percentile(EstRoundTripTimeInMs, 50) default=0, p95=percentile(EstRoundTripTimeInMs, 95) default=0 on TimeGenerated from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain}", + "size": 0, + "title": "RTT median and 95th percentile", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.desktopvirtualization/hostpools", + "crossComponentResources": [ + "{HostPools}" + ], + "visualization": "timechart" + }, + "customWidth": "50", + "name": "User Rtt Time Chart" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDConnections\r\n| where UserName == \"{UserName}\"\r\n| as ConnectionData\r\n| where State == \"Started\"\r\n| join kind = leftsemi\r\n(\r\n // Only include connections that actually reached the host to prevent short (failed) attempts from skewing the data\r\n WVDCheckpoints\r\n | where Source == \"RDStack\" and Name =~ \"RdpStackConnectionEstablished\"\r\n) on CorrelationId\r\n\r\n| join kind = inner\r\n(\r\n WVDCheckpoints\r\n | where Name =~ \"LoadBalancedNewConnection\"\r\n | extend LoadBalanceOutcome=Parameters.LoadBalanceOutcome\r\n | project CorrelationId, ConnectionBackground=case(LoadBalanceOutcome == \"NewSession\", \"New session\", LoadBalanceOutcome in (\"Active\", \"Disconnected\", \"Pending\"), \"Existing session\", LoadBalanceOutcome)\r\n) on CorrelationId\r\n| join kind = inner\r\n(\r\n WVDCheckpoints // new session\r\n | where Name =~ \"ShellReady\" or (Name =~ \"LaunchExecutable\" and Parameters.connectionStage == \"RdpShellAppExecuted\") or Name=~\"RdpShellAppExecuted\"\r\n | project TimeNewSessions=TimeGenerated, CorrelationId\r\n | summarize TimeNewSessions=min(TimeNewSessions) by CorrelationId\r\n\r\n) on CorrelationId\r\n| join kind = inner ( \r\n ConnectionData // existent Sessions\r\n | where State == \"Connected\"\r\n | project TimeConnected=TimeGenerated, CorrelationId\r\n) on CorrelationId\r\n| extend ProductiveTime = iff(ConnectionBackground==\"New session\",TimeNewSessions, TimeConnected)\r\n| join kind = leftouter\r\n( \r\n //OnCredentialsAcquisitionCompleted\t2021-09-13T16:48:50.4440000Z\tClient\t{\"CredentialType\":\"SavedPassword\",\"DurationMS\":\"9\",\"Success\":\"True\"}\r\n WVDCheckpoints\r\n | where Name =~ \"OnCredentialsAcquisitionCompleted\"\r\n | project CorrelationId, credaquire = Parameters.DurationMS \r\n) on CorrelationId\r\n| join kind = leftouter\r\n( \r\n WVDCheckpoints\r\n | where Name =~ \"SSOTokenRetrieval\"\r\n | project CorrelationId, ssotokeretrieval = Parameters.DurationMS\r\n) on CorrelationId\r\n| extend ProductiveTime = (ProductiveTime - TimeGenerated) / 1s - (coalesce(credaquire,0)/1000) - (coalesce(ssotokeretrieval,0)/1000)\r\n| make-series Median=percentile(ProductiveTime, 50) on TimeGenerated from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by ConnectionBackground\r\n\r\n", + "size": 0, + "aggregation": 5, + "title": "Time to connect", + "timeContextFromParameter": "TimeRange", + "showExportToExcel": true, + "queryType": 0, + "resourceType": "microsoft.desktopvirtualization/hostpools", + "crossComponentResources": [ + "{HostPools}" + ], + "visualization": "unstackedbar", + "chartSettings": { + "ySettings": { + "numberFormatSettings": { + "unit": 24, + "options": { + "style": "decimal", + "useGrouping": true, + "maximumSignificantDigits": 2 + } + } + } + } + }, + "customWidth": "50", + "name": "TimeToConnectGraph" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDConnections\r\n| where UserName == '{UserName}'\r\n| as Connections\r\n| where State == \"Connected\" and TimeGenerated <= {TimeRange:end}\r\n| join kind=inner\r\n(\r\n WVDCheckpoints\r\n | where (Name == \"LaunchExecutable\" and Parameters.connectionStage == \"RdpShellAppExecuted\") or Name==\"RdpShellAppExecuted\"\r\n | project CorrelationId, Parameters=tostring(Parameters.filename)\r\n | extend Parameters=trim('\"', Parameters) // remove quotes for paths with spaces in them\r\n | extend Parameters=replace(@\"^(.*[\\\\\\/])\", \"\", tolower(Parameters)) // remove binary path to only keep file name\r\n | extend Parameters=replace(@\"microsoft.windows(.+)_8wekyb3d8bbwe!app\", @\"\\1\", Parameters) //inbox apps to readable format\r\n) on CorrelationId\r\n| summarize TotalLaunches=dcount(CorrelationId), DaysLaunched=dcount(bin(TimeGenerated, 1d)), Clients=dcount(ClientType) by Parameters\r\n| extend Subtitle=\"Launches / Days with usage\", Clients=strcat(\"Used on \", Clients, iif(Clients > 1, \" different \", \" \"), \"client\", iif(Clients > 1, \"s\", \"\"))\r\n| sort by TotalLaunches desc", + "size": 1, + "title": "Remote apps used", + "queryType": 0, + "resourceType": "microsoft.desktopvirtualization/hostpools", + "crossComponentResources": [ + "{HostPools}" + ], + "visualization": "tiles", + "tileSettings": { + "titleContent": { + "columnMatch": "Parameters", + "formatter": 1 + }, + "subtitleContent": { + "columnMatch": "Clients" + }, + "leftContent": { + "columnMatch": "TotalLaunches", + "formatter": 12, + "formatOptions": { + "palette": "auto" + }, + "numberFormat": { + "unit": 17, + "options": { + "style": "decimal", + "maximumFractionDigits": 2, + "maximumSignificantDigits": 3 + } + } + }, + "rightContent": { + "columnMatch": "DaysLaunched", + "formatter": 12, + "formatOptions": { + "palette": "auto" + } + }, + "secondaryContent": { + "columnMatch": "Subtitle", + "formatter": 1 + }, + "showBorder": false + } + }, + "customWidth": "50", + "name": "PopularResourceByUser" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDConnections\r\n| where UserName == '{UserName}'\r\n| as Connections\r\n| where State == \"Started\" and TimeGenerated <= {TimeRange:end}\r\n| extend StartTime = TimeGenerated\r\n| join kind=leftouter\r\n(\r\n Connections\r\n | where UserName == \"{UserName}\" and State == \"Completed\"\r\n | extend EndTime = TimeGenerated\r\n)\r\non CorrelationId\r\n| extend EndTime = coalesce(EndTime, {TimeRange:end})\r\n| where EndTime >= {TimeRange:start}\r\n// limit duration hours to our timeframe even if connection spanned the edges...\r\n| extend Duration = min_of(EndTime, {TimeRange:end}) - max_of(StartTime, {TimeRange:start})\r\n| summarize Duration = sum(Duration) by Host=trim_end(@\"\\..*\", SessionHostName)\r\n| project Host, UsageHours = Duration / 1h\r\n", + "size": 1, + "title": "Connection time by host", + "queryType": 0, + "resourceType": "microsoft.desktopvirtualization/hostpools", + "crossComponentResources": [ + "{HostPools}" + ], + "visualization": "piechart", + "chartSettings": { + "yAxis": [ + "UsageHours" + ], + "ySettings": { + "numberFormatSettings": { + "unit": 26, + "options": { + "style": "decimal", + "useGrouping": true, + "maximumFractionDigits": 1 + } + } + } + } + }, + "customWidth": "50", + "name": "HostUsageByUser" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDFeeds\r\n| where UserName == \"{UserName}\" and (\"{SelectedClientType}\" == \"*\" or ClientType == \"{SelectedClientType}\")\r\n| extend ClientTypeShort=replace(\"com.microsoft.rdc.\", \"\", ClientType)\r\n| make-series FeedRefreshes=dcount(CorrelationId) on TimeGenerated from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by strcat(ClientTypeShort, \" v\", ClientVersion)\r\n", + "size": 0, + "title": "Feed refreshes by client and version", + "timeContextFromParameter": "TimeRange", + "showExportToExcel": true, + "queryType": 0, + "resourceType": "microsoft.desktopvirtualization/workspaces", + "crossComponentResources": [ + "{wvdworkspaces}" + ], + "visualization": "areachart", + "gridSettings": { + "formatters": [ + { + "columnMatch": "ClientType", + "formatter": 5 + }, + { + "columnMatch": "ClientVersion", + "formatter": 5 + }, + { + "columnMatch": "LastSeen", + "formatter": 6, + "formatOptions": { + "aggregation": "Max", + "customColumnWidthSetting": "30ch" + } + }, + { + "columnMatch": "FeedRefreshes", + "formatter": 4, + "formatOptions": { + "min": 0, + "palette": "gray", + "aggregation": "Sum", + "customColumnWidthSetting": "128px" + } + }, + { + "columnMatch": "Connections", + "formatter": 4, + "formatOptions": { + "min": 0, + "palette": "blue", + "aggregation": "Sum", + "customColumnWidthSetting": "115px" + } + } + ], + "hierarchySettings": { + "treeType": 1, + "groupBy": [ + "ClientType" + ], + "expandTopLevel": true, + "finalBy": "ClientVersion" + } + }, + "sortBy": [] + }, + "customWidth": "100", + "name": "FeedRefreshesByClientAndVersion", + "styleSettings": { + "margin": "6px" + } } ] }, @@ -1580,4 +1576,4 @@ "Azure Monitor" ], "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json" -} +} \ No newline at end of file diff --git a/Workbooks/Windows Virtual Desktop/User Report/User report.workbook b/Workbooks/Windows Virtual Desktop/User Report/User report.workbook index 6da8b9c880..eb4ea33545 100644 --- a/Workbooks/Windows Virtual Desktop/User Report/User report.workbook +++ b/Workbooks/Windows Virtual Desktop/User Report/User report.workbook @@ -400,374 +400,49 @@ "type": 3, "content": { "version": "KqlItem/1.0", - "query": "WVDConnections\r\n| where UserName == '{UserName}'\r\n| where _ResourceId =~ '{HostPool}'\r\n| as SourceData\r\n| where State==\"Started\"\r\n| project-rename StartTime=TimeGenerated\r\n| join kind=fullouter\r\n(\r\n SourceData\r\n | where State == \"Completed\"\r\n | project CorrelationId, SessionHostName, EndTime=TimeGenerated\r\n) on CorrelationId\r\n| project CorrelationId=coalesce(CorrelationId, CorrelationId1), StartTime=coalesce(StartTime, {TimeRange:start}), EndTime=coalesce(EndTime, {TimeRange:end}), SessionHostName=coalesce(SessionHostName, SessionHostName1)\r\n| extend ProbeTime = range(bin_at(StartTime, {TimeRange:grain}, {TimeRange:start}), bin_at(EndTime + {TimeRange:grain}, {TimeRange:grain}, {TimeRange:end}), {TimeRange:grain})\r\n| mv-expand ProbeTime to typeof(datetime)\r\n| extend ConnectionContainsProbe = (StartTime < ProbeTime and EndTime > ProbeTime),\r\n ConnectionEndedInPreviousBucket = (StartTime < ProbeTime and EndTime + {TimeRange:grain} >= ProbeTime)\r\n| make-series Connections=dcountif(CorrelationId, ConnectionContainsProbe or ConnectionEndedInPreviousBucket) on ProbeTime from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by Host=trim_end(@\"\\..*\", SessionHostName)", - "size": 1, - "title": "Connections over time for {UserName}", - "noDataMessage": "No connections detected for this user in the specified time frame.", - "showExportToExcel": true, - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "crossComponentResources": [ - "{poolla}" - ], - "visualization": "areachart", - "chartSettings": { - "showMetrics": false, - "showLegend": true - } - }, - "customWidth": "50", - "name": "ConnectionsbyUser" - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDFeeds\r\n| where UserName == '{UserName}' and _ResourceId =~ '{wvdworkspace}'\r\n| make-series FeedCount = dcount(CorrelationId) default=long(0) on TimeGenerated from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain}", + "query": "let GetDaysInTimeRange = (Period:timespan) {\r\n // Get the number of days in the period, note the period MUST be longer than 1 day, and in full days\r\n max_of(toint(round(Period/1d, 0)), 1);\r\n};\r\nlet GetTimespanAverage = (Time:timespan, Period:timespan) {\r\n // Get the number of days in the period, note the period MUST be longer than 1 day, and in full days\r\n let Days = GetDaysInTimeRange(Period);\r\n // Total hours is the number of days*24, plus the hours\r\n let TotalHours = toint(format_timespan(Time, \"d\")) * 24 + toint(format_timespan(Time, \"H\"));\r\n // Calculate the average hours\r\n let AvgHours = TotalHours / Days;\r\n // We're rounding down for the average, figure out fractional hours left \r\n let MinuteResidue = toint(((1.0*TotalHours/Days) - AvgHours) * 60);\r\n // Total minutes is the number of minutes plus the residue from above\r\n let TotalMinutes = toint(format_timespan(Time, \"m\")) + MinuteResidue;\r\n // Calculate average minutes\r\n let AvgMinutes = toint(round(todouble(TotalMinutes) / Days));\r\n // Pad with zero if necessary\r\n let MinuteStr = iif(AvgMinutes < 10, strcat(\"0\", AvgMinutes), tostring(AvgMinutes));\r\n let Result = strcat(\"~ \", AvgHours, \":\", MinuteStr, \" h\");\r\n iif(Time/1s > 0, Result, '');\r\n};\r\nlet ProbeRange = range ProbeDay from {TimeRange:start} to {TimeRange:end} step 1d | extend JoinKey=1;\r\nWVDConnections\r\n| where UserName == \"{UserName}\" and _ResourceId =~ '{HostPool}'\r\n| as UserConnections\r\n| where State == \"Started\" and TimeGenerated <= {TimeRange:end}\r\n| extend StartTime = TimeGenerated\r\n| join kind=rightouter\r\n(\r\n UserConnections\r\n | where State == \"Completed\"\r\n | extend EndTime = TimeGenerated\r\n) on CorrelationId\r\n| extend StartTime = min_of(coalesce(StartTime, {TimeRange:start}), EndTime), UserName=coalesce(UserName,UserName1) // If the start time has fallen out of retention, it should not be set to after the EndTime\r\n| where StartTime between ({TimeRange:start} .. {TimeRange:end}) or // Connections starting in our date range\r\n EndTime between ({TimeRange:start} .. {TimeRange:end}) or // Connections ending in our date range\r\n (StartTime <= {TimeRange:start} and EndTime >= {TimeRange:end}) // Connections that started before and ended after the range\r\n| extend EndTime=min_of(EndTime, {TimeRange:end})-1s, StartTime=max_of(StartTime,{TimeRange:start})+1s // Crop connections to within the report boundary\r\n| extend Duration = EndTime-StartTime\r\n| sort by ClientType asc // Sort for Clients string to be homogenous\r\n| as UserUsage\r\n| join kind=leftouter\r\n(\r\n WVDCheckpoints\r\n | where (Name == \"LaunchExecutable\" and Parameters.connectionStage == \"RdpShellAppExecuted\") or Name==\"RdpShellAppExecuted\"\r\n | project CorrelationId, Parameters=tostring(Parameters.filename)\r\n) on CorrelationId\r\n| summarize AppsToStartConnection=dcount(ResourceAlias),\r\n AppsUsed=dcount(Parameters),\r\n Connections=dcount(CorrelationId),\r\n Time=sum(Duration),\r\n ClientCount=dcount(ClientType)\r\n by UserName\r\n| join kind=leftouter \r\n(\r\n // Count days with usage\r\n UserUsage\r\n | extend JoinKey = 1\r\n | join kind=fullouter ProbeRange on JoinKey\r\n | make-series ConnectedToday=dcountif(CorrelationId, bin(ProbeDay, 1d) == bin(StartTime, 1d) or \r\n bin(ProbeDay, 1d) == bin(EndTime, 1d) or \r\n bin(ProbeDay, 1d) between (bin(StartTime, 1d)..bin(EndTime, 1d)))\r\n on ProbeDay from {TimeRange:start} to {TimeRange:end} step 1d by UserName\r\n | mv-expand ProbeDay to typeof(datetime), ConnectedToday to typeof(int)\r\n | summarize Days=countif(ConnectedToday > 0) by UserName\r\n) on UserName\r\n| project\r\n ['Days in time range']=GetDaysInTimeRange({TimeRange:end} - {TimeRange:start}),\r\n ['Days with usage']=coalesce(Days, 0),\r\n ['Apps to start connection']=coalesce(AppsToStartConnection, 0),\r\n ['Apps used']=coalesce(AppsUsed, 0),\r\n ['Connection count']=coalesce(Connections, 0),\r\n ['Connection time']=strcat(\"~ \", toint(format_timespan(Time, 'd'))*24+toint(format_timespan(Time, 'H')), \":\", format_timespan(Time, 'mm'), \" h\"),\r\n ['Average daily time']=GetTimespanAverage(Time,{TimeRange:end} - {TimeRange:start}),\r\n ['Client count']=coalesce(ClientCount, 0)\r\n| evaluate narrow()\r\n| project Metric=replace(@\"\\[|'|\\]\", \"\", Column), Value", "size": 1, - "title": "Feed refreshes over time for {UserName}", - "noDataMessage": "No feed refreshes for this user in the specified time frame.", - "timeContext": { - "durationMs": 0 - }, - "timeContextFromParameter": "TimeRange", - "showExportToExcel": true, - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "crossComponentResources": [ - "{poolla}" - ], - "visualization": "barchart", - "chartSettings": { - "showMetrics": false - } - }, - "customWidth": "50", - "name": "FeedRefreshesByUser" - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDFeeds\r\n| where UserName == \"{UserName}\" and _ResourceId =~ '{wvdworkspace}'\r\n| summarize LastSeen=max(TimeGenerated), FeedRefreshes=dcount(CorrelationId) by ClientType\r\n| join kind=fullouter\r\n(\r\n WVDConnections\r\n | where UserName == \"{UserName}\" and _ResourceId =~ '{HostPool}'\r\n | summarize LastSeen=max(TimeGenerated), Connections=dcount(CorrelationId) by ClientType\r\n) on ClientType\r\n| project ClientType=coalesce(ClientType, ClientType1), Subtitle=\"Connections / Feed refrehes\", LastSeen=max_of(LastSeen, LastSeen1), FeedRefreshes=coalesce(FeedRefreshes, 0), Connections=coalesce(Connections, 0)\r\n| extend ClientTypeShort=replace(\"com.microsoft.rdc.\", \"\", ClientType)\r\n| sort by Connections desc", - "size": 0, - "title": "Select a client", - "timeContext": { - "durationMs": 0 - }, - "timeContextFromParameter": "TimeRange", - "exportFieldName": "ClientType", - "exportParameterName": "SelectedClientType", - "exportDefaultValue": "*", + "title": "Key usage numbers", "queryType": 0, "resourceType": "microsoft.operationalinsights/workspaces", "crossComponentResources": [ "{poolla}" ], "visualization": "tiles", - "gridSettings": { - "sortBy": [ - { - "itemKey": "LastSeen", - "sortOrder": 2 - } - ] - }, - "sortBy": [ - { - "itemKey": "LastSeen", - "sortOrder": 2 - } - ], - "tileSettings": { - "titleContent": { - "columnMatch": "ClientTypeShort", - "formatter": 1 - }, - "subtitleContent": { - "columnMatch": "Subtitle" - }, - "leftContent": { - "columnMatch": "Connections", - "formatter": 12, - "formatOptions": { - "min": -1, - "max": 0, - "palette": "blue" - } - }, - "rightContent": { - "columnMatch": "FeedRefreshes", - "formatter": 12, - "formatOptions": { - "min": -1, - "max": 0, - "palette": "orange" - }, - "numberFormat": { - "unit": 0, - "options": { - "style": "decimal", - "maximumSignificantDigits": 2 - } - } - }, - "secondaryContent": { - "columnMatch": "LastSeen", - "formatter": 6, - "dateFormat": { - "formatName": "shortDateTimePattern" - }, - "tooltipFormat": { - "tooltip": "Last seen time" - } - }, - "showBorder": true - } - }, - "customWidth": "16", - "name": "ClientTiles", - "styleSettings": { - "maxWidth": "250px" - } - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDConnections\r\n| where _ResourceId =~ '{HostPool}' and UserName == '{UserName}' and (\"{SelectedClientType}\" == \"*\" or ClientType == \"{SelectedClientType}\")\r\n| as SourceData\r\n| where State==\"Started\"\r\n| project-rename StartTime=TimeGenerated\r\n| join kind=fullouter\r\n(\r\n SourceData\r\n | where State == \"Completed\"\r\n | project CorrelationId, ClientVersion, ClientType, EndTime=TimeGenerated\r\n) on CorrelationId\r\n| project CorrelationId=coalesce(CorrelationId, CorrelationId1), StartTime=coalesce(StartTime, {TimeRange:start}), EndTime=coalesce(EndTime, {TimeRange:end}), ClientType=coalesce(ClientType, ClientType1), ClientVersion=coalesce(ClientVersion, ClientVersion1)\r\n| extend ClientTypeShort=replace(\"com.microsoft.rdc.\", \"\", ClientType), ProbeTime = range(bin_at(StartTime, {TimeRange:grain}, {TimeRange:start}), bin_at(EndTime + {TimeRange:grain}, {TimeRange:grain}, {TimeRange:end}), {TimeRange:grain})\r\n| mv-expand ProbeTime to typeof(datetime)\r\n| extend ConnectionContainsProbe = (StartTime < ProbeTime and EndTime > ProbeTime),\r\n ConnectionEndedInPreviousBucket = (StartTime < ProbeTime and EndTime + {TimeRange:grain} >= ProbeTime)\r\n| make-series Connections=dcountif(CorrelationId, ConnectionContainsProbe or ConnectionEndedInPreviousBucket) on ProbeTime from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by Client=strcat(ClientTypeShort, \" v\", ClientVersion)\r\n\r\n", - "size": 0, - "title": "Connections by client and version", - "noDataMessage": "No connections found for the configured filters.", - "timeContext": { - "durationMs": 0 - }, - "timeContextFromParameter": "TimeRange", - "showExportToExcel": true, - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "crossComponentResources": [ - "{poolla}" - ], - "visualization": "areachart", - "sortBy": [], - "chartSettings": { - "showMetrics": false, - "showLegend": true - } - }, - "customWidth": "42", - "name": "ConnectionsByClientAndVersion", - "styleSettings": { - "margin": "6px" - } - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDFeeds\r\n| where UserName == \"{UserName}\" and _ResourceId =~ '{wvdworkspace}' and (\"{SelectedClientType}\" == \"*\" or ClientType == \"{SelectedClientType}\")\r\n| extend ClientTypeShort=replace(\"com.microsoft.rdc.\", \"\", ClientType)\r\n| make-series FeedRefreshes=dcount(CorrelationId) on TimeGenerated from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by strcat(ClientTypeShort, \" v\", ClientVersion)\r\n", - "size": 0, - "title": "Feed refreshes by client and version", - "timeContext": { - "durationMs": 0 - }, - "timeContextFromParameter": "TimeRange", - "showExportToExcel": true, - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "crossComponentResources": [ - "{poolla}" - ], - "visualization": "areachart", "gridSettings": { "formatters": [ { - "columnMatch": "ClientType", - "formatter": 5 - }, - { - "columnMatch": "ClientVersion", - "formatter": 5 - }, - { - "columnMatch": "LastSeen", - "formatter": 6, - "formatOptions": { - "aggregation": "Max", - "customColumnWidthSetting": "30ch" - } - }, - { - "columnMatch": "FeedRefreshes", - "formatter": 4, - "formatOptions": { - "min": 0, - "palette": "gray", - "aggregation": "Sum", - "customColumnWidthSetting": "128px" + "columnMatch": "Days", + "formatter": 0, + "numberFormat": { + "unit": 27, + "options": { + "style": "decimal", + "useGrouping": false + } } }, { - "columnMatch": "Connections", - "formatter": 4, - "formatOptions": { - "min": 0, - "palette": "blue", - "aggregation": "Sum", - "customColumnWidthSetting": "115px" + "columnMatch": "ConnectionTime", + "formatter": 0, + "numberFormat": { + "unit": 24, + "options": { + "style": "decimal", + "useGrouping": false, + "maximumSignificantDigits": 2 + } } } - ], - "hierarchySettings": { - "treeType": 1, - "groupBy": [ - "ClientType" - ], - "expandTopLevel": true, - "finalBy": "ClientVersion" - } + ] }, - "sortBy": [] - }, - "customWidth": "42", - "name": "FeedRefreshesByClientAndVersion", - "styleSettings": { - "margin": "6px" - } - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDConnections\r\n| where UserName == '{UserName}' and _ResourceId =~ '{HostPool}'\r\n| as Connections\r\n| where State == \"Connected\" and TimeGenerated <= {TimeRange:end}\r\n| join kind=inner\r\n(\r\n WVDCheckpoints\r\n | where (Name == \"LaunchExecutable\" and Parameters.connectionStage == \"RdpShellAppExecuted\") or Name==\"RdpShellAppExecuted\"\r\n | project CorrelationId, Parameters=tostring(Parameters.filename)\r\n | extend Parameters=trim('\"', Parameters) // remove quotes for paths with spaces in them\r\n | extend Parameters=replace(@\"^(.*[\\\\\\/])\", \"\", tolower(Parameters)) // remove binary path to only keep file name\r\n | extend Parameters=replace(@\"microsoft.windows(.+)_8wekyb3d8bbwe!app\", @\"\\1\", Parameters) //inbox apps to readable format\r\n) on CorrelationId\r\n| summarize TotalLaunches=dcount(CorrelationId), DaysLaunched=dcount(bin(TimeGenerated, 1d)), Clients=dcount(ClientType) by Parameters\r\n| extend Subtitle=\"Launches / Days with usage\", Clients=strcat(\"Used on \", Clients, iif(Clients > 1, \" different \", \" \"), \"client\", iif(Clients > 1, \"s\", \"\"))\r\n| sort by TotalLaunches desc", - "size": 1, - "title": "Remote apps used", - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "crossComponentResources": [ - "{poolla}" - ], - "visualization": "tiles", "tileSettings": { "titleContent": { - "columnMatch": "Parameters", + "columnMatch": "Metric", "formatter": 1 }, - "subtitleContent": { - "columnMatch": "Clients" - }, "leftContent": { - "columnMatch": "TotalLaunches", - "formatter": 12, - "formatOptions": { - "palette": "auto" - }, - "numberFormat": { - "unit": 17, - "options": { - "style": "decimal", - "maximumFractionDigits": 2, - "maximumSignificantDigits": 3 - } - } - }, - "rightContent": { - "columnMatch": "DaysLaunched", - "formatter": 12, - "formatOptions": { - "palette": "auto" - } - }, - "secondaryContent": { - "columnMatch": "Subtitle", - "formatter": 1 - }, - "showBorder": false - } - }, - "customWidth": "50", - "name": "PopularResourceByUser" - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDConnections\r\n| where UserName == '{UserName}' and _ResourceId =~ '{HostPool}'\r\n| as Connections\r\n| where State == \"Started\" and TimeGenerated <= {TimeRange:end}\r\n| extend StartTime = TimeGenerated\r\n| join kind=leftouter\r\n(\r\n Connections\r\n | where UserName == \"{UserName}\" and State == \"Completed\"\r\n | extend EndTime = TimeGenerated\r\n)\r\non CorrelationId\r\n| extend EndTime = coalesce(EndTime, {TimeRange:end})\r\n| where EndTime >= {TimeRange:start}\r\n// limit duration hours to our timeframe even if connection spanned the edges...\r\n| extend Duration = min_of(EndTime, {TimeRange:end}) - max_of(StartTime, {TimeRange:start})\r\n| summarize Duration = sum(Duration) by Host=trim_end(@\"\\..*\", SessionHostName)\r\n| project Host, UsageHours = Duration / 1h\r\n", - "size": 1, - "title": "Connection time by host", - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "crossComponentResources": [ - "{poolla}" - ], - "visualization": "piechart", - "chartSettings": { - "yAxis": [ - "UsageHours" - ], - "ySettings": { - "numberFormatSettings": { - "unit": 26, - "options": { - "style": "decimal", - "useGrouping": true, - "maximumFractionDigits": 1 - } - } - } - } - }, - "customWidth": "50", - "name": "HostUsageByUser" - }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "let GetDaysInTimeRange = (Period:timespan) {\r\n // Get the number of days in the period, note the period MUST be longer than 1 day, and in full days\r\n max_of(toint(round(Period/1d, 0)), 1);\r\n};\r\nlet GetTimespanAverage = (Time:timespan, Period:timespan) {\r\n // Get the number of days in the period, note the period MUST be longer than 1 day, and in full days\r\n let Days = GetDaysInTimeRange(Period);\r\n // Total hours is the number of days*24, plus the hours\r\n let TotalHours = toint(format_timespan(Time, \"d\")) * 24 + toint(format_timespan(Time, \"H\"));\r\n // Calculate the average hours\r\n let AvgHours = TotalHours / Days;\r\n // We're rounding down for the average, figure out fractional hours left \r\n let MinuteResidue = toint(((1.0*TotalHours/Days) - AvgHours) * 60);\r\n // Total minutes is the number of minutes plus the residue from above\r\n let TotalMinutes = toint(format_timespan(Time, \"m\")) + MinuteResidue;\r\n // Calculate average minutes\r\n let AvgMinutes = toint(round(todouble(TotalMinutes) / Days));\r\n // Pad with zero if necessary\r\n let MinuteStr = iif(AvgMinutes < 10, strcat(\"0\", AvgMinutes), tostring(AvgMinutes));\r\n let Result = strcat(\"~ \", AvgHours, \":\", MinuteStr, \" h\");\r\n iif(Time/1s > 0, Result, '');\r\n};\r\nlet ProbeRange = range ProbeDay from {TimeRange:start} to {TimeRange:end} step 1d | extend JoinKey=1;\r\nWVDConnections\r\n| where UserName == \"{UserName}\" and _ResourceId =~ '{HostPool}'\r\n| as UserConnections\r\n| where State == \"Started\" and TimeGenerated <= {TimeRange:end}\r\n| extend StartTime = TimeGenerated\r\n| join kind=rightouter\r\n(\r\n UserConnections\r\n | where State == \"Completed\"\r\n | extend EndTime = TimeGenerated\r\n) on CorrelationId\r\n| extend StartTime = min_of(coalesce(StartTime, {TimeRange:start}), EndTime), UserName=coalesce(UserName,UserName1) // If the start time has fallen out of retention, it should not be set to after the EndTime\r\n| where StartTime between ({TimeRange:start} .. {TimeRange:end}) or // Connections starting in our date range\r\n EndTime between ({TimeRange:start} .. {TimeRange:end}) or // Connections ending in our date range\r\n (StartTime <= {TimeRange:start} and EndTime >= {TimeRange:end}) // Connections that started before and ended after the range\r\n| extend EndTime=min_of(EndTime, {TimeRange:end})-1s, StartTime=max_of(StartTime,{TimeRange:start})+1s // Crop connections to within the report boundary\r\n| extend Duration = EndTime-StartTime\r\n| sort by ClientType asc // Sort for Clients string to be homogenous\r\n| as UserUsage\r\n| join kind=leftouter\r\n(\r\n WVDCheckpoints\r\n | where (Name == \"LaunchExecutable\" and Parameters.connectionStage == \"RdpShellAppExecuted\") or Name==\"RdpShellAppExecuted\"\r\n | project CorrelationId, Parameters=tostring(Parameters.filename)\r\n) on CorrelationId\r\n| summarize AppsToStartConnection=dcount(ResourceAlias),\r\n AppsUsed=dcount(Parameters),\r\n Connections=dcount(CorrelationId),\r\n Time=sum(Duration),\r\n ClientCount=dcount(ClientType)\r\n by UserName\r\n| join kind=leftouter \r\n(\r\n // Count days with usage\r\n UserUsage\r\n | extend JoinKey = 1\r\n | join kind=fullouter ProbeRange on JoinKey\r\n | make-series ConnectedToday=dcountif(CorrelationId, bin(ProbeDay, 1d) == bin(StartTime, 1d) or \r\n bin(ProbeDay, 1d) == bin(EndTime, 1d) or \r\n bin(ProbeDay, 1d) between (bin(StartTime, 1d)..bin(EndTime, 1d)))\r\n on ProbeDay from {TimeRange:start} to {TimeRange:end} step 1d by UserName\r\n | mv-expand ProbeDay to typeof(datetime), ConnectedToday to typeof(int)\r\n | summarize Days=countif(ConnectedToday > 0) by UserName\r\n) on UserName\r\n| project\r\n ['Days in time range']=GetDaysInTimeRange({TimeRange:end} - {TimeRange:start}),\r\n ['Days with usage']=coalesce(Days, 0),\r\n ['Apps to start connection']=coalesce(AppsToStartConnection, 0),\r\n ['Apps used']=coalesce(AppsUsed, 0),\r\n ['Connection count']=coalesce(Connections, 0),\r\n ['Connection time']=strcat(\"~ \", toint(format_timespan(Time, 'd'))*24+toint(format_timespan(Time, 'H')), \":\", format_timespan(Time, 'mm'), \" h\"),\r\n ['Average daily time']=GetTimespanAverage(Time,{TimeRange:end} - {TimeRange:start}),\r\n ['Client count']=coalesce(ClientCount, 0)\r\n| evaluate narrow()\r\n| project Metric=replace(@\"\\[|'|\\]\", \"\", Column), Value", - "size": 1, - "title": "Key usage numbers", - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "crossComponentResources": [ - "{poolla}" - ], - "visualization": "tiles", - "gridSettings": { - "formatters": [ - { - "columnMatch": "Days", - "formatter": 0, - "numberFormat": { - "unit": 27, - "options": { - "style": "decimal", - "useGrouping": false - } - } - }, - { - "columnMatch": "ConnectionTime", - "formatter": 0, - "numberFormat": { - "unit": 24, - "options": { - "style": "decimal", - "useGrouping": false, - "maximumSignificantDigits": 2 - } - } - } - ] - }, - "tileSettings": { - "titleContent": { - "columnMatch": "Metric", - "formatter": 1 - }, - "leftContent": { - "columnMatch": "Value", + "columnMatch": "Value", "formatter": 12, "formatOptions": { "palette": "auto" @@ -798,40 +473,6 @@ }, "name": "KeyUsageNumbersTiles" }, - { - "type": 3, - "content": { - "version": "KqlItem/1.0", - "query": "WVDConnections\r\n| where _ResourceId =~ \"{HostPool}\" and UserName == \"{UserName}\"\r\n| as ConnectionData\r\n| where State == \"Started\"\r\n| join kind = leftsemi\r\n(\r\n // Only include connections that actually reached the host to prevent short (failed) attempts from skewing the data\r\n WVDCheckpoints\r\n | where Source == \"RDStack\" and Name =~ \"RdpStackConnectionEstablished\" and _ResourceId =~ \"{HostPool}\"\r\n) on CorrelationId\r\n\r\n| join kind = inner\r\n(\r\n WVDCheckpoints\r\n | where _ResourceId =~ \"{HostPool}\"\r\n | where Name =~ \"LoadBalancedNewConnection\"\r\n | extend LoadBalanceOutcome=Parameters.LoadBalanceOutcome\r\n | project CorrelationId, ConnectionBackground=case(LoadBalanceOutcome == \"NewSession\", \"New session\", LoadBalanceOutcome in (\"Active\", \"Disconnected\", \"Pending\"), \"Existing session\", LoadBalanceOutcome)\r\n) on CorrelationId\r\n| join kind = inner\r\n(\r\n WVDCheckpoints // new session\r\n | where _ResourceId =~ \"{HostPool}\"\r\n | where Name =~ \"ShellReady\" or (Name =~ \"LaunchExecutable\" and Parameters.connectionStage == \"RdpShellAppExecuted\") or Name=~\"RdpShellAppExecuted\"\r\n | project TimeNewSessions=TimeGenerated, CorrelationId\r\n | summarize TimeNewSessions=min(TimeNewSessions) by CorrelationId\r\n\r\n) on CorrelationId\r\n| join kind = inner ( \r\n ConnectionData // existent Sessions\r\n | where State == \"Connected\"\r\n | project TimeConnected=TimeGenerated, CorrelationId\r\n) on CorrelationId\r\n| extend ProductiveTime = iff(ConnectionBackground==\"New session\",TimeNewSessions, TimeConnected)\r\n| join kind = leftouter\r\n( \r\n //OnCredentialsAcquisitionCompleted\t2021-09-13T16:48:50.4440000Z\tClient\t{\"CredentialType\":\"SavedPassword\",\"DurationMS\":\"9\",\"Success\":\"True\"}\r\n WVDCheckpoints\r\n | where _ResourceId =~ \"{HostPool}\"\r\n | where Name =~ \"OnCredentialsAcquisitionCompleted\"\r\n | project CorrelationId, credaquire = Parameters.DurationMS \r\n) on CorrelationId\r\n| join kind = leftouter\r\n( \r\n WVDCheckpoints\r\n | where _ResourceId =~ \"{HostPool}\"\r\n | where Name =~ \"SSOTokenRetrieval\"\r\n | project CorrelationId, ssotokeretrieval = Parameters.DurationMS\r\n) on CorrelationId\r\n| extend ProductiveTime = (ProductiveTime - TimeGenerated) / 1s - (coalesce(credaquire,0)/1000) - (coalesce(ssotokeretrieval,0)/1000)\r\n| make-series Median=percentile(ProductiveTime, 50) on TimeGenerated from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by ConnectionBackground\r\n\r\n", - "size": 0, - "aggregation": 5, - "title": "Time to connect", - "timeContext": { - "durationMs": 0 - }, - "timeContextFromParameter": "TimeRange", - "showExportToExcel": true, - "queryType": 0, - "resourceType": "microsoft.operationalinsights/workspaces", - "crossComponentResources": [ - "{poolla}" - ], - "visualization": "unstackedbar", - "chartSettings": { - "ySettings": { - "numberFormatSettings": { - "unit": 24, - "options": { - "style": "decimal", - "useGrouping": true, - "maximumSignificantDigits": 2 - } - } - } - } - }, - "name": "TimeToConnectGraph" - }, { "type": 12, "content": { @@ -996,9 +637,6 @@ "size": 2, "title": "Connecting to {CorrelationHost}", "noDataMessage": "No errors were found for the selected session.", - "timeContext": { - "durationMs": 0 - }, "timeContextFromParameter": "TimeRange", "showExportToExcel": true, "queryType": 0, @@ -1180,9 +818,6 @@ "aggregation": 3, "title": "Ranking of errors impacting {wvdActivityType} activities for {UserName} in {TimeRange:label}", "noDataMessage": "No errors reported in the selected time period", - "timeContext": { - "durationMs": 0 - }, "timeContextFromParameter": "TimeRange", "exportFieldName": "label", "exportParameterName": "wvderror", @@ -1353,6 +988,358 @@ "styleSettings": { "showBorder": true } + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDFeeds\r\n| where UserName == \"{UserName}\" and _ResourceId =~ '{wvdworkspace}'\r\n| summarize LastSeen=max(TimeGenerated), FeedRefreshes=dcount(CorrelationId) by ClientType\r\n| join kind=fullouter\r\n(\r\n WVDConnections\r\n | where UserName == \"{UserName}\" and _ResourceId =~ '{HostPool}'\r\n | summarize LastSeen=max(TimeGenerated), Connections=dcount(CorrelationId) by ClientType\r\n) on ClientType\r\n| project ClientType=coalesce(ClientType, ClientType1), Subtitle=\"Connections / Feed refrehes\", LastSeen=max_of(LastSeen, LastSeen1), FeedRefreshes=coalesce(FeedRefreshes, 0), Connections=coalesce(Connections, 0)\r\n| extend ClientTypeShort=replace(\"com.microsoft.rdc.\", \"\", ClientType)\r\n| sort by Connections desc", + "size": 0, + "title": "Select a client", + "timeContextFromParameter": "TimeRange", + "exportFieldName": "ClientType", + "exportParameterName": "SelectedClientType", + "exportDefaultValue": "*", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "crossComponentResources": [ + "{poolla}" + ], + "visualization": "tiles", + "gridSettings": { + "sortBy": [ + { + "itemKey": "LastSeen", + "sortOrder": 2 + } + ] + }, + "sortBy": [ + { + "itemKey": "LastSeen", + "sortOrder": 2 + } + ], + "tileSettings": { + "titleContent": { + "columnMatch": "ClientTypeShort", + "formatter": 1 + }, + "subtitleContent": { + "columnMatch": "Subtitle" + }, + "leftContent": { + "columnMatch": "Connections", + "formatter": 12, + "formatOptions": { + "min": -1, + "max": 0, + "palette": "blue" + } + }, + "rightContent": { + "columnMatch": "FeedRefreshes", + "formatter": 12, + "formatOptions": { + "min": -1, + "max": 0, + "palette": "orange" + }, + "numberFormat": { + "unit": 0, + "options": { + "style": "decimal", + "maximumSignificantDigits": 2 + } + } + }, + "secondaryContent": { + "columnMatch": "LastSeen", + "formatter": 6, + "dateFormat": { + "formatName": "shortDateTimePattern" + }, + "tooltipFormat": { + "tooltip": "Last seen time" + } + }, + "showBorder": true + } + }, + "customWidth": "16", + "name": "ClientTiles", + "styleSettings": { + "maxWidth": "250px" + } + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDConnections\r\n| where _ResourceId =~ '{HostPool}' and UserName == '{UserName}' and (\"{SelectedClientType}\" == \"*\" or ClientType == \"{SelectedClientType}\")\r\n| as SourceData\r\n| where State==\"Started\"\r\n| project-rename StartTime=TimeGenerated\r\n| join kind=fullouter\r\n(\r\n SourceData\r\n | where State == \"Completed\"\r\n | project CorrelationId, ClientVersion, ClientType, EndTime=TimeGenerated\r\n) on CorrelationId\r\n| project CorrelationId=coalesce(CorrelationId, CorrelationId1), StartTime=coalesce(StartTime, {TimeRange:start}), EndTime=coalesce(EndTime, {TimeRange:end}), ClientType=coalesce(ClientType, ClientType1), ClientVersion=coalesce(ClientVersion, ClientVersion1)\r\n| extend ClientTypeShort=replace(\"com.microsoft.rdc.\", \"\", ClientType), ProbeTime = range(bin_at(StartTime, {TimeRange:grain}, {TimeRange:start}), bin_at(EndTime + {TimeRange:grain}, {TimeRange:grain}, {TimeRange:end}), {TimeRange:grain})\r\n| mv-expand ProbeTime to typeof(datetime)\r\n| extend ConnectionContainsProbe = (StartTime < ProbeTime and EndTime > ProbeTime),\r\n ConnectionEndedInPreviousBucket = (StartTime < ProbeTime and EndTime + {TimeRange:grain} >= ProbeTime)\r\n| make-series Connections=dcountif(CorrelationId, ConnectionContainsProbe or ConnectionEndedInPreviousBucket) on ProbeTime from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by Client=strcat(ClientTypeShort, \" v\", ClientVersion)\r\n\r\n", + "size": 0, + "title": "Connections by client and version", + "noDataMessage": "No connections found for the configured filters.", + "timeContextFromParameter": "TimeRange", + "showExportToExcel": true, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "crossComponentResources": [ + "{poolla}" + ], + "visualization": "areachart", + "sortBy": [], + "chartSettings": { + "showMetrics": false, + "showLegend": true + } + }, + "customWidth": "42", + "name": "ConnectionsByClientAndVersion", + "styleSettings": { + "margin": "6px" + } + }, + { + "type": 12, + "content": { + "version": "NotebookGroup/1.0", + "groupType": "editable", + "items": [ + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDConnections\r\n| where UserName == '{UserName}'\r\n| where _ResourceId =~ '{HostPool}'\r\n| as SourceData\r\n| where State==\"Started\"\r\n| project-rename StartTime=TimeGenerated\r\n| join kind=fullouter\r\n(\r\n SourceData\r\n | where State == \"Completed\"\r\n | project CorrelationId, SessionHostName, EndTime=TimeGenerated\r\n) on CorrelationId\r\n| project CorrelationId=coalesce(CorrelationId, CorrelationId1), StartTime=coalesce(StartTime, {TimeRange:start}), EndTime=coalesce(EndTime, {TimeRange:end}), SessionHostName=coalesce(SessionHostName, SessionHostName1)\r\n| extend ProbeTime = range(bin_at(StartTime, {TimeRange:grain}, {TimeRange:start}), bin_at(EndTime + {TimeRange:grain}, {TimeRange:grain}, {TimeRange:end}), {TimeRange:grain})\r\n| mv-expand ProbeTime to typeof(datetime)\r\n| extend ConnectionContainsProbe = (StartTime < ProbeTime and EndTime > ProbeTime),\r\n ConnectionEndedInPreviousBucket = (StartTime < ProbeTime and EndTime + {TimeRange:grain} >= ProbeTime)\r\n| make-series Connections=dcountif(CorrelationId, ConnectionContainsProbe or ConnectionEndedInPreviousBucket) on ProbeTime from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by Host=trim_end(@\"\\..*\", SessionHostName)", + "size": 0, + "title": "Connections over time for {UserName}", + "noDataMessage": "No connections detected for this user in the specified time frame.", + "showExportToExcel": true, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "crossComponentResources": [ + "{poolla}" + ], + "visualization": "areachart", + "chartSettings": { + "showMetrics": false, + "showLegend": true + } + }, + "customWidth": "100", + "name": "ConnectionsbyUser" + } + ] + }, + "customWidth": "42", + "name": "Connections over time Group" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "//aligning to this: https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/rds-rdsh-performance-counters\r\nWVDConnectionNetworkData\r\n| join kind=inner (WVDConnections\r\n | where _ResourceId =~ \"{HostPool}\" and UserName == \"{UserName}\"\r\n | project UserName, GatewayRegion, CorrelationId\r\n | where GatewayRegion != \"<>\"\r\n) on CorrelationId\r\n| project-away CorrelationId1\r\n| make-series Median=percentile(EstRoundTripTimeInMs, 50) default=0, p95=percentile(EstRoundTripTimeInMs, 95) default=0 on TimeGenerated from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain}", + "size": 0, + "aggregation": 3, + "title": "RTT median and 95th percentile", + "timeContextFromParameter": "TimeRange", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "crossComponentResources": [ + "{poolla}" + ], + "visualization": "timechart" + }, + "customWidth": "50", + "name": "User Rtt Time Chart" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDConnections\r\n| where _ResourceId =~ \"{HostPool}\" and UserName == \"{UserName}\"\r\n| as ConnectionData\r\n| where State == \"Started\"\r\n| join kind = leftsemi\r\n(\r\n // Only include connections that actually reached the host to prevent short (failed) attempts from skewing the data\r\n WVDCheckpoints\r\n | where Source == \"RDStack\" and Name =~ \"RdpStackConnectionEstablished\" and _ResourceId =~ \"{HostPool}\"\r\n) on CorrelationId\r\n\r\n| join kind = inner\r\n(\r\n WVDCheckpoints\r\n | where _ResourceId =~ \"{HostPool}\"\r\n | where Name =~ \"LoadBalancedNewConnection\"\r\n | extend LoadBalanceOutcome=Parameters.LoadBalanceOutcome\r\n | project CorrelationId, ConnectionBackground=case(LoadBalanceOutcome == \"NewSession\", \"New session\", LoadBalanceOutcome in (\"Active\", \"Disconnected\", \"Pending\"), \"Existing session\", LoadBalanceOutcome)\r\n) on CorrelationId\r\n| join kind = inner\r\n(\r\n WVDCheckpoints // new session\r\n | where _ResourceId =~ \"{HostPool}\"\r\n | where Name =~ \"ShellReady\" or (Name =~ \"LaunchExecutable\" and Parameters.connectionStage == \"RdpShellAppExecuted\") or Name=~\"RdpShellAppExecuted\"\r\n | project TimeNewSessions=TimeGenerated, CorrelationId\r\n | summarize TimeNewSessions=min(TimeNewSessions) by CorrelationId\r\n\r\n) on CorrelationId\r\n| join kind = inner ( \r\n ConnectionData // existent Sessions\r\n | where State == \"Connected\"\r\n | project TimeConnected=TimeGenerated, CorrelationId\r\n) on CorrelationId\r\n| extend ProductiveTime = iff(ConnectionBackground==\"New session\",TimeNewSessions, TimeConnected)\r\n| join kind = leftouter\r\n( \r\n //OnCredentialsAcquisitionCompleted\t2021-09-13T16:48:50.4440000Z\tClient\t{\"CredentialType\":\"SavedPassword\",\"DurationMS\":\"9\",\"Success\":\"True\"}\r\n WVDCheckpoints\r\n | where _ResourceId =~ \"{HostPool}\"\r\n | where Name =~ \"OnCredentialsAcquisitionCompleted\"\r\n | project CorrelationId, credaquire = Parameters.DurationMS \r\n) on CorrelationId\r\n| join kind = leftouter\r\n( \r\n WVDCheckpoints\r\n | where _ResourceId =~ \"{HostPool}\"\r\n | where Name =~ \"SSOTokenRetrieval\"\r\n | project CorrelationId, ssotokeretrieval = Parameters.DurationMS\r\n) on CorrelationId\r\n| extend ProductiveTime = (ProductiveTime - TimeGenerated) / 1s - (coalesce(credaquire,0)/1000) - (coalesce(ssotokeretrieval,0)/1000)\r\n| make-series Median=percentile(ProductiveTime, 50) on TimeGenerated from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by ConnectionBackground\r\n\r\n", + "size": 0, + "aggregation": 5, + "title": "Time to connect", + "timeContextFromParameter": "TimeRange", + "showExportToExcel": true, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "crossComponentResources": [ + "{poolla}" + ], + "visualization": "unstackedbar", + "chartSettings": { + "ySettings": { + "numberFormatSettings": { + "unit": 24, + "options": { + "style": "decimal", + "useGrouping": true, + "maximumSignificantDigits": 2 + } + } + } + } + }, + "customWidth": "50", + "name": "TimeToConnectGraph" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDConnections\r\n| where UserName == '{UserName}' and _ResourceId =~ '{HostPool}'\r\n| as Connections\r\n| where State == \"Connected\" and TimeGenerated <= {TimeRange:end}\r\n| join kind=inner\r\n(\r\n WVDCheckpoints\r\n | where (Name == \"LaunchExecutable\" and Parameters.connectionStage == \"RdpShellAppExecuted\") or Name==\"RdpShellAppExecuted\"\r\n | project CorrelationId, Parameters=tostring(Parameters.filename)\r\n | extend Parameters=trim('\"', Parameters) // remove quotes for paths with spaces in them\r\n | extend Parameters=replace(@\"^(.*[\\\\\\/])\", \"\", tolower(Parameters)) // remove binary path to only keep file name\r\n | extend Parameters=replace(@\"microsoft.windows(.+)_8wekyb3d8bbwe!app\", @\"\\1\", Parameters) //inbox apps to readable format\r\n) on CorrelationId\r\n| summarize TotalLaunches=dcount(CorrelationId), DaysLaunched=dcount(bin(TimeGenerated, 1d)), Clients=dcount(ClientType) by Parameters\r\n| extend Subtitle=\"Launches / Days with usage\", Clients=strcat(\"Used on \", Clients, iif(Clients > 1, \" different \", \" \"), \"client\", iif(Clients > 1, \"s\", \"\"))\r\n| sort by TotalLaunches desc", + "size": 1, + "title": "Remote apps used", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "crossComponentResources": [ + "{poolla}" + ], + "visualization": "tiles", + "tileSettings": { + "titleContent": { + "columnMatch": "Parameters", + "formatter": 1 + }, + "subtitleContent": { + "columnMatch": "Clients" + }, + "leftContent": { + "columnMatch": "TotalLaunches", + "formatter": 12, + "formatOptions": { + "palette": "auto" + }, + "numberFormat": { + "unit": 17, + "options": { + "style": "decimal", + "maximumFractionDigits": 2, + "maximumSignificantDigits": 3 + } + } + }, + "rightContent": { + "columnMatch": "DaysLaunched", + "formatter": 12, + "formatOptions": { + "palette": "auto" + } + }, + "secondaryContent": { + "columnMatch": "Subtitle", + "formatter": 1 + }, + "showBorder": false + } + }, + "customWidth": "50", + "name": "PopularResourceByUser" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDConnections\r\n| where UserName == '{UserName}' and _ResourceId =~ '{HostPool}'\r\n| as Connections\r\n| where State == \"Started\" and TimeGenerated <= {TimeRange:end}\r\n| extend StartTime = TimeGenerated\r\n| join kind=leftouter\r\n(\r\n Connections\r\n | where UserName == \"{UserName}\" and State == \"Completed\"\r\n | extend EndTime = TimeGenerated\r\n)\r\non CorrelationId\r\n| extend EndTime = coalesce(EndTime, {TimeRange:end})\r\n| where EndTime >= {TimeRange:start}\r\n// limit duration hours to our timeframe even if connection spanned the edges...\r\n| extend Duration = min_of(EndTime, {TimeRange:end}) - max_of(StartTime, {TimeRange:start})\r\n| summarize Duration = sum(Duration) by Host=trim_end(@\"\\..*\", SessionHostName)\r\n| project Host, UsageHours = Duration / 1h\r\n", + "size": 1, + "title": "Connection time by host", + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "crossComponentResources": [ + "{poolla}" + ], + "visualization": "piechart", + "chartSettings": { + "yAxis": [ + "UsageHours" + ], + "ySettings": { + "numberFormatSettings": { + "unit": 26, + "options": { + "style": "decimal", + "useGrouping": true, + "maximumFractionDigits": 1 + } + } + } + } + }, + "customWidth": "50", + "name": "HostUsageByUser" + }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "WVDFeeds\r\n| where UserName == \"{UserName}\" and _ResourceId =~ '{wvdworkspace}' and (\"{SelectedClientType}\" == \"*\" or ClientType == \"{SelectedClientType}\")\r\n| extend ClientTypeShort=replace(\"com.microsoft.rdc.\", \"\", ClientType)\r\n| make-series FeedRefreshes=dcount(CorrelationId) on TimeGenerated from {TimeRange:start} to {TimeRange:end} step {TimeRange:grain} by strcat(ClientTypeShort, \" v\", ClientVersion)\r\n", + "size": 0, + "title": "Feed refreshes by client and version", + "timeContextFromParameter": "TimeRange", + "showExportToExcel": true, + "queryType": 0, + "resourceType": "microsoft.operationalinsights/workspaces", + "crossComponentResources": [ + "{poolla}" + ], + "visualization": "areachart", + "gridSettings": { + "formatters": [ + { + "columnMatch": "ClientType", + "formatter": 5 + }, + { + "columnMatch": "ClientVersion", + "formatter": 5 + }, + { + "columnMatch": "LastSeen", + "formatter": 6, + "formatOptions": { + "aggregation": "Max", + "customColumnWidthSetting": "30ch" + } + }, + { + "columnMatch": "FeedRefreshes", + "formatter": 4, + "formatOptions": { + "min": 0, + "palette": "gray", + "aggregation": "Sum", + "customColumnWidthSetting": "128px" + } + }, + { + "columnMatch": "Connections", + "formatter": 4, + "formatOptions": { + "min": 0, + "palette": "blue", + "aggregation": "Sum", + "customColumnWidthSetting": "115px" + } + } + ], + "hierarchySettings": { + "treeType": 1, + "groupBy": [ + "ClientType" + ], + "expandTopLevel": true, + "finalBy": "ClientVersion" + } + }, + "sortBy": [] + }, + "customWidth": "100", + "name": "FeedRefreshesByClientAndVersion", + "styleSettings": { + "margin": "6px" + } } ] }, @@ -1371,4 +1358,4 @@ "Azure Monitor" ], "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json" -} +} \ No newline at end of file diff --git a/Workbooks/Windows Virtual Desktop/shared/Host Pool Overview/Host Pool Overview.workbook b/Workbooks/Windows Virtual Desktop/shared/Host Pool Overview/Host Pool Overview.workbook index bda54fbf03..7971b6f2b5 100644 --- a/Workbooks/Windows Virtual Desktop/shared/Host Pool Overview/Host Pool Overview.workbook +++ b/Workbooks/Windows Virtual Desktop/shared/Host Pool Overview/Host Pool Overview.workbook @@ -180,11 +180,26 @@ }, "timeContextFromParameter": "TimeRange", "queryType": 1, + "resourceType": "microsoft.resourcegraph/resources", + "value": null + }, + { + "id": "c9f78080-1f60-4900-b137-3b9c319bde41", + "version": "KqlParameterItem/1.0", + "name": "HostpoolType", + "type": 1, + "query": "resources\r\n| where type == \"microsoft.desktopvirtualization/hostpools\" and resourceGroup =~ \"{ResourceGroup}\" and id =~ \"{HostPool}\"\r\n| project hostPoolType= properties.hostPoolType", + "crossComponentResources": [ + "{HostPool}" + ], + "isHiddenWhenLocked": true, + "queryType": 1, "resourceType": "microsoft.resourcegraph/resources" } ], "style": "pills", - "queryType": 12 + "queryType": 1, + "resourceType": "microsoft.resourcegraph/resources" }, "name": "parameters_general" }, @@ -215,7 +230,7 @@ "type": 3, "content": { "version": "KqlItem/1.0", - "query": "{\"version\":\"ARMEndpoint/1.0\",\"data\":null,\"headers\":[],\"method\":\"GETARRAY\",\"path\":\"/subscriptions/{Subscription:id}/resourcegroups/{ResourceGroup}/providers/Microsoft.DesktopVirtualization/hostpools/{HostPool:label}/sessionhosts?api-version=2022-10-14-preview\",\"urlParams\":[{\"key\":\"\",\"value\":\"\"}],\"batchDisabled\":false,\"transformers\":[{\"type\":\"jsonpath\",\"settings\":{\"columns\":[{\"path\":\"$.name\",\"columnid\":\"Name\"},{\"path\":\"$.properties.resourceId\",\"columnid\":\"ResoourceId\"},{\"path\":\"$.properties.status\",\"columnid\":\"Status\"},{\"path\":\"$.properties.allowNewSession\",\"columnid\":\"AllowNewSessions\"},{\"path\":\"$.properties.lastHeartBeat\",\"columnid\":\"LastHeartBeat\"},{\"path\":\"$.properties.sxSStackVersion\",\"columnid\":\"StackVersion\"},{\"path\":\"$.properties.lastUpdateTime\",\"columnid\":\"LastUpdateTime\"},{\"path\":\"$.name\",\"columnid\":\"HostPool\",\"substringRegexMatch\":\"(.*)/.*\",\"substringReplace\":\"$1\"},{\"path\":\"$.properties.sessions\",\"columnid\":\"Sessions\"},{\"path\":\"$.properties.osVersion\",\"columnid\":\"OSversion\"},{\"path\":\"$.id\",\"columnid\":\"URI\"}]}}]}", + "query": "{\"version\":\"ARMEndpoint/1.0\",\"data\":null,\"headers\":[],\"method\":\"GETARRAY\",\"path\":\"/subscriptions/{Subscription:id}/resourcegroups/{ResourceGroup}/providers/Microsoft.DesktopVirtualization/hostpools/{HostPool:label}/sessionhosts?api-version=2022-10-14-preview\",\"urlParams\":[{\"key\":\"\",\"value\":\"\"}],\"batchDisabled\":false,\"transformers\":[{\"type\":\"jsonpath\",\"settings\":{\"columns\":[{\"path\":\"$.name\",\"columnid\":\"Name\"},{\"path\":\"$.properties.resourceId\",\"columnid\":\"ResoourceId\"},{\"path\":\"$.properties.status\",\"columnid\":\"Status\"},{\"path\":\"$.properties.allowNewSession\",\"columnid\":\"AllowNewSessions\"},{\"path\":\"$.properties.lastHeartBeat\",\"columnid\":\"LastHeartBeat\"},{\"path\":\"$.properties.sxSStackVersion\",\"columnid\":\"StackVersion\"},{\"path\":\"$.properties.lastUpdateTime\",\"columnid\":\"LastUpdateTime\"},{\"path\":\"$.name\",\"columnid\":\"HostPool\",\"substringRegexMatch\":\"(.*)/.*\",\"substringReplace\":\"$1\"},{\"path\":\"$.properties.sessions\",\"columnid\":\"Sessions\"},{\"path\":\"$.properties.osVersion\",\"columnid\":\"OSversion\"},{\"path\":\"$.id\",\"columnid\":\"URI\"},{\"path\":\"$.properties.assignedUser\",\"columnid\":\"AssignedUser\"}]}}]}", "size": 0, "queryType": 12 }, @@ -250,7 +265,7 @@ "type": 3, "content": { "version": "KqlItem/1.0", - "query": "{\"version\":\"Merge/1.0\",\"merges\":[{\"id\":\"e0297506-948a-4021-b236-175107e751db\",\"mergeType\":\"leftouter\",\"leftTable\":\"HostPools\",\"rightTable\":\"hostpoolspecifics\",\"leftColumn\":\"HostPool\",\"rightColumn\":\"HostPool\"},{\"id\":\"e0297506-948a-4021-b236-175107e751dc\",\"mergeType\":\"leftouter\",\"leftTable\":\"HostPools\",\"rightTable\":\"HostUsers\",\"leftColumn\":\"Name\",\"rightColumn\":\"Computername\"}],\"projectRename\":[{\"originalName\":\"[HostPools].ResoourceId\",\"mergedName\":\"Host Name\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].Status\",\"mergedName\":\"Status\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].LastHeartBeat\",\"mergedName\":\"LastUpdateStatus\",\"fromId\":\"unknown\"},{\"originalName\":\"[HostPools].AllowNewSessions\",\"mergedName\":\"AllowNewSessions\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].StackVersion\",\"mergedName\":\"StackVersion\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].HostPool\",\"mergedName\":\"HostPool\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].Sessions\",\"mergedName\":\"Sessions\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[hostpoolspecifics].MaxSessionLimit\",\"mergedName\":\"MaxSessionLimit\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[Added column]\",\"mergedName\":\"AvailableSessions\",\"fromId\":null,\"isNewItem\":true,\"newItemData\":[{\"criteriaContext\":{\"leftOperand\":\"Sessions\",\"operator\":\"<=\",\"rightValType\":\"column\",\"rightVal\":\"MaxSessionLimit\",\"resultValType\":\"expression\",\"resultVal\":\"[\\\"MaxSessionLimit\\\"] -[\\\"Sessions\\\"]\"}},{\"criteriaContext\":{\"operator\":\"Default\",\"rightValType\":\"column\",\"resultValType\":\"static\",\"resultVal\":\"0\"}}]},{\"originalName\":\"[HostUsers].Users\",\"mergedName\":\"Users\",\"fromId\":\"e0297506-948a-4021-b236-175107e751dc\"},{\"originalName\":\"[HostPools].URI\",\"mergedName\":\"URI\",\"fromId\":\"unknown\"},{\"originalName\":\"[HostPools].OSversion\",\"mergedName\":\"OSversion\",\"fromId\":\"unknown\"},{\"originalName\":\"[HostPools].Name\"},{\"originalName\":\"[hostpoolspecifics].resourceGroup\"},{\"originalName\":\"[hostpoolspecifics].HostPool\"},{\"originalName\":\"[hostpoolspecifics].location\"},{\"originalName\":\"[hostpoolspecifics].LoadBalanceType\"},{\"originalName\":\"[hostpoolspecifics].HostPoolType\"},{\"originalName\":\"[HostUsers].Computername\"},{\"originalName\":\"[HostUsers].SessionHostName\"},{\"originalName\":\"[HostUsers].HostPool\"},{\"originalName\":\"[HostPools].LastUpdateStatus\"},{\"originalName\":\"[HostPools].LastUpdateTime\"},{\"originalName\":\"[hostpoolspecifics]. LoadBalanceType\"}]}", + "query": "{\"version\":\"Merge/1.0\",\"merges\":[{\"id\":\"e0297506-948a-4021-b236-175107e751db\",\"mergeType\":\"leftouter\",\"leftTable\":\"HostPools\",\"rightTable\":\"hostpoolspecifics\",\"leftColumn\":\"HostPool\",\"rightColumn\":\"HostPool\"},{\"id\":\"e0297506-948a-4021-b236-175107e751dc\",\"mergeType\":\"leftouter\",\"leftTable\":\"HostPools\",\"rightTable\":\"HostUsers\",\"leftColumn\":\"Name\",\"rightColumn\":\"Computername\"}],\"projectRename\":[{\"originalName\":\"[HostPools].ResoourceId\",\"mergedName\":\"Host Name\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].Status\",\"mergedName\":\"Status\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].LastHeartBeat\",\"mergedName\":\"LastUpdateStatus\",\"fromId\":\"unknown\"},{\"originalName\":\"[HostPools].AllowNewSessions\",\"mergedName\":\"AllowNewSessions\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].StackVersion\",\"mergedName\":\"StackVersion\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].HostPool\",\"mergedName\":\"HostPool\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].Sessions\",\"mergedName\":\"Sessions\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[hostpoolspecifics].MaxSessionLimit\",\"mergedName\":\"MaxSessionLimit\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[Added column]\",\"mergedName\":\"AvailableSessions\",\"fromId\":null,\"isNewItem\":true,\"newItemData\":[{\"criteriaContext\":{\"leftOperand\":\"Sessions\",\"operator\":\"<=\",\"rightValType\":\"column\",\"rightVal\":\"MaxSessionLimit\",\"resultValType\":\"expression\",\"resultVal\":\"[\\\"MaxSessionLimit\\\"] -[\\\"Sessions\\\"]\"}},{\"criteriaContext\":{\"operator\":\"Default\",\"rightValType\":\"column\",\"resultValType\":\"static\",\"resultVal\":\"0\"}}]},{\"originalName\":\"[HostUsers].Users\",\"mergedName\":\"Users\",\"fromId\":\"e0297506-948a-4021-b236-175107e751dc\"},{\"originalName\":\"[HostPools].URI\",\"mergedName\":\"URI\",\"fromId\":\"unknown\"},{\"originalName\":\"[HostPools].OSversion\",\"mergedName\":\"OSversion\",\"fromId\":\"unknown\"},{\"originalName\":\"[HostPools].AssignedUser\",\"mergedName\":\"AssignedUser\",\"fromId\":\"unknown\"},{\"originalName\":\"[HostPools].Name\"},{\"originalName\":\"[hostpoolspecifics].resourceGroup\"},{\"originalName\":\"[hostpoolspecifics].HostPool\"},{\"originalName\":\"[hostpoolspecifics].location\"},{\"originalName\":\"[hostpoolspecifics].LoadBalanceType\"},{\"originalName\":\"[hostpoolspecifics].HostPoolType\"},{\"originalName\":\"[HostUsers].Computername\"},{\"originalName\":\"[HostUsers].SessionHostName\"},{\"originalName\":\"[HostUsers].HostPool\"},{\"originalName\":\"[HostPools].LastUpdateStatus\"},{\"originalName\":\"[HostPools].LastUpdateTime\"},{\"originalName\":\"[hostpoolspecifics]. LoadBalanceType\"}]}", "size": 1, "exportedParameters": [ { @@ -405,6 +420,10 @@ "columnMatch": "URI", "formatter": 5 }, + { + "columnMatch": "AssignedUser", + "formatter": 5 + }, { "columnMatch": "Host pool", "formatter": 13, @@ -463,9 +482,266 @@ } ] }, + "conditionalVisibility": { + "parameterName": "HostpoolType", + "comparison": "isEqualTo", + "value": "Pooled" + }, "showPin": false, "name": "hostandperfmerge" }, + { + "type": 3, + "content": { + "version": "KqlItem/1.0", + "query": "{\"version\":\"Merge/1.0\",\"merges\":[{\"id\":\"e0297506-948a-4021-b236-175107e751db\",\"mergeType\":\"leftouter\",\"leftTable\":\"HostPools\",\"rightTable\":\"hostpoolspecifics\",\"leftColumn\":\"HostPool\",\"rightColumn\":\"HostPool\"},{\"id\":\"e0297506-948a-4021-b236-175107e751dc\",\"mergeType\":\"leftouter\",\"leftTable\":\"HostPools\",\"rightTable\":\"HostUsers\",\"leftColumn\":\"Name\",\"rightColumn\":\"Computername\"}],\"projectRename\":[{\"originalName\":\"[HostPools].ResoourceId\",\"mergedName\":\"Host Name\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].Status\",\"mergedName\":\"Status\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].LastHeartBeat\",\"mergedName\":\"LastUpdateStatus\",\"fromId\":\"unknown\"},{\"originalName\":\"[HostPools].AssignedUser\",\"mergedName\":\"AssignedUser\",\"fromId\":\"unknown\"},{\"originalName\":\"[HostPools].AllowNewSessions\",\"mergedName\":\"AllowNewSessions\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].StackVersion\",\"mergedName\":\"StackVersion\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].HostPool\",\"mergedName\":\"HostPool\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].Sessions\",\"mergedName\":\"Sessions\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[hostpoolspecifics].MaxSessionLimit\",\"mergedName\":\"MaxSessionLimit\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[Added column]\",\"mergedName\":\"AvailableSessions\",\"fromId\":null,\"isNewItem\":true,\"newItemData\":[{\"criteriaContext\":{\"leftOperand\":\"Sessions\",\"operator\":\"<=\",\"rightValType\":\"column\",\"rightVal\":\"MaxSessionLimit\",\"resultValType\":\"expression\",\"resultVal\":\"[\\\"MaxSessionLimit\\\"] -[\\\"Sessions\\\"]\"}},{\"criteriaContext\":{\"operator\":\"Default\",\"rightValType\":\"column\",\"resultValType\":\"static\",\"resultVal\":\"0\"}}]},{\"originalName\":\"[HostUsers].Users\",\"mergedName\":\"Users\",\"fromId\":\"e0297506-948a-4021-b236-175107e751dc\"},{\"originalName\":\"[HostPools].URI\",\"mergedName\":\"URI\",\"fromId\":\"unknown\"},{\"originalName\":\"[HostPools].OSversion\",\"mergedName\":\"OSversion\",\"fromId\":\"unknown\"},{\"originalName\":\"[HostPools].Name\",\"mergedName\":\"Name\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostPools].LastUpdateTime\",\"mergedName\":\"LastUpdateTime\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[hostpoolspecifics]. LoadBalanceType\",\"mergedName\":\" LoadBalanceType\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[hostpoolspecifics].HostPoolType\",\"mergedName\":\"HostPoolType\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[hostpoolspecifics].HostPool\",\"mergedName\":\"HostPool1\",\"fromId\":\"e0297506-948a-4021-b236-175107e751db\"},{\"originalName\":\"[HostUsers].SessionHostName\",\"mergedName\":\"SessionHostName\",\"fromId\":\"e0297506-948a-4021-b236-175107e751dc\"},{\"originalName\":\"[HostUsers].HostPool\",\"mergedName\":\"HostPool2\",\"fromId\":\"e0297506-948a-4021-b236-175107e751dc\"},{\"originalName\":\"[HostUsers].Computername\",\"mergedName\":\"Computername\",\"fromId\":\"e0297506-948a-4021-b236-175107e751dc\"}]}", + "size": 1, + "exportedParameters": [ + { + "fieldName": "URI", + "parameterName": "SelectedComputer" + }, + { + "fieldName": "Sessions", + "parameterName": "SelectedSessions", + "parameterType": 1, + "defaultValue": "0" + }, + { + "fieldName": "Host Name", + "parameterName": "Computer", + "parameterType": 5 + }, + { + "fieldName": "AssignedUser", + "parameterName": "UserName", + "parameterType": 1 + } + ], + "queryType": 7, + "visualization": "table", + "gridSettings": { + "formatters": [ + { + "columnMatch": "Host Name", + "formatter": 13, + "formatOptions": { + "linkTarget": "Resource", + "showIcon": true + } + }, + { + "columnMatch": "Status", + "formatter": 18, + "formatOptions": { + "thresholdsOptions": "icons", + "thresholdsGrid": [ + { + "operator": "==", + "thresholdValue": "Unavailable", + "representation": "3", + "text": "{0}{1}" + }, + { + "operator": "==", + "thresholdValue": "Available", + "representation": "success", + "text": "{0}{1}" + }, + { + "operator": "==", + "thresholdValue": "Shutdown", + "representation": "stopped", + "text": "{0}{1}" + }, + { + "operator": "==", + "thresholdValue": "NeedsAssistance", + "representation": "2", + "text": "Needs Assistance{1}" + }, + { + "operator": "==", + "thresholdValue": "Upgrading", + "representation": "1", + "text": "{0}{1}" + }, + { + "operator": "Default", + "thresholdValue": null, + "representation": "unknown", + "text": "{0}{1}" + } + ] + } + }, + { + "columnMatch": "LastUpdateStatus", + "formatter": 6, + "formatOptions": { + "customColumnWidthSetting": "27ch" + } + }, + { + "columnMatch": "AllowNewSession", + "formatter": 18, + "formatOptions": { + "thresholdsOptions": "icons", + "thresholdsGrid": [ + { + "operator": "==", + "thresholdValue": "true", + "representation": "Available", + "text": "Allowed" + }, + { + "operator": "==", + "thresholdValue": "false", + "representation": "disabled", + "text": "Drain" + }, + { + "operator": "Default", + "thresholdValue": null, + "representation": "Blank", + "text": "{0}{1}" + } + ] + } + }, + { + "columnMatch": "HostPool", + "formatter": 5 + }, + { + "columnMatch": "Sessions", + "formatter": 0, + "formatOptions": { + "aggregation": "Sum" + } + }, + { + "columnMatch": "MaxSessionLimit", + "formatter": 5 + }, + { + "columnMatch": "AvailableSessions", + "formatter": 5, + "formatOptions": { + "customColumnWidthSetting": "40fr" + } + }, + { + "columnMatch": "Users", + "formatter": 4, + "formatOptions": { + "min": 0, + "palette": "blue", + "customColumnWidthSetting": "40fr" + }, + "numberFormat": { + "unit": 0, + "options": { + "style": "decimal", + "useGrouping": false + }, + "emptyValCustomText": "0" + } + }, + { + "columnMatch": "URI", + "formatter": 5 + }, + { + "columnMatch": "Name", + "formatter": 5 + }, + { + "columnMatch": "LastUpdateTime", + "formatter": 5 + }, + { + "columnMatch": " LoadBalanceType", + "formatter": 5 + }, + { + "columnMatch": "SessionHostName", + "formatter": 5 + }, + { + "columnMatch": "Computername", + "formatter": 5 + }, + { + "columnMatch": "Host pool", + "formatter": 13, + "formatOptions": { + "linkTarget": "Resource", + "showIcon": true + } + } + ], + "rowLimit": 5000, + "sortBy": [ + { + "itemKey": "$gen_thresholds_Status_1", + "sortOrder": 2 + } + ], + "labelSettings": [ + { + "columnId": "Host Name", + "label": "Session Hosts" + }, + { + "columnId": "LastUpdateStatus", + "label": "Status changed" + }, + { + "columnId": "AssignedUser", + "label": "Assigned user" + }, + { + "columnId": "AllowNewSessions", + "label": "New sessions" + }, + { + "columnId": "StackVersion", + "label": "Stack version" + }, + { + "columnId": "HostPool", + "label": "Host pool" + }, + { + "columnId": "AvailableSessions", + "label": "Available sessions" + }, + { + "columnId": "Users", + "label": "24h users" + }, + { + "columnId": "OSversion", + "label": "OS version" + } + ] + }, + "sortBy": [ + { + "itemKey": "$gen_thresholds_Status_1", + "sortOrder": 2 + } + ] + }, + "conditionalVisibility": { + "parameterName": "HostpoolType", + "comparison": "isEqualTo", + "value": "Personal" + }, + "showPin": false, + "name": "hostandperfmerge - Personal" + }, { "type": 12, "content": { @@ -607,6 +883,11 @@ } }, "customWidth": "0", + "conditionalVisibility": { + "parameterName": "HostpoolType", + "comparison": "isEqualTo", + "value": "Pooled" + }, "showPin": false, "name": "HostPool-MaxSessions-Card", "styleSettings": { diff --git a/Workbooks/Windows Virtual Desktop/shared/Template Versioning Footer/Template Versioning Footer.workbook b/Workbooks/Windows Virtual Desktop/shared/Template Versioning Footer/Template Versioning Footer.workbook index 66a8ee5647..5c3ff97f55 100644 --- a/Workbooks/Windows Virtual Desktop/shared/Template Versioning Footer/Template Versioning Footer.workbook +++ b/Workbooks/Windows Virtual Desktop/shared/Template Versioning Footer/Template Versioning Footer.workbook @@ -13,7 +13,7 @@ "type": 1, "description": "Internal parameter to centralize the template version", "isRequired": true, - "value": "4.1.0", + "value": "4.2.0", "isHiddenWhenLocked": true } ],