Dashboard fails to start when service starts

Product: PowerShell Universal
Version: 2.3.1

Currently running 3 Dashboards on my instance. One (which is the production dashboard), does not auto-start when the Powershell Universal service starts up. I can manually start it no problem. 1 of the dev-dashboards is the same code as the prod (with a couple of extra’s in it). I tried looking through the logs, but could not find anything. Not sure if anyone could point me to anything specific so I could provide more information, or if anyone else has had similar issues.

I’d suggest turning up logging to Debug for the Default log level. We do some additional logging about starting the dashboard processes as well as any STDOUT produced by those processes.

Okay… This is weird

I turned on debugging, dashboard started.
I reverted to normal logging, dashboard did not start.
I turned on debugging, dashboard started.
I reverted to normal logging, dashboard did not start.
I turned on debugging, dashboard started.
I reverted to normal logging, dashboard DID start.

I restarted the service, and the dashboard started.

But here is something from the debug log…

2021-09-22T14:32:06.2476162-04:00  [DBG] Loading framework from asset directory: C:\ProgramData\PowerShellUniversal\Dashboard\Frameworks\UniversalDashboard\2.9.9 (67d454a5)
2021-09-22T14:32:07.3733712-04:00  [DBG] The specified RequiredModules entry 'Universal' in the module manifest 'C:\ProgramData\PowerShellUniversal\Dashboard\Frameworks\UniversalDashboard\2.9.9\UniversalDashboard.psd1' is invalid. Try again after updating this entry with valid values. (2e6cdc60)
2021-09-22T14:32:07.3889463-04:00  [DBG] Loading framework from asset directory: C:\ProgramData\PowerShellUniversal\Dashboard\Frameworks\UniversalDashboard\3.5.1 (67748671)
2021-09-22T14:32:07.5191331-04:00  [DBG] The specified RequiredModules entry 'Universal' in the module manifest 'C:\ProgramData\PowerShellUniversal\Dashboard\Frameworks\UniversalDashboard\3.5.1\UniversalDashboard.psd1' is invalid. Try again after updating this entry with valid values. (4df3cbe3)
2021-09-22T14:32:07.5201421-04:00  [DBG] Loading framework from asset directory: C:\ProgramData\PowerShellUniversal\Dashboard\Frameworks\UniversalDashboard\3.6.0 (c1480138)
2021-09-22T14:32:07.6544343-04:00  [DBG] The specified RequiredModules entry 'Universal' in the module manifest 'C:\ProgramData\PowerShellUniversal\Dashboard\Frameworks\UniversalDashboard\3.6.0\UniversalDashboard.psd1' is invalid. Try again after updating this entry with valid values. (a61c6ebf)
2021-09-22T14:32:07.7751769-04:00  [DBG] The specified RequiredModules entry 'UniversalDashboard' in the module manifest 'C:\ProgramData\PowerShellUniversal\Dashboard\Components\UniversalDashboard.Charts\1.3.2\UniversalDashboard.Charts.psd1' is invalid. Try again after updating this entry with valid values. (e063b8b7)
2021-09-22T14:32:07.9060633-04:00  [DBG] The specified RequiredModules entry 'UniversalDashboard' in the module manifest 'C:\ProgramData\PowerShellUniversal\Dashboard\Components\UniversalDashboard.CodeEditor\1.1.1\UniversalDashboard.CodeEditor.psd1' is invalid. Try again after updating this entry with valid values. (05afda7a)
2021-09-22T14:32:08.0184935-04:00  [DBG] The specified RequiredModules entry 'UniversalDashboard' in the module manifest 'C:\ProgramData\PowerShellUniversal\Dashboard\Components\UniversalDashboard.Map\1.0\UniversalDashboard.Map.psd1' is invalid. Try again after updating this entry with valid values. (a1910263)
2021-09-22T14:32:08.1922699-04:00  [DBG] Reading configuration file licenses.ps1 (54ebd27c)
2021-09-22T14:32:08.2869995-04:00  [DBG] Activating online license key... (110fa7ec)
2021-09-22T14:32:11.2204400-04:00  [DBG] Reading configuration file accessControls.ps1 (de0353aa)
2021-09-22T14:32:11.2210774-04:00  [DBG] Reading configuration file tags.ps1 (502a9e15)
2021-09-22T14:32:11.2235811-04:00  [DBG] Reading configuration file settings.ps1 (25ccd83a)
2021-09-22T14:32:11.2347450-04:00  [DBG] Reading configuration file variables.ps1 (fed1013d)
2021-09-22T14:32:11.2592621-04:00  [DBG] Reading configuration file roles.ps1 (eb4aef62)
2021-09-22T14:32:11.2835589-04:00  [DBG] Reading configuration file environments.ps1 (64b51cd2)
2021-09-22T14:32:11.5029781-04:00  [DBG] Reading configuration file scripts.ps1 (30814263)
2021-09-22T14:32:11.5797898-04:00  [DBG] Reading configuration file authentication.ps1 (5f7879e3)
2021-09-22T14:32:11.5806455-04:00  [DBG] Reading configuration file endpoints.ps1 (3751b85e)
2021-09-22T14:32:11.8396315-04:00  [DBG] Adding endpoint: (POST) /cdetails (9872fd85)
2021-09-22T14:32:11.8412433-04:00  [DBG] Reading configuration file dashboards.ps1 (6293f74f)
2021-09-22T14:32:11.8734695-04:00  [DBG] Reading configuration file schedules.ps1 (37071f11)
2021-09-22T14:32:11.9143249-04:00  [DBG] Starting C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with command line  -NoProfile -Command "& { [System.Reflection.Assembly]::LoadFrom('D:\Universal\Host\Host.dll') | Out-Null; [UniversalHost.AgentService]::StartDashboard(49380, 22660, 49378) }" (b76f622e)
2021-09-22T14:32:11.9143608-04:00  [INF] Starting job using Process. (4d0a5a43)
2021-09-22T14:32:11.9645074-04:00  [INF] Scheduling script 4 on schedule 0 0 * * * with time zone America/New_York. (5aaaad6e)
2021-09-22T14:32:11.9668777-04:00  [INF] Scheduling job '1' with schedule 0 0 * * * on time zone America/New_York (d1ac76b2)
2021-09-22T14:32:12.0206280-04:00  [DBG] Reading configuration file publishedFolders.ps1 (024d01a3)
2021-09-22T14:32:12.0210751-04:00  [DBG] Reading configuration file ratelimits.ps1 (9a933eb1)
2021-09-22T14:32:12.0215216-04:00  [DBG] Reading configuration file triggers.ps1 (37bab43c)
2021-09-22T14:32:12.0219152-04:00  [DBG] Reading configuration file loginPage.ps1 (2804e6ac)
2021-09-22T14:32:12.0960686-04:00  [DBG] Deploying embedded assets (e8761142)
2021-09-22T14:32:12.0962077-04:00  [DBG] Loading framework in: D:\Universal\UniversalDashboard\Frameworks\v2 (56c7b7e4)
2021-09-22T14:32:12.1199000-04:00  [DBG] Registered SignalR Protocol: "json", implemented by "Microsoft.AspNetCore.SignalR.Protocol.JsonHubProtocol". (c8f2a7df)
2021-09-22T14:32:12.2523454-04:00  [INF] Now listening on: "http://[::]:80" (d826f4b8)
2021-09-22T14:32:12.2523774-04:00  [INF] Now listening on: "https://[::]:443" (d826f4b8)
2021-09-22T14:32:12.2524326-04:00  [DBG] Loaded hosting startup assembly "Universal.Server" (3d5180d3)
2021-09-22T14:32:12.2537102-04:00  [INF] Application started. Hosting environment: "Production"; Content root path: "D:\Universal\" (9029d6f8)
2021-09-22T14:32:12.2537496-04:00  [DBG] Hosting started (e6def423)
2021-09-22T14:32:12.4103779-04:00  [DBG] The specified RequiredModules entry 'Universal' in the module manifest 'D:\Universal\UniversalDashboard\Frameworks\v2\UniversalDashboard.psd1' is invalid. Try again after updating this entry with valid values. (4cb7bc92)
2021-09-22T14:32:12.4116509-04:00  [DBG] Dashboard framework already exists. (f14ac959)
2021-09-22T14:32:12.4116739-04:00  [DBG] Loading framework in: D:\Universal\UniversalDashboard\Frameworks\v3 (a0754b42)
2021-09-22T14:32:12.5800036-04:00  [DBG] The specified RequiredModules entry 'Universal' in the module manifest 'D:\Universal\UniversalDashboard\Frameworks\v3\UniversalDashboard.psd1' is invalid. Try again after updating this entry with valid values. (d3f0e21a)
2021-09-22T14:32:12.5810706-04:00  [DBG] Dashboard framework already exists. (f14ac959)
2021-09-22T14:32:12.5811953-04:00  [DBG] Loading component in: D:\Universal\UniversalDashboard\Components\UniversalDashboard.Charts (dee374dd)
2021-09-22T14:32:13.9477941-04:00  [DBG] The specified RequiredModules entry 'UniversalDashboard' in the module manifest 'D:\Universal\UniversalDashboard\Components\UniversalDashboard.Charts\UniversalDashboard.Charts.psd1' is invalid. Try again after updating this entry with valid values. (3e617c65)
2021-09-22T14:32:13.9496651-04:00  [DBG] Dashboard component already exists. (4cfe4bd7)
2021-09-22T14:32:13.9496752-04:00  [DBG] Loading component in: D:\Universal\UniversalDashboard\Components\UniversalDashboard.CodeEditor (501113ac)
2021-09-22T14:32:15.8870765-04:00  [DBG] The specified RequiredModules entry 'UniversalDashboard' in the module manifest 'D:\Universal\UniversalDashboard\Components\UniversalDashboard.CodeEditor\UniversalDashboard.CodeEditor.psd1' is invalid. Try again after updating this entry with valid values. (1597affb)
2021-09-22T14:32:15.8881577-04:00  [DBG] Dashboard component already exists. (4cfe4bd7)
2021-09-22T14:32:15.8881674-04:00  [DBG] Loading component in: D:\Universal\UniversalDashboard\Components\UniversalDashboard.Map (1fd7a997)
2021-09-22T14:32:15.9347091-04:00  [DBG] Agent Output: 1c1bb340-93fc-4404-bebe-c339dcb9dfa2 (b23ade37)
2021-09-22T14:32:15.9904051-04:00  [DBG] Agent Output: t0k1poE8p__hJu1RLi6~HIGlk816-1bai8 (9bdb7c30)
2021-09-22T14:32:16.0192727-04:00  [DBG] The specified RequiredModules entry 'UniversalDashboard' in the module manifest 'D:\Universal\UniversalDashboard\Components\UniversalDashboard.Map\UniversalDashboard.Map.psd1' is invalid. Try again after updating this entry with valid values. (7498ba48)
2021-09-22T14:32:16.0202668-04:00  [DBG] Dashboard component already exists. (4cfe4bd7)
2021-09-22T14:32:16.0202763-04:00  [DBG] Loading component in: D:\Universal\UniversalDashboard\Components\UniversalDashboard.Style (afcda995)
2021-09-22T14:32:16.0349212-04:00  [DBG] Agent Output: 644ee707-2edc-41d2-959a-887310d2fe2a (f99fa833)

That is weird. Seems like some sort of timing issue. I’ve seen those errors about loading modules before but it hasn’t prevented my dashboards from starting.

This is gonna sound weird but what’s in your dashboards.ps1? Is the production dashboard listed first? I’m just wonder if its trying to start that one before something is available and then when it gets to the dev dashboards the stuff is available.

Production dashboard is listed first and uses a few modules.

AzureAD, Microsoft.Graph.Intune and Active Directory

I’ve attached the full dashboard script, just editing a couple of things. I am using some variables I’ve saved in Universal for the Intune Graph API.

$Navigation = @(
    New-UDListItem -Label "Home" -OnClick { Invoke-UDRedirect '/Home' }
    New-UDListItem -Label "Jira Tickets" -OnClick { Invoke-UDRedirect -URL https://google.-Compress -OpenInNewWindow }
    New-UDListItem -Label "LAPS Lookup" -OnClick { Invoke-UDRedirect '/LAPS-Lookup' }
    New-UDListItem -Label "Bitlocker Recovery Key" -OnClick { Invoke-UDRedirect '/Bitlocker-Recovery-Key' }
    New-UDListItem -Label "Computer Inventory Details" -OnClick { Invoke-UDRedirect '/Computer-Inventory-Details' }
    New-UDListItem -Label "Intune" -Children {
        New-UDList -Children {
            New-UDListItem -Label "Primary User" -OnClick { Invoke-UDRedirect '/Change-Intune-Primary-User' }

        }
    }
)


$Pages = @()

$Pages += New-UDPage -Name 'Home' -Content {
    New-UDTypography -Text "V1.0`r"
    New-UDTypography -Text 'More to come!  Send feedback to .'
} -NavigationLayout permanent -Navigation $Navigation

$Pages += New-UDPage -Name 'LAPS Lookup' -Content {
    New-UDElement -id 'dynProgress' -tag 'div'
    New-UDForm -Content {
        New-UDTextBox -ID 'txtDeviceName' -Label 'Computer Name'
    } -OnValidate {
        $FormContent = $EventData
        If ($FormContent.txtDeviceName -eq $null) {
            New-UDFormValidationResult -ValidationError "Device Name blank"
        }
        Else {
            New-UDFormValidationResult -Valid
        }
    } -OnSubmit {
        Set-UDElement -id 'tblResult' -content {  }
        $DeviceName = $EventData.txtDeviceName
        $tenant = $AzureTenant
        $authority = "https://login.windows.net/$tenant"
        $clientId = $IntuneGraphAPIClientID
        $clientSecret = $IntuneGraphAPIClientSecret
        Update-MSGraphEnvironment -AppId $clientId -Quiet
        Update-MSGraphEnvironment -AuthUrl $authority -Quiet
        $Session:Output = 'Connecting to MS Graph'
        Sync-UDElement -Id 'output'
        Try {
            Connect-MSGraph -ClientSecret $ClientSecret -Quiet
            $Session:Output = 'Connected to MS Graph'
            Sync-UDElement -Id 'output'
        }
        Catch {
            $Session:Output = 'Failed to Connect to MS Graph'
            Sync-UDElement -Id 'output'
        }
        $graphApiVersion = "Beta"
        $graphUrl = "https://graph.microsoft.com/$graphApiVersion"

        $Session:Output = 'Querying LAPS Password from Intune'
        Sync-UDElement -Id 'output'

        $result = (Invoke-MSGraphRequest -Url "$graphUrl/deviceManagement/deviceHealthScripts" -HttpMethod GET).value | Where-Object { $_.DisplayName -eq "Intune Lean LAPS" }

        $script = (Invoke-MSGraphRequest -Url ("$graphUrl/deviceManagement/deviceHealthScripts/$($Result.id)/deviceRunStates/" + '?$expand=*') -HttpMethod GET).value

        $Obj = $script | Where-Object { $_.ManagedDevice.deviceName -eq $DeviceName } | Sort-Object LasteStateUpdateDateTime | Select-Object -First 1

        $LAPS = $Obj.postRemediationDetectionScriptOutput
        $LAPS = $laps.Substring($Laps.IndexOf('password:') + 10, ( $laps.IndexOf(' for ITAdmi') - ($Laps.IndexOf('password:') + 10)))
        If ($LAPS -eq $null -or $LAPS -eq "") {
            Set-UDElement -id 'tblResult' -content {
                New-UDCard -Content { "$($EventData.txtDeviceName) not found in Intune." }
            }
        }
        Else {
            $Data = @(
                @{' ' = 'Device Name'; '  ' = $EventData.txtDeviceName }
                @{' ' = 'LAPS Password'; '  ' = $LAPS }
            )
            Set-UDElement -Id 'txtDeviceName' -Content { "$($EventData.txtDeviceName)" }
            Set-UDElement -id 'tblResult' -content {
                New-UDTable -Data $Data
            }
        }
    } -OnProcessing {
        $Session:Output = 'Loading'
        New-UDDynamic -ID 'output' -Content {
            New-UDTypography -text  $Session:Output
        }
        New-UDProgress
    }

    New-UDElement -id 'tblResult' -tag 'div'
} -NavigationLayout permanent -Navigation $Navigation


$Pages += New-UDPage -Name 'Computer Inventory Details' -Content {
    New-UDForm -Content {
        New-UDTextBox -ID 'txtDeviceName' -Label 'Computer Name'
    } -OnValidate {
        $FormContent = $EventData
        If ($FormContent.txtDeviceName -eq $null) {
            New-UDFormValidationResult -ValidationError "Device Name blank"
        }
        Else {
            New-UDFormValidationResult -Valid
        }
    } -OnSubmit {
        try {
            $ComputerName = $EventData.txtDeviceName
            New-UDTypography -Text $Computername
            $File = Get-ChildItem -Path D:\ComputerInventory -Filter "*$ComputerName.json"
            $Data = Get-Content $file.Fullname -raw | ConvertFrom-Json
            $Hardware = $Data.Hardware | Get-Member -MemberType NoteProperty
            $BIOS = $Data.'BIOS Settings' | Get-Member -MemberType NoteProperty
            New-UDTabs -Tabs {
                New-UDTab -Text 'Hardware' -Content { 
                    If ($Data.Hardware.ComputerSystem) {
                        $PropData = @()
                        $PropData = @(
                            @{' ' = 'Name'; '  ' = $data.hardware.computersystem.name }
                            @{' ' = 'Model'; '  ' = $data.hardware.computersystem.model }
                            @{' ' = 'Manufacturer'; '  ' = $data.hardware.computersystem.Manufacturer }
                            @{' ' = 'Total Memory'; '  ' = $data.hardware.computersystem.TotalPhysicalMemory }
                        )
                        $Columns = @(
                            New-UDTableColumn -Property " " -Width 10
                            New-UDTableColumn -Property "  " -Width 10  
                        )
                        New-UDGrid -Item -ExtraSmallSize 3 -Content {
                            New-UDTable -Title "Computer System" -Data $Propdata -Columns $Columns
                        }
                    }
                    If ($Data.Hardware.BIOS) {
                        $PropData = @()
                        $PropData = @(
                            @{' ' = 'Version'; '  ' = $data.hardware.bios.name }
                            @{' ' = 'Manufacturer'; '  ' = $data.hardware.bios.Manufacturer }
                            @{' ' = 'Serial Number'; '  ' = $data.hardware.bios.SerialNumber }
                        )
                        $Columns = @(
                            New-UDTableColumn -Property " " -Width 10
                            New-UDTableColumn -Property "  " -Width 10  
                        )
                        New-UDGrid -Item -ExtraSmallSize 3 -Content {
                            New-UDTable -Title "BIOS" -Data $Propdata -Columns $Columns
                        }
                    }
                    If ($Data.Hardware.DiskDrive) {
                        $PropData = @()
                        $PropData = @(
                            @{' ' = 'Model'; '  ' = $data.hardware.DiskDrive.Model }
                            @{' ' = 'DeviceID'; '  ' = $data.hardware.DiskDrive.DeviceID }
                            @{' ' = 'Partitions'; '  ' = $data.hardware.DiskDrive.Partitions }
                            @{' ' = 'Size'; '  ' = $data.hardware.DiskDrive.Size }
                        )
                        $Columns = @(
                            New-UDTableColumn -Property " " -Width 10
                            New-UDTableColumn -Property "  " -Width 10  
                        )
                        New-UDGrid -Item -ExtraSmallSize 3 -Content {
                            New-UDTable -Title "Disk Drive" -Data $Propdata -Columns $Columns
                        }
                    }
                    If ($Data.Hardware.LogicalDisk) {
                        $PropData = @()
                        $PropData = @(
                            @{' ' = 'VolumeName'; '  ' = $data.hardware.LogicalDisk.VolumeName }
                            @{' ' = 'DeviceID'; '  ' = $data.hardware.LogicalDisk.DeviceID }
                            @{' ' = 'DriveType'; '  ' = $data.hardware.LogicalDisk.DriveType }
                            @{' ' = 'Size'; '  ' = $data.hardware.LogicalDisk.Size }
                            @{' ' = 'FreeSpace'; '  ' = $data.hardware.LogicalDisk.FreeSpace }
                        )
                        $Columns = @(
                            New-UDTableColumn -Property " " -Width 10
                            New-UDTableColumn -Property "  " -Width 10  
                        )
                        New-UDGrid -Item -ExtraSmallSize 3 -Content {
                            New-UDTable -Title "Logical Disk" -Data $Propdata -Columns $Columns
                        }
                    }
                    If ($Data.Hardware.Memory) {
                        $Columns = @(
                            New-UDTableColumn -Property "Bank" -Title "Bank" -Width 10
                            New-UDTableColumn -Property "PartNumber" -Title "PartNumber" -Width 10
                            New-UDTableColumn -Property "Capacity" -Title "Capacity" -Width 10
                            New-UDTableColumn -Property "Speed" -Title "Speed" -Width 10
                        )
                        New-UDGrid -Item -ExtraSmallSize 3 -Content {
                            New-UDTable -Title Memory -Data $data.hardware.memory
                        }
                    }
                    If ($Data.Hardware.Network) {
                        New-UDGrid -Item -ExtraSmallSize 3 -Content {
                            New-UDTable -Title Network -Data $data.hardware.network
                        }
                    }
                    If ($Data.Hardware.OperatingSystem) {
                        $PropData = @()
                        $PropData = @(
                            @{' ' = 'Caption'; '  ' = $data.hardware.OperatingSystem.Caption }
                            @{' ' = 'Version'; '  ' = $data.hardware.OperatingSystem.Version }
                            @{' ' = 'InstallDate'; '  ' = $data.hardware.OperatingSystem.InstallDate }
                            @{' ' = 'OSArchitecture'; '  ' = $data.hardware.OperatingSystem.OSArchitecture }
                        )
                        $Columns = @(
                            New-UDTableColumn -Property " " -Width 10
                            New-UDTableColumn -Property "  " -Width 10  
                        )
                        New-UDGrid -Item -ExtraSmallSize 3 -Content {
                            New-UDTable -Title "Operating System" -Data $PropData -Columns $Columns
                        }
                    }
                }
                New-UDTab -Text 'Applications' -Content { 
                    $Columns = @(
                        New-UDTableColumn -Property "ProductName" -Title "Product Name" -IncludeInSearch
                        New-UDTableColumn -Property "Publisher" -Title "Publisher" -IncludeInSearch
                        New-UDTableColumn -Property "VersionString" -Title "VersionString"
                        New-UDTableColumn -Property "InstallDate" -Title "Install Date"
                        New-UDTableColumn -Property "InstallLocation" -Title "Install Location"
                        New-UDTableColumn -Property "LocalPackage" -Title "Local Package"
                    )
                    New-UDTable -Title "Applications" -Data $Data.Applications -Columns $Columns -ShowSearch
                }
                New-UDTab -Text 'Executable Files' -Content { 
                    $Columns = @(
                        New-UDTableColumn -Property "Name" -Title "Name" -IncludeInSearch
                        New-UDTableColumn -Property "Description" -Title "Description" -IncludeInSearch
                        New-UDTableColumn -Property "Company Name" -Title "Publisher" -IncludeInSearch
                        New-UDTableColumn -Property "FileVersion" -Title "Version"
                        New-UDTableColumn -Property "Path" -Title "Path"
                        New-UDTableColumn -Property "FileName" -Title "FileName"
                    )
                    New-UDTable -Title "Executable Files" -Data $Data.exeFiles -Columns $Columns -ShowSearch
                }
                New-UDTab -Text 'Users' -Content { 
                    $Columns = @(
                        New-UDTableColumn -Property "Name" -Title "Name"
                        New-UDTableColumn -Property "LastLogon" -Title "Last Logon"
                    )
                    New-UDTable -Title "Users" -Data $Data.Users -Columns $Columns
                }
                New-UDTab -Text 'Bios Settings' -Content { 
                    $Columns = @(
                        New-UDTableColumn -Property "Attribute" -Width 15
                        New-UDTableColumn -Property "CurrentValue" -Width 15
                        New-UDTableColumn -Property "ShortDescription" -Width 15
                        New-UDTableColumn -Property "Description" -Width 25
                    )
                    $BIOS.name | Foreach {
                        $Name = $_
                        $BiosItem = $data.'BIOS Settings'.$name
                        $PropData = @()
                        $BiosItem | Foreach {
                            $PropData += @(
                                @{'Attribute' = $_.Attribute; 'CurrentValue' = $_.CurrentValue; 'ShortDescription' = $_.ShortDescription; 'Description' = $_.Description }
                            )
                        }
                        New-UDTable -Title $Name -Data $PropData -Columns $Columns
                    } 
                }
            }
    
        }
        catch {
            
        }
        
    }
} -NavigationLayout permanent -Navigation $Navigation

$Pages += New-UDPage -Name 'Bitlocker Recovery Key' -Content {
    New-UDForm -Content {
        New-UDTextBox -ID 'txtDeviceName' -Label 'Computer Name'
    } -OnValidate {
        $FormContent = $EventData
        If ($FormContent.txtDeviceName -eq $null) {
            New-UDFormValidationResult -ValidationError "Device Name blank"
        }
        Else {
            New-UDFormValidationResult -Valid
        }
    } -OnSubmit {
        try {
            $ADComputer = Get-ADComputer -identity $Eventdata.txtDeviceName -ErrorAction Stop
            $BitLockerObjects = (Get-ADObject -Filter {objectclass -eq 'msFVE-RecoveryInformation'} -SearchBase $ADComputer.DistinguishedName -Properties 'msFVE-RecoveryPassword').'msFVE-RecoveryPassword'

            $Data = @(
                @{' ' = 'Device Name'; '  ' = $Eventdata.txtDeviceName }
                @{' ' = 'Bitlocker Key'; '  ' = $BitLockerObjects }
            )
            
            Set-UDElement -id 'tblResult' -Content {
                New-UDTable -Data $Data
            }
    
        }
        catch {
            
        }
        
    }
    New-UDElement -id 'tblResult' -tag 'div'
} -NavigationLayout permanent -Navigation $Navigation


$Pages += New-UDPage -Name 'Change Intune Primary User' -Content {
    New-UDForm -Content {
        New-UDTextBox -ID 'txtDeviceName' -Label 'Computer Name'
    } -OnValidate {
        $FormContent = $EventData
        If ($FormContent.txtDeviceName -eq $null) {
            New-UDFormValidationResult -ValidationError "Device Name blank"
        }
        Else {
            New-UDFormValidationResult -Valid
        }
    } -OnSubmit {
        $DeviceName = $EventData.txtDeviceName
        $tenant = $AzureTenant
        $authority = "https://login.windows.net/$tenant"
        $clientId = $IntuneGraphAPIClientID
        $clientSecret = $IntuneGraphAPIClientSecret
        Update-MSGraphEnvironment -AppId $clientId -Quiet
        Update-MSGraphEnvironment -AuthUrl $authority -Quiet
        $Session:Output = 'Connecting to MS Graph'
        Sync-UDElement -Id 'output'
        Try {
            Connect-MSGraph -ClientSecret $ClientSecret -Quiet
            $Session:Output = 'Connected to MS Graph'
            Sync-UDElement -Id 'output'
        }
        Catch {
            $Session:Output = 'Failed to Connect to MS Graph'
            Sync-UDElement -Id 'output'
        }
        $graphApiVersion = "Beta"
        $graphUrl = "https://graph.microsoft.com/$graphApiVersion/"

        $Device = Get-IntuneManagedDevice | Where-Object { $_.devicename -eq $DeviceName }
        if ($Device) {
            $PrimaryUser = (Invoke-MSGraphRequest -URL "$graphUrl/deviceManagement/managedDevices/$($Device.id)/users" -HttpMethod GET ).value
        
            $Data = @(
                @{' ' = 'Device Name'; '  ' = $Devicename }
                @{' ' = 'User Name'; '  ' = $PrimaryUser.displayname }
                @{' ' = 'UPN'; '  ' = $PrimaryUser.userPrincipalName }
            ) 
    
            Set-UDElement -id 'tblData' -Content {
                New-UDTable -Data $Data
            }
    
            Set-UDElement -Id 'frmUser' -Content {
                New-UDForm -Content {
                    New-UDTypography -Text 'Enter user UPN to reassign device'
                    New-UDTextBox -ID 'txtUPN'
                } -OnValidate {
                    $FormContent = $EventData
                    If ($FormContent.txtUPN -eq $null) {
                        New-UDFormValidationResult -ValidationError "UPN blank"
                    }
                    Else {
                        New-UDFormValidationResult -Valid
                    }
                } -OnSubmit {
                    $UPN = $EventData.txtUPN
                    try {
                        $UserObj = (Invoke-MSGraphRequest -URL "$graphurl/users/$UPN")
                    }
                    catch {
                            
                    }
                    If ($UserObj) {
                        $Resource = "deviceManagement/managedDevices('$($device.id)')/users/`$ref"
                            
                        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
                
                        $userUri = "https://graph.microsoft.com/$graphApiVersion/users/" + $UserObj.id
                        $id = "@odata.id"
                        $JSON = @{ $id = "$userUri" } | ConvertTo-Json -Compress
                        Invoke-MSGraphRequest -Url $uri -HttpMethod POST -Content $JSON.tostring() -Verbose
                        $OldUser = $PrimaryUser.userPrincipalName
                        $PrimaryUser = (Invoke-MSGraphRequest -URL "$graphUrl/deviceManagement/managedDevices/$($Device.id)/users" -HttpMethod GET ).value
                        
                        $Time = Get-Date
                        $Logitem = [PSCustomObject]@{TimeStamp = $Time.ToString() ; User = $User ; Action = "Change Primary User" ; Target = "$DeviceName FROM: $OldUser   TO: $($PrimaryUser.userPrincipalName)" }
                        $R = Get-Random -Minimum 1 -Maximum 100
                        $Logitem | ConvertTo-Json | Out-File D:\Logging\Inbound\$($Time.tostring("yyyyMMddHHmmss"))$R.json
                        $Logstring = "[$($LogItem.TimeStamp)]   $($LogItem.User)   $($LogItem.Action)   $($LogItem.Target)"
                        do {
                            $Logstring | Out-file D:\Logging\HelpDeskDash.log -append -ErrorAction Stop
                            $C = $true
                        } until ($C -eq $true)

                        $Data = @(
                            @{' ' = 'Device Name'; '  ' = $Devicename }
                            @{' ' = 'User Name'; '  ' = $PrimaryUser.displayname }
                            @{' ' = 'UPN'; '  ' = $PrimaryUser.userPrincipalName }
                        )
    
                        Set-UDElement -id 'tblData' -Content {
                            New-UDTable -Data $Data
                        }
                
                    }
                }
            }
        }
        Else {

            Set-UDElement -id 'tblData' -Content {
                New-UDCard -Content { "Device: $Devicename not found." }
            }
            Set-UDElement -d 'frmUser' -Content {}
        }


    } <#-OnProcessing {
        $Session:Output = 'Loading'
        New-UDDynamic -ID 'output' -Content {
            New-UDTypography -text $Session:Output
        }
        New-UDProgress
    }#>

    New-UDElement  -id 'tblData' -tag 'div'

    New-UDElement  -id 'frmUser' -tag 'div'

} -NavigationLayout permanent -Navigation $Navigation



New-UDDashboard -Pages $Pages -Title 'Helpdesk Dashboard'

I wonder if you move the prod dashboard to the bottom, it will start since it will try to start the dev ones first.