Windows Service Dashboard not Populating Data

The below is saved in a dashboard.ps1, but when I go to publish it, while publishing successfully, the $data is empty (since it takes about 10 minutes to run through this). Will it work after 10 minutes? Any recommendations around manging the information?

function Get-ADServers {
[CmdletBinding()]
param (
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential
)

begin {

    $CgetADComputerSplat = @{
        Filter     = {OperatingSystem -like "*Windows Server*"}
        Properties = 'Name', 'Enabled', 'DNSHostName', 'Description', 'CanonicalName', 'OperatingSystem', 'ManagedBy'
        Server     = "contoso.com"
        Credential = $Credential
    }
    $UgetADComputerSplat = @{
        Filter     = {OperatingSystem -like "*Windows Server*"}
        Properties = 'Name', 'Enabled', 'DNSHostName', 'Description', 'CanonicalName', 'OperatingSystem', 'ManagedBy'
        Server     = "dsdc008.contoso.com"
        Credential = $Credential
    }
    $PgetADComputerSplat = @{
        Filter     = {OperatingSystem -like "*Windows Server*"}
        Properties = 'Name', 'Enabled', 'DNSHostName', 'Description', 'CanonicalName', 'OperatingSystem', 'ManagedBy'
        Server     = "rcpm-pwv-adds01.contoso.com"
        Credential = $Credential
    }
    $PCgetADComputerSplat = @{
        Filter     = {OperatingSystem -like "*Windows Server*"}
        Properties = 'Name', 'Enabled', 'DNSHostName', 'Description', 'CanonicalName', 'OperatingSystem', 'ManagedBy'
        Server     = "rcpc-pwv-adds02.contoso.com"
        Credential = $Credential
    }
}

process {
    $CMGMIResults = Get-ADComputer @CgetADComputerSplat

    $UGCNTResults = Get-ADComputer @UgetADComputerSplat

    $PMIResults = Get-ADComputer @PgetADComputerSplat

    $PMICorpResults = Get-ADComputer @PCgetADComputerSplat

    $Servers = $CMGMIResults + $UGCNTResults + $PMIResults + $PMICorpResults

    Write-Host "[INFO] Building AD Server inventory" -ForegroundColor Cyan
    $output = foreach ($Server in $Servers) {
        [PSCustomObject]@{
            Name                    = $server.Name
            vCenterName             = $null
            FQDN                    = $server.DNSHostName
            Description             = $server.Description
            DescriptionInvCenter    = $null
            IPAddress               = $null
            IPAddress2              = $null
            DNS                     = $server.DNSHostName
            OperatingSystem         = $server.OperatingSystem
            vCenter                 = $null
            vCenterStatus           = $null
            CriticalVulnerabilities = $null
            BusinessOwner           = $null
            Applications            = $null
            Environment             = $null
            Enabled                 = $server.Enabled
            PatchGroup              = $null
            OrionStatus             = $null
        }
    }
}

end {
    $output
}

}

function Get-VMServers {
[CmdletBinding()]
param (
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential
)

begin {
    $Output = [System.Collections.Generic.List[psobject]]::new()
    $VDIRange = "10.4.170|10.4.172|10.4.174|10.4.176|10.4.151|10.4.156|10.4.157|10.4.158|10.4.159|10.4.160|10.4.161|10.89.196|10.89.197|10.89.198|10.89.199"

    $vCenters = @(
        "AMI-PAVVCTR001.contoso.com"
        "AMI-NAVVCTR001.contoso.com"
        "AMI-PAVVCTR002.contoso.com"
        "AMI-PAVVCTR003.contoso.com"
        #"AMI-RAVVCTR003.contoso.com"
        "AMI-PAVVCTR005.contoso.com"
        #"AMI-RAVVCTR005.contoso.com"
        "AMI-PAVVCTR006.contoso.com"
        #"AMI-RAVVCTR006.contoso.com"
        #"VMWVCS501.contoso.com"
        #"VMWVCS901.contoso.com"
    )

    Connect-VIServer -Server $vCenters -Credential $Credential -ErrorAction SilentlyContinue

}

process {
    $VirtualMachines = (Get-VM).where( {$PSItem.guest.ipaddress -notmatch $VDIRange -and $psitem.guestid -notmatch "rhel"})

    foreach ($Server in $VirtualMachines) {

        if ($server.guest.ipaddress -is [array]) {
            $primaryip = $server.guest.ipaddress[0]
            $secondaryip = $server.guest.ipaddress[1]
        }
        else {
            $primaryip = $server.guest.ipaddress
            $secondaryip = $null
        }

        $results = [PSCustomObject]@{
            Name                    = $null
            NameInvCenter           = $server.name
            FQDN                    = $null
            Description             = $null
            DescriptionInvCenter    = $server.notes
            IPAddress               = $primaryip
            IPAddress2              = $secondaryip
            DNS                     = $null
            OperatingSystem         = $null
            vCenter                 = $server.Uid.Substring($server.Uid.IndexOf('@') + 1).Split(":")[0]
            vCenterStatus           = $server.powerstate
            CriticalVulnerabilities = $null
            BusinessOwner           = $null
            Applications            = $null
            Environment             = $null
            Enabled                 = $null
            PatchGroup              = $null
            OrionStatus             = $null
        }
        $output.Add($results)
    }
}

end {
    $output
}

}

function Get-OrionServers {
[CmdletBinding()]
param (
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential
)

begin {

    $orionhost = "orion.contoso.com"
    $swis = Connect-Swis -Hostname $orionhost -Credential $Credential
    $s = Get-SwisData $swis 'SELECT NodeID, Caption FROM Orion.Nodes'

    <#
            ## Custom properties for AMI-SWVBILDAP03
    $b = Get-SwisObject $swis -Uri 'swis://localhost/Orion/Orion.Nodes/NodeID=4043'
    $c = Get-SwisObject $swis -Uri 'swis://localhost/Orion/Orion.Nodes/NodeID=4043/customproperties'

    Import-Module $ENV:USerProfile\PSArchInventory\PSArchInventory\PSArchInventory.psm1

    ## patchgroup
    Write-host "Patch group is [$($c.patchgroup)]"
    Write-Host "DNS is [$($b.dns)]"
    Write-host "NodeID is [$($b.NodeID)]"
    Write-host "Caption (or name) is [$($b.caption)]"
    URL: https://orion.contoso.com/Orion/NetPerfMon/NodeDetails.aspx?NetObject=N:4051
    #>
}

process {

    $output = foreach ($server in $s) {
        $server = Get-SwisObject $swis -Uri "swis://localhost/Orion/Orion.Nodes/NodeID=$($Server.nodeID)"
        $servercustomproperties = Get-SwisObject $swis -Uri "swis://localhost/Orion/Orion.Nodes/NodeID=$($server.nodeID)/customproperties"

        if ($server.nodeID) {
            $orionstatus = "Enabled"
        }

        [PSCustomObject]@{
            Name                    = $server.caption
            vCenterName             = $null
            FQDN                    = $null
            Description             = $null
            DescriptionInvCenter    = $null
            IPAddress               = $null
            IPAddress2              = $null
            DNS                     = $null
            OperatingSystem         = $null
            vCenter                 = $null
            vCenterStatus           = $null
            CriticalVulnerabilities = $null
            BusinessOwner           = $servercustomproperties.BusinessOwner
            Applications            = $null
            Environment             = $servercustomproperties.Environment
            Enabled                 = $null
            PatchGroup              = $servercustomproperties.patchgroup
            OrionStatus             = $orionstatus
            NodeID                  = $server.nodeID
        }
    }
}

end {
    $output
}

}

function Get-TenableSeverity {

[CmdletBinding()]
param (
    [Parameter(Position = 0, Mandatory = $true)]
    [ValidateSet(
        'Critical',
        'High',
        'Medium',
        'Low'
    )]
    [string]
    $Severity,

    [ValidateNotNull()]
    [System.Management.Automation.PSCredential]
    [System.Management.Automation.Credential()]
    $Credential
)

begin {

    switch ($Severity) {
        "Critical" {$ID = "4"}
        "High" {$ID = "3"}
        "Medium" {$ID = "2"}
        "Low" {$ID = "1"}
    }

    # Disable SSL certificate validation.
    [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }

    # Credentials
    $APICredential = @{
        username       = $Credential.UserName
        password       = $Credential.GetNetworkCredential().Password
        releaseSession = "FALSE"
    }

    $SessionSplat = @{
        URI             = "https://tenable.contoso.com/rest/token"
        SessionVariable = "SCSession"
        Method          = "Post"
        ContentType     = "application/json"
        Body            = (ConvertTo-Json $APICredential)
    }

    try {
        $Session = Invoke-RestMethod @SessionSplat
    }
    catch {
        Write-Host "ERROR: Unable to login to Tenable." -ForegroundColor Red
        Write-Host $_.Exception.Message
        Write-Host $_.Exception.ItemName
        return
    }

    ## Token
    $token = $Session.response.token
}

process {

    $query = @{
        "tool"       = "vulnipdetail"
        "sortField"  = "cveID"
        "sortDir"    = "ASC"
        "type"       = "vuln"
        "sourceType" = "cumulative"
        "query"      = @{
            "name"         = ""
            "description"  = ""
            "context"      = ""
            "status"       = "-1"
            "createdTime"  = 0
            "modifiedtime" = 0
            "sourceType"   = "cumulative"
            "sortDir"      = "desc"
            "tool"         = "listvuln"
            "groups"       = "[]"
            "type"         = "vuln"
            "startOffset"  = 0
            "endOffset"    = 5000
            "filters"      = [array]@{
                "id"           = "severity"
                "filterName"   = "severity"
                "operator"     = "="
                "type"         = "vuln"
                "ispredefined" = $true
                "value"        = "$ID"
            }
            "vulntool"     = "listvuln"
            "sortField"    = "severity"
        }
    }

    $body = ConvertTo-Json ($query) -depth 5

    $splat = @{
        URI        = "https://tenable.contoso.com/rest/analysis"
        Method     = "POST"
        WebSession = $SCSession
        Headers    = @{"X-SecurityCenter" = "$Token"}
        Body       = $body
    }

    $Output = Invoke-RestMethod @splat
}

end {
    $Output.response.results
}

}

function Publish-Report {
[CmdletBinding()]
param (
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential,

    [psobject]
    $ADServers,

    [psobject]
    $VMServers,

    [psobject]
    $OrionServers,

    [psobject]
    $TenableHighs,

    [psobject]
    $TenableCriticals
)

begin {
    $output = [System.Collections.Generic.List[psobject]]::new()

    foreach ($server in $ADServers) {
        Write-host "Processing $($Server.Name)"
        $vcenterdetails = $VMServers | Where-Object {$psitem.nameinvcenter -eq $server.name}
        $oriondetails = $OrionServers | Where-Object {$psitem.name -eq $server.name}

        $criticals = $TenableCriticals | Where-Object {$psitem.dnsName -eq $server.dns}
        $highs = $TenableHighs | Where-Object {$psitem.dnsName -eq $server.dns}

        $object = [PSCustomObject]@{
            Name                    = $server.Name
            vCenterName             = $vcenterdetails.vCenterName
            Description             = $server.Description
            vCenterDescription      = $vcenterdetails.DescriptionInvCenter
            Enabled                 = $server.Enabled
            FQDN                    = $server.DNS
            DNS                     = $server.DNS
            OperatingSystem         = $server.OperatingSystem
            Environment             = $oriondetails.environment
            PatchGroup              = $oriondetails.PatchGroup
            OrionStatus             = $oriondetails.OrionStatus
            BusinessOwner           = $oriondetails.BusinessOwner
            Link                    = "https://orion.contoso.com/Orion/NetPerfMon/NodeDetails.aspx?NetObject=N:$($oriondetails.nodeid)"
            IPAddress               = $vcenterdetails.IPAddress
            IPAddress2              = $vcenterdetails.IPAddress2
            vCenter                 = $vcenterdetails.vCenter
            vCenterStatus           = $vcenterdetails.vCenterStatus
            CriticalVulnerabilities = $null
            Applications            = $null
            Criticals               = $criticals.count
            Highs                   = $highs.count
        }
        $output.Add($object)

    }

}

process {
}

end {
    $output
}

}

$null = Set-PowerCLIConfiguration -ProxyPolicy NoProxy -Confirm:$false
$null = Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false

$Credential = BetterCredentials\Get-Credential -UserName “svcPSMon” -Store
$VMwareCredential = BetterCredentials\Get-Credential “CMGMI\svcPSMon” -Store

$ADServers = Get-ADServers -Credential $Credential
$VMServers = Get-VMServers -Credential $VMwareCredential
$OrionServers = Get-OrionServers -Credential $Credential
$TenableHighs = Get-TenableSeverity -Severity “High” -Credential $Credential
$TenableCriticals = Get-TenableSeverity -Severity “Critical” -Credential $Credential

$reportsplat = @{
Credential = $Credential
ADServers = $ADServers
OrionServers = $OrionServers
TenableHighs = $TenableHighs
TenableCriticals = $TenableCriticals
VMServers = $VMServers
}

$data = Publish-Report @reportsplat

Get-UDDashboard | Stop-UDDashboard

$Dashboard = New-UDDashboard -Title “Windows Servers” -Content {

New-UdGrid -Title "Active Directory" -Endpoint {
    $data | Select-Object Name, Description, DNS, OperatingSystem, Enabled | Out-UDGridData
}

New-UdGrid -Title "vCenter" -Endpoint {
    $data | Select-Object Name, vCenterDescription, IpAddress, vcenter | Out-UDGridData
}

New-UdGrid -Title "Orion" -Endpoint {
    $data | Select-Object Name, Environment, PatchGroup, OrionStatus, BusinessOwner | Out-UDGridData
}

New-UdGrid -Title "Tenable" -Endpoint {
    $data | Select-Object Name, Criticals, Highs | Out-UDGridData
}

}

Start-UDDashboard -Dashboard $Dashboard -Port 12345

Welcome @jwmoss thanks for posting your question. It’s late where I am and I don’t have my glasses on but from what I see is you are missing https://docs.universaldashboard.io/endpoints/endpoint-initialization#passing-functions-to-endpoints
I cannot see the New-UDEndpointInitialization command in your script. I did do a simple demo here
https://github.com/psDevUK/UDscripts/blob/master/EndPointDemo.ps1
I hope this help clarify why you not getting data. If you need further assistance just ask. Thanks

Perfect! I’ll give that a shot. Thanks for your reply @psDevUK.

No worries @jwmoss hope I make your xmas card list when you see this is what your missing :smile: also as @adam cares about his customers/end-users he wrote a very helpful article which he posted right here Dude, where's my variable? Understanding scoping in Universal Dashboard
It’s nearly midnight where I’m at but happy to script up an example tomorrow if you still having issues. Peace and happy dashboarding

So I got much farther, but didn’t have to use this feature quite yet (passing functions/variables to UD). I’d like to take advantage of $cache variables and scheduling, but not sure of the best way.

Would I put everything above Get-UDDashboard | Stop-UDDashboard into the $Dashboard scriptblock? If I do, that means I’d need to add the following, right? :

New-UDEndpointInitialization -Module @(“ActiveDirectory”,“BetterCredentials”,“VMware.PowerCLI”,".\functions.psm1") -Variable “Data”

But even with Data, how do I convert that to a $cache variable and not a regular variable that I’m passing into the scriptblock?

Dude I remember trying to get my head around the cache variable and how I could use it. I have an example here


Where I set the schedule every 60 seconds then set that schedule to an endpoint where I include my function, and write a sql query. Then run that query but store it in a cache variable. Then I can reuse that cache variable anywhere in the script. Hopefully the example I posted makes sense on how to define and use a cache variable