Hey guys, is it currently possible to control the schedule for a UD endpoint? after the dashboard has started?
Hi,
I canât answer your specific question, but maybe you could try to explain what you are trying to achieve?
@dtnyc9005
Unfortunately you cannot edit the endpoint schedule after starting the dashboard.
@PorreKaj, so Iâm looking to give the user the option to possibly control the schedule for endpoints, for instance on a standard endpoint as such, would be interesting if we can pass an integer to the New-UDEndpointSchedule -every (parameter), again not even sure if this is possible.
New-UDEndpoint -Endpoint {
#Do Something....
} -Schedule (New-UDEndpointSchedule -Every $Cache:VARIABLE -Minute)
Im thinking of a $cache:varable because UD under the hood will need to know the latest value, maybe a question for @adam , if this could be changed at runtime, how often is the endpoint/schedule evaluated?
@dtnyc9005
you can have the endpoint schedule for 1 every day and create a button to call the endpoint at any time and re run the script block to refresh the value of the cache variable.
look for Invoke-UDEvent -Id âsomeidâ -scheduled.
So the Beaty/horrors of Powershell is that there is never only one way of solving things.
Iâm this case I would experiment with having the endpoint run on the lowest amount of time possible (the amount of time the script will take to execute), then inside the endpoint have code that checks wether or not X amount has
passed since last run. With X being the $cache:variable that the user can define.
Yup I definitely need to test this out, Iâve been looking for this for sometime didnât even know it was there
I saw that you had opened a git issue, seeing this come to life would be awesome Iâm sure others with cache datasets would love to have that kind of control outside the endpoint.
Iâll try this and post my findings⌠thanks.
Yes, I was thinking the same, love that posh is this way, you just have to be creative with it ;),
Iâm in the process of writing all this up so I will post how this goes.
thanks again.
@wsl2001 So, this feature is insanley cool to have, but I have noticed an issue, where it will only fire once, let me explain:
- I have table that gets populated with $cache:variable (that comes from the schedule endpoint, this is fine comes through with data and buttons and modals - no biggy hereâŚ)
- One of those buttons has the invoke-UDEvent code, this fires but ONLY once, thereafter when trying to execute it, I get: âan unexpected error occured invoking âclient eventâ on the server. exceptionâ
- no matter what I do, refresh, click other buttons with modals, I get same âclientEventâ error
Iâm adding somecode to put things in perspective:
####### ENDPOINT1.PS1 #########
New-UDEndpoint -id "testthisep" -Endpoint {
#Execute some code and build cache variable below
$cache:data = @(
[PSCustomObject]@{
Application = (New-UDLink -Text "$AppName" -Url "/Apps/$AppName" -OpenInNewWindow)
Results = $Result
Uri = (New-UDLink -Text "$Uri" -Url "$Uri" -OpenInNewWindow)
Details = New-UDButton -Text "Details" -OnClick (
New-UDEndpoint -Endpoint {
Show-UDModal -Width auto -Height auto -Content {
}
}
)
ReExecuteEndpoint = New-UDButton -Text "ReFire" -OnClick (
New-UDEndpoint -Endpoint {
Invoke-UDEvent -Id "testthisep" -scheduled
}
)
LasRefreshed = (get-date -Format t)
}
)
} -Schedule (New-UDEndpointSchedule -Every 30 -Minute)
####### HOMEPAGE.PS1 #########
New-UDPage -Name "HomePage" -Content {
New-UDTable -Title "AppList" -Headers @("Application", "Results", "Uri", "Details", "ReExecuteEndpoint", "LastRefreshed") -AutoRefresh -Endpoint {
$cache:data | Out-UDTableData -Property @("Application", "Results", "Uri", "Details", "ReExecuteEndpoint", "LastRefreshed")
}
}
Anythoughts on this behavior?
@dtnyc9005 thats correct because the id of the button changed, this is the same issue i have when i tried invoke-udevent and i have opened a ticket for it.
try to give your button an id and see if that helps in your example.
@wsl2001, yeah this was exciting while it lasted, but still no dice, tried to set ids where ever possible but same clientevent on server error. throwing this @adam way to see if there any plans for enterprise folks?
hoping I can give the other solution a try (@PorreKaj) tonight, were we set and control the schedule through a $cache:endpoint
Hey everyone,
Seems like controlling schedules after the dashboard has started is really important to you so I took some this morning and implemented it.
Iâve added Remove-UDEndpoint. Youâll be able to remove endpoints (even non-scheduled ones but be careful with that).
$Schedule = New-UDEndpointSchedule -Every 1 -Second
$EverySecond = New-UDEndpoint -Schedule $Schedule -Endpoint {
$Cache:Value = "FirstSchedule"
} -Id 'schedule'
Remove-UDEndpoint -Id 'schedule'
Youâll also be able to create new schedules on the fly. If you call New-UDEndpoint in a script block, like an OnClick for example, it will automatically register the new endpoint with the UD scheduler.
For example, in my tests, Iâm using the UD REST API to remove the existing schedule and then recreate a new schedule on the fly.
$Endpoint = New-UDEndpoint -Url "changeSchedule" -Endpoint {
Remove-UDEndpoint -Id 'schedule'
$Schedule = New-UDEndpointSchedule -Every 1 -Second
New-UDEndpoint -Schedule $Schedule -Endpoint {
$Cache:Value = "SecondSchedule"
} -Id 'schedule'
}
Hope thatâs what youâre after. This will be in tonightâs nightly build.
This is awesome. Thanks
@adam
is it possible to call the scheduled endpoint manually as well like onclick?
That wasnât part of this PR.
I have a question about that though. Whatâs the benefit of calling a scheduled endpoint manually vs just calling a function in a module? For example, letâs say you have a scheduled endpoint that sets some value in the cache.
$Schedule = New-UDEndpointSchedule -Every 1 -Second
$EverySecond = New-UDEndpoint -Schedule $Schedule -Endpoint {
$Cache:Value = "FirstSchedule"
} -Id 'schedule'
I understand that youâd like to call that scheduled endpoint with something like:
New-UDButton -OnClick {
Invoke-UDEndpoint -Id 'schedule'
}
But couldnât you just define a function in a module like Set-Cache
?
function Set-Cache {
$Cache:Value = "FirstSchedule"
}
$Schedule = New-UDEndpointSchedule -Every 1 -Second
$EverySecond = New-UDEndpoint -Schedule $Schedule -Endpoint {
Set-Cache
} -Id 'schedule'
New-UDButton -OnClick {
Set-Cache
}
Or is it that you want the Invoke-UDEndpoint to run asynchronously?
@adam
so the benefit of that is then you dont have to to run a scheduled endpoints more often and have the memory builds up fast.
so for example if i have an endpoint scheduled every 5 sec to pull services from a remote machine and assign a button foreach to control start/stop the service, i have noticed that in matter of couple hours the memory consumption is high and UD becomes slow.
but with the same scenario if the schedule is every 1 day then when someone click the button to start / stop a service i can add a call of the endpoint manually and this way i can get the service status updated right away and will save the memory from building up very quickly.
the issue right now with invoke-udelement is if you have the button as a part of the scheduled endpoint once you initiated the invoke-udelement it will change the button id and it wont work for a second click.
I can understand the memory issue. But couldnât you just update the server list when the user clicked the button? Why does it need to call the scheduled endpoint when the user clicks the button and not just a function or something?
The issue with a changing button ID will not be solved by allowing you to invoke a scheduled endpoint. If the ID changes and the user doesnât refresh the page or if the control containing that button ID isnât updated some how, the button wonât work because it will try to invoke the old ID.
You could cache the server data and create the button when the page is loaded rather than creating the button and storing it in the cache.
Maybe this is all best described in code. I will put an example of what I am thinking and then we can discuss it based on the code. I need to take my dog for a walk but I will write something up when I get back
@adam
Thank you for the upcoming testing code i have tried in the past your idea but i have found that creating the button in the cache was way faster than having it created when the page is loaded am going to post my code as an example which shows the fastest way based on my testing but with a downside of memory build up fast if i scheduled the endpoint in seconds.
$Schedule1 = New-UDEndpointSchedule -Every 5 -Second
$Services = New-UDEndpoint -Schedule $Schedule1 -Endpoint {
$Cache:Services = (Get-Service -ComputerName "server1" -Name "*Certification*", "*Notification*", "*Distribution*") | Select-Object Name, Status | Sort-Object Name | ForEach-Object {
$FontColor = 'Green'
if ($_.Status -ne 'Running') {
$FontColor = 'Red'
}
[PSCustomObject]@{
Name = $_.Name
Status = New-UDElement -Tag 'div' -Attributes @{ style = @{ color = $FontColor } } -Content { $_.Status.ToString() }
Select = if ($_.Status -eq 'Stopped') {
New-UDTooltip -Type info -Effect float -Place top -TooltipContent { "Start service!" } -Content {
New-UDButtonLoader -BackgroundColor "Red" -Text "Start" -OnClick {
Get-Service -ComputerName "server1" -Name $_.Name | Start-Service
Show-UDToast -Message "Service Started!" -MessageColor Green -Title $_.Name -Position topCenter -Duration 2500
}
}
}
else {
if ($_.Status -eq 'Running') {
New-UDTooltip -Type info -Effect float -Place top -TooltipContent { "Stop service!" } -Content {
New-UDButtonLoader -BackgroundColor "#26a69a" -Text "Stop" -Icon stop_circle -OnClick {
Get-Service -ComputerName "server1" -Name $_.Name | Stop-Service
Show-UDToast -Message "Service Stopped!" -MessageColor Red -Title $_.Name -Position topCenter -Duration 2500
}
}
}
}
}
}
}
$HomePage = New-UDPage -Name "Home" -Icon home -Content {
New-UDScrollUp
New-UDCard -Title "Welcome to Tasks Manager Dashboard! You can begin by clicking hamburger menu icon above"
New-UdRow {
New-UdColumn -Size 6 -Content {
New-UdRow {
New-UdColumn -Size 12 -Content {
New-UdTable -Title "Server Information" -Headers @(" ", " ") -BackgroundColor "#050f7f" -FontColor "#FFFFFF" -Endpoint {
@{
'Computer Name' = $env:COMPUTERNAME
'Operating System' = (Get-CimInstance -ClassName Win32_OperatingSystem).Caption
'Total Disk Space (C:)' = (Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceID='C:'").Size / 1GB | ForEach-Object { "$([Math]::Round($_, 2)) GBs " }
'Free Disk Space (C:)' = (Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceID='C:'").FreeSpace / 1GB | ForEach-Object { "$([Math]::Round($_, 2)) GBs " }
'Product Version' = "2.8.3"
}.GetEnumerator() | Out-UDTableData -Property @("Name", "Value")
}
}
}
New-UdRow {
New-UdColumn -Size 3 -Content {
New-UdChart -Title "Memory by Process" -Type Doughnut -RefreshInterval 5 -Endpoint {
Get-Process | ForEach-Object { [PSCustomObject]@{ Name = $_.Name; WorkingSet = [Math]::Round($_.WorkingSet / 1MB, 2) } } | Out-UDChartData -DataProperty "WorkingSet" -LabelProperty Name
} -Options @{
legend = @{
display = $false
}
}
}
New-UdColumn -Size 3 -Content {
New-UdChart -Title "CPU by Process" -Type Doughnut -RefreshInterval 5 -Endpoint {
Get-Process | ForEach-Object { [PSCustomObject]@{ Name = $_.Name; CPU = $_.CPU } } | Out-UDChartData -DataProperty "CPU" -LabelProperty Name
} -Options @{
legend = @{
display = $false
}
}
}
New-UdColumn -Size 3 -Content {
New-UdChart -Title "Handle by Process" -Type Doughnut -RefreshInterval 5 -Endpoint {
Get-Process | Out-UDChartData -DataProperty "HandleCount" -LabelProperty Name
} -Options @{
legend = @{
display = $false
}
}
}
New-UdColumn -Size 3 -Content {
New-UdChart -Title "Threads by Process" -Type Doughnut -RefreshInterval 5 -Endpoint {
Get-Process | ForEach-Object { [PSCustomObject]@{ Name = $_.Name; Threads = $_.Threads.Count } } | Out-UDChartData -DataProperty "Threads" -LabelProperty Name
} -Options @{
legend = @{
display = $false
}
}
}
}
New-UdRow {
New-UdColumn -Size 12 -Content {
New-UdChart -Title "Disk Space by Drive" -Type Bar -AutoRefresh -Endpoint {
Get-CimInstance -ClassName Win32_LogicalDisk | ForEach-Object {
[PSCustomObject]@{ DeviceId = $_.DeviceID;
Size = [Math]::Round($_.Size / 1GB, 2);
FreeSpace = [Math]::Round($_.FreeSpace / 1GB, 2);
} } | Out-UDChartData -LabelProperty "DeviceID" -Dataset @(
New-UdChartDataset -DataProperty "Size" -Label "Size" -BackgroundColor "#80962F23" -HoverBackgroundColor "#80962F23"
New-UdChartDataset -DataProperty "FreeSpace" -Label "Free Space" -BackgroundColor "#8014558C" -HoverBackgroundColor "#8014558C"
)
} -Options @{
scales = @{
yAxes = @(@{
min = 0
})
}
}
}
}
}
New-UdColumn -Size 6 -Content {
New-UdRow {
New-UdColumn -Size 6 -Content {
New-UdMonitor -Title "CPU (% processor time)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#80FF6B63' -ChartBorderColor '#FFFF6B63' -Endpoint {
try {
Get-Counter '\Processor(_Total)\% Processor Time' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
}
catch {
0 | Out-UDMonitorData
}
}
}
New-UdColumn -Size 6 -Content {
New-UdMonitor -Title "SQL CPU (% processor time)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#80FF6B63' -ChartBorderColor '#FFFF6B63' -Endpoint {
try {
Get-Counter '\Processor(_Total)\% Processor Time' -ComputerName sqlserver -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
}
catch {
0 | Out-UDMonitorData
}
}
}
New-UdColumn -Size 6 -Content {
New-UdMonitor -Title "Memory (% in use)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#8028E842' -ChartBorderColor '#FF28E842' -Endpoint {
try {
Get-Counter '\memory\% committed bytes in use' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
}
catch {
0 | Out-UDMonitorData
}
}
}
New-UdColumn -Size 6 -Content {
New-UdMonitor -Title "SQL Memory (% in use)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#8028E842' -ChartBorderColor '#FF28E842' -Endpoint {
try {
Get-Counter '\memory\% committed bytes in use' -ComputerName sqlserver -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
}
catch {
0 | Out-UDMonitorData
}
}
}
}
New-UdRow {
New-UdColumn -Size 6 -Content {
New-UdMonitor -Title "Network (IO Read Bytes/sec)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#80E8611D' -ChartBorderColor '#FFE8611D' -Endpoint {
try {
Get-Counter '\Process(_Total)\IO Read Bytes/sec' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
}
catch {
0 | Out-UDMonitorData
}
}
}
New-UdColumn -Size 6 -Content {
New-UdMonitor -Title "SQL Network (IO Read Bytes/sec)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#80E8611D' -ChartBorderColor '#FFE8611D' -Endpoint {
try {
Get-Counter '\Process(_Total)\IO Read Bytes/sec' -ComputerName sqlserver -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
}
catch {
0 | Out-UDMonitorData
}
}
}
New-UdColumn -Size 6 -Content {
New-UdMonitor -Title "Disk (% disk time)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#80E8611D' -ChartBorderColor '#FFE8611D' -Endpoint {
try {
Get-Counter '\physicaldisk(_total)\% disk time' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
}
catch {
0 | Out-UDMonitorData
}
}
}
New-UdColumn -Size 6 -Content {
New-UdMonitor -Title "SQL Disk (% disk time)" -Type Line -DataPointHistory 20 -RefreshInterval 5 -ChartBackgroundColor '#80E8611D' -ChartBorderColor '#FFE8611D' -Endpoint {
try {
Get-Counter '\physicaldisk(_total)\% disk time' -ComputerName sqlserver -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue | Out-UDMonitorData
}
catch {
0 | Out-UDMonitorData
}
}
}
}
New-UdRow {
New-UdColumn -Size 12 {
New-UdGrid -Title "Processes" -Headers @("Name", "ID", "Working Set", "CPU") -Properties @("Name", "Id", "WorkingSet", "CPU") -AutoRefresh -RefreshInterval 60 -Endpoint {
Get-Process | Select-Object Name, ID, WorkingSet, CPU | Out-UDGridData
}
}
}
}
}
}
$Page1 = New-UDPage -Name "Services" -Icon server -Content {
New-UDScrollUp
New-UDElement -Id "Services" -Tag div4 -EndPoint {
New-UDGrid -Id "Grid" -Title "Services" -Headers @("Name", "Status", "Select") -Properties @("Name", "Status", "Select") -Endpoint {
$Cache:Services | Out-UDGridData
}
}
New-UDButtonParticle -Color "#26a69a" -BackgroundColor "#26a69a" -Text "Refresh" -Duration "800" -OnClick {
Start-Sleep -Seconds 2
Sync-UDElement -Id "Grid" -Broadcast
Start-Sleep -Seconds 2
}
}
Start-UDDashboard -Wait -Endpoint @($Services) -Content {
New-UDDashboard -Pages @($HomePage, $Page1) -Title "Tasks Manager" -Color '#FF050F7F'
} -Port 80 -Name Tasks
as you can see from the code above i have added a refresh button in the page so when a user click the button to start a service he can click the refresh button which is in return refresh the grid and he can see the status of the service has been changed based on his action and the button changed as well to stop. since the schedule is set for 5 seconds the results will be fast since by the time the user click the refresh button the cache will be updated.
but the downside of this process a memory consumption is high.
if the scheduled time increased for example to every 1 minute, after the user click the button to start a service he has to wait longer until the cache updated before he can see the new status and that might cause confusing.
the ultimate solution will be a schedule for every 1 -day , when user click the button recall the schedule to run 1 time so the cache updated right away and hitting the refresh button will show the new status.
I understand what you are describing and thatâs definitely a solution but in my opinion it makes sense to ha something like a Invoke-UDScheduledEndpoint
in order to have a cleaner code. For it makes more sense to but all the logic in the scheduled endpoint and invoke it whenever I want, rather than to have to create an âexternalâ function ⌠and for many people it is more self explained to manually execute a scheduled endpoint
And one last addition in order to explain why it would result in bad code (only my opinion):
The scheduled endpoint is having for example a punch of logic (code) - caching variable get/set and in some conditions execute other functions (e.g. Active Directory) and/or update some UD components âŚ
If I would have to create an external function for that I would name it âInvoke-âŚScheduledEndpointLogicâ and put in the exact same logic maybe used and needed only for this one use case.
And that makes my code much harder to read.