Pages/Static/HomePage.ps1
#region >> Create Home Page $HomePageContent = { $PUDRSSyncHT = $global:PUDRSSyncHT # Define some Cache: variables that we'll be using in a lot of different contexts $Cache:ThisModuleFunctionsStringArray = $ThisModuleFunctionsStringArray = $(Get-Module PUDAdminCenterPrototype).Invoke({$FunctionsForSBUse}) $Cache:DynamicPages = $DynamicPages = @( "PSRemotingCreds" "ToolSelect" "Overview" "Certificates" "Devices" "Events" "Files" "Firewall" "Users And Groups" "Network" "Processes" "Registry" "Roles And Features" "Scheduled Tasks" "Services" "Storage" "Updates" ) # Load PUDAdminCenter Module Functions Within ScriptBlock $ThisModuleFunctionsStringArray | Where-Object {$_ -ne $null} | foreach {Invoke-Expression $_ -ErrorAction SilentlyContinue} #region >> Loading Indicator New-UDRow -Columns { New-UDColumn -Endpoint { $Cache:RHostRefreshAlreadyRan = $False $Session:HomePageLoadingTracker = $False $Session:SearchRemoteHosts = $False } New-UDHeading -Text "Home" -Size 4 } New-UDRow -Columns { New-UDColumn -AutoRefresh -RefreshInterval 1 -Endpoint { if (!$Session:HomePageLoadingTracker) { New-UDHeading -Text "Loading...Please wait..." -Size 5 New-UDPreloader -Size small } } } #endregion >> Loading Indicator #region >> HomePage Main Content New-UDRow -Endpoint { New-UDColumn -Endpoint { New-UDHeading -Text "General Network Scan" -Size 5 New-UDElement -Id "ScanNetwork" -Tag div -EndPoint { if ($Session:ScanNetwork) { New-UDHeading -Text "Scanning Network for RemoteHosts...Please wait..." -Size 6 New-UDPreloader -Size small } } } } New-UDRow -Endpoint { New-UDColumn -Endpoint { New-UDButton -Text "Scan Network" -OnClick { $Session:ScanNetwork = $True Sync-UDElement -Id "ScanNetwork" [System.Collections.ArrayList]$ScanRemoteHostListPrep = $(GetComputerObjectsInLDAP).Name # Let's just get 20 of them initially. We want *something* on the HomePage but we don't want hundreds/thousands of entries. We want # the user to specify individual/range of hosts/devices that they want to manage. #$ScanRemoteHostListPrep = $ScanRemoteHostListPrep[0..20] if ($PSVersionTable.PSEdition -eq "Core") { [System.Collections.ArrayList]$ScanRemoteHostListPrep = $ScanRemoteHostListPrep | foreach {$_ -replace "CN=",""} } # Filter Out the Remote Hosts that we can't resolve [System.Collections.ArrayList]$ScanRemoteHostList = @() $null = Clear-DnsClientCache foreach ($HName in $ScanRemoteHostListPrep) { try { $RemoteHostNetworkInfo = ResolveHost -HostNameOrIP $HName -ErrorAction Stop if ($ScanRemoteHostList.FQDN -notcontains $RemoteHostNetworkInfo.FQDN) { $null = $ScanRemoteHostList.Add($RemoteHostNetworkInfo) } } catch { continue } } $PUDRSSyncHT.RemoteHostList = $ScanRemoteHostList # Add Keys for each of the Remote Hosts in the $InitialRemoteHostList foreach ($RHost in $ScanRemoteHostList) { $Key = $RHost.HostName + "Info" if ($PUDRSSyncHT.Keys -notcontains $Key) { $Value = @{ NetworkInfo = $RHost CredHT = $null ServerInventoryStatic = $null RelevantNetworkInterfaces = $null LiveDataRSInfo = $null LiveDataTracker = @{Current = $null; Previous = $null} } foreach ($DynPage in $($DynamicPages | Where-Object {$_ -notmatch "PSRemotingCreds|ToolSelect"})) { $DynPageHT = @{ LiveDataRSInfo = $null LiveDataTracker = @{Current = $null; Previous = $null} } $Value.Add($($DynPage -replace "[\s]",""),$DynPageHT) } $PUDRSSyncHT.Add($Key,$Value) } } $Session:ScanNetwork = $False Sync-UDElement -Id "ScanNetwork" # Refresh the Main Content Sync-UDElement -Id "MainContent" } } } # RemoteHost / Device Search New-UDRow -Endpoint { New-UDColumn -Endpoint { New-UDHeading -Text "Find Specific Remote Hosts" -Size 5 New-UDElement -Id "SearchRemoteHosts" -Tag div -EndPoint { if ($Session:SearchRemoteHosts) { New-UDHeading -Text "Searching for RemoteHosts...Please wait..." -Size 6 New-UDPreloader -Size small } } } New-UDColumn -Size 12 -Endpoint { New-UDRow -Endpoint { New-UDColumn -Size 5 -Endpoint { New-UDTextbox -Id "HostNameOrFQDN" -Label "HostName_Or_FQDN" -Placeholder "Enter a HostName/FQDN, or comma-separated HostNames/FQDNs" } New-UDColumn -Size 5 -Endpoint { New-UDTextbox -Id "IPAddress" -Label "IPAddress" -Placeholder "Enter an IP, comma-separated IPs, a range of IPs using a '-', or a range of IPs using CIDR" } New-UDColumn -Size 2 -Endpoint { New-UDButton -Text "Search" -OnClick { $Session:SearchRemoteHosts = $True Sync-UDElement -Id "SearchRemoteHosts" $HostNameTextBox = Get-UDElement -Id "HostNameOrFQDN" $IPTextBox = Get-UDElement -Id "IPAddress" $HostNames = $HostNameTextBox.Attributes['value'] $IPAddresses = $IPTextBox.Attributes['value'] [System.Collections.ArrayList]$RemoteHostListPrep = @() if ($HostNames) { if ($HostNames -match [regex]::Escape(',')) { $HostNames -split [regex]::Escape(',') | foreach { if (![System.String]::IsNullOrWhiteSpace($_)) { $null = $RemoteHostListPrep.Add($_.Trim()) } } } else { $null = $RemoteHostListPrep.Add($HostNames.Trim()) } } if ($IPAddresses) { # Do some basic validation. Make sure no unexpected characters are present. $UnexpectedCharsCheck = $([char[]]$IPAddresses -notmatch "[\s]|,|-|\/|[0-9]") | Where-Object {$_ -ne '.'} if ($UnexpectedCharsCheck.Count -gt 0) { $Session:SearchRemoteHosts = $False Sync-UDElement -Id "SearchRemoteHosts" $Msg = "The following invalid characters were found in the 'IPAddress' field:`n$($UnexpectedCharsCheck -join ', ')" Show-UDToast -Message $Msg -Position 'topRight' -Title "BadChars" -Duration 10000 Write-Error $Msg return } if (!$($IPAddresses -match [regex]::Escape(',')) -and !$($IPAddresses -match [regex]::Escape('-')) -and !$($IPAddresses -match [regex]::Escape('/'))) { $null = $RemoteHostListPrep.Add($IPAddresses.Trim()) } if ($IPAddresses -match [regex]::Escape(',')) { $ArrayOfRanges = $IPAddresses -split [regex]::Escape(',') | foreach { if (![System.String]::IsNullOrWhiteSpace($_)) { $_.Trim() } } if ($IPAddresses -match [regex]::Escape('-') -and $IPAddresses -match [regex]::Escape('/')) { foreach ($IPRange in $ArrayOfRanges) { if ($IPRange -match [regex]::Escape('-')) { $StartIP = $($IPRange -split [regex]::Escape('-'))[0] $EndIP = $($IPRange -split [regex]::Escape('-'))[-1] if (!$(TestIsValidIPAddress -IPAddress $StartIP)) { Show-UDToast -Message "$StartIP is NOT a valid IPv4 Address!" -Position 'topRight' -Title "BadStartIP" -Duration 5000 } if (!$(TestIsValidIPAddress -IPAddress $EndIP)) { Show-UDToast -Message "$EndIP is NOT a valid IPv4 Address!" -Position 'topRight' -Title "BadEndIP" -Duration 5000 } if (!$(TestIsValidIPAddress -IPAddress $StartIP) -or !$(TestIsValidIPAddress -IPAddress $EndIP)) { continue } Get-IPRange -start $StartIP -end $EndIP | foreach { $null = $RemoteHostListPrep.Add($_) } } if ($IPRange -match [regex]::Escape('/')) { $IPAddr = $($IPRange -split [regex]::Escape('/'))[0] $CIDRInt = $($IPRange -split [regex]::Escape('/'))[-1] Get-IPRange -ip $IPAddr -cidr $CIDRInt | foreach { $null = $RemoteHostListPrep.Add($_) } } } } if ($IPAddresses -match [regex]::Escape('-') -and !$($IPAddresses -match [regex]::Escape('/'))) { foreach ($IPRange in $ArrayOfRanges) { $StartIP = $($IPRange -split [regex]::Escape('-'))[0] $EndIP = $($IPRange -split [regex]::Escape('-'))[-1] if (!$(TestIsValidIPAddress -IPAddress $StartIP)) { Show-UDToast -Message "$StartIP is NOT a valid IPv4 Address!" -Position 'topRight' -Title "BadStartIP" -Duration 5000 } if (!$(TestIsValidIPAddress -IPAddress $EndIP)) { Show-UDToast -Message "$EndIP is NOT a valid IPv4 Address!" -Position 'topRight' -Title "BadEndIP" -Duration 5000 } if (!$(TestIsValidIPAddress -IPAddress $StartIP) -or !$(TestIsValidIPAddress -IPAddress $EndIP)) { continue } Get-IPRange -start $StartIP -end $EndIP | foreach { $null = $RemoteHostListPrep.Add($_) } } } if ($IPAddresses -match [regex]::Escape('/') -and !$($IPAddresses -match [regex]::Escape('-'))) { foreach ($IPRange in $ArrayOfRanges) { $IPAddr = $($IPRange -split [regex]::Escape('/'))[0] $CIDRInt = $($IPRange -split [regex]::Escape('/'))[-1] Get-IPRange -ip $IPAddr -cidr $CIDRInt | foreach { $null = $RemoteHostListPrep.Add($_) } } } if (!$($IPAddresses -match [regex]::Escape('/')) -and !$($IPAddresses -match [regex]::Escape('-'))) { $IPAddresses -split [regex]::Escape(',') | foreach { if (!$(TestIsValidIPAddress -IPAddress $_)) { Show-UDToast -Message "$_ is NOT a valid IPv4 Address!" -Position 'topRight' -Title "BadIP" -Duration 5000 } else { $null = $RemoteHostListPrep.Add($_.Trim()) } } } } if ($IPAddresses -match [regex]::Escape('-') -and $IPAddresses -match [regex]::Escape('/')) { Write-Error "You are either missing a comma between two or more separate IP Ranges, or your notation is incorrect. Please try again." $global:FunctionResult = "1" return } if ($IPAddresses -match [regex]::Escape('-') -and !$($IPAddresses -match [regex]::Escape('/'))) { $StartIP = $($IPRange -split [regex]::Escape('-'))[0] $EndIP = $($IPRange -split [regex]::Escape('-'))[-1] if (!$(TestIsValidIPAddress -IPAddress $StartIP)) { Show-UDToast -Message "$StartIP is NOT a valid IPv4 Address!" -Position 'topRight' -Title "BadStartIP" -Duration 5000 } if (!$(TestIsValidIPAddress -IPAddress $EndIP)) { Show-UDToast -Message "$EndIP is NOT a valid IPv4 Address!" -Position 'topRight' -Title "BadEndIP" -Duration 5000 } if (!$(TestIsValidIPAddress -IPAddress $StartIP) -or !$(TestIsValidIPAddress -IPAddress $EndIP)) { continue } Get-IPRange -start $StartIP -end $EndIP | foreach { $null = $RemoteHostListPrep.Add($_) } } if ($IPAddresses -match [regex]::Escape('/') -and !$($IPAddresses -match [regex]::Escape('-'))) { $IPAddr = $($IPRange -split [regex]::Escape('/'))[0] $CIDRInt = $($IPRange -split [regex]::Escape('/'))[-1] Get-IPRange -ip $IPAddr -cidr $CIDRInt | foreach { $null = $RemoteHostListPrep.Add($_) } } } # Filter Out the Remote Hosts that we can't resolve via DNS [System.Collections.ArrayList]$RemoteHostList = @() $null = Clear-DnsClientCache foreach ($HNameOrIP in $RemoteHostListPrep) { try { $RemoteHostNetworkInfo = ResolveHost -HostNameOrIP $HNameOrIP -ErrorAction Stop $null = $RemoteHostList.Add($RemoteHostNetworkInfo) } catch { Show-UDToast -Message "Unable to resolve $HNameOrIP" -Position 'topRight' -Title "CheckDNS" -Duration 5000 continue } } $PUDRSSyncHT.RemoteHostList = $RemoteHostList # Add Keys for each of the Remote Hosts in the $InitialRemoteHostList foreach ($RHost in $RemoteHostList) { if ($PUDRSSyncHT.Keys -notcontains "$($RHost.HostName)Info") { $Key = $RHost.HostName + "Info" $Value = @{ NetworkInfo = $RHost CredHT = $null ServerInventoryStatic = $null RelevantNetworkInterfaces = $null LiveDataRSInfo = $null LiveDataTracker = @{Current = $null; Previous = $null} } foreach ($DynPage in $($Cache:DynamicPages | Where-Object {$_ -notmatch "PSRemotingCreds|ToolSelect"})) { $DynPageHT = @{ LiveDataRSInfo = $null LiveDataTracker = @{Current = $null; Previous = $null} } $Value.Add($($DynPage -replace "[\s]",""),$DynPageHT) } $PUDRSSyncHT.Add($Key,$Value) } } $Session:SearchRemoteHosts = $True Sync-UDElement -Id "SearchRemoteHosts" # Refresh the Main Content Sync-UDElement -Id "MainContent" } } } } } <# New-UDRow -Endpoint { New-UDColumn -Endpoint { New-UDHeading -Text "Sampling of Available Remote Hosts" -Size 5 } } #> New-UDElement -Id "MainContent" -Tag div -EndPoint { New-UDRow -Endpoint { New-UDColumn -Size 12 -Endpoint { $RHostUDTableEndpoint = { $PUDRSSyncHT = $global:PUDRSSyncHT $Cache:ThisModuleFunctionsStringArray | Where-Object {$_ -ne $null} | foreach {Invoke-Expression $_ -ErrorAction SilentlyContinue} $RHost = $PUDRSSyncHT.RemoteHostList | Where-Object {$_.HostName -eq $RHostName} $RHostTableData = @{} $RHostTableData.Add("HostName",$RHost.HostName.ToUpper()) $RHostTableData.Add("FQDN",$RHost.FQDN) # Guess Operating System if ($RHost.HostName -eq $env:ComputerName) { $OSGuess = $(Get-CimInstance Win32_OperatingSystem).Caption } else { if ([bool]$(Get-Command nmap -ErrorAction SilentlyContinue)) { $NmapOSResult = nmap -O $RHost.IPAddressList[0] if ($NmapOSResult -match "OS details:") { $OSGuessPrep = $($NmapOSResult | Where-Object {$_ -match "OS details:"}) -replace "OS details: ","" $OSGuess = if ($OSGuessPrep -match ',') {$($OSGuessPrep -split ',')[0].Trim()} else {$OSGuessPrep.Trim()} } if ($NmapOSResult -match "Aggressive OS guesses:") { $OSGuessPrep = $($NmapOSResult | Where-Object {$_ -match "Aggressive OS guesses:"}) -replace "Aggressive OS guesses: ","" $OSGuessPrep = if ($OSGuessPrep -match ',') {$($OSGuessPrep -split ',')[0]} else {$OSGuessPrep} $OSGuess = $($OSGuessPrep -replace "[\s]\([0-9]+%\)","").Trim() } if (!$OSGuess) { $OSGuess = $null } } else { $OSGuess = $null } } $RHostTableData.Add("OS_Guess",$OSGuess) $IPAddressListAsString = @($RHost.IPAddressList) -join ", " $RHostTableData.Add("IPAddress",$IPAddressListAsString) # Check Ping try { $PingResult = [System.Net.NetworkInformation.Ping]::new().Send( $RHost.IPAddressList[0],1000 ) | Select-Object -Property Address,Status,RoundtripTime -ExcludeProperty PSComputerName,PSShowComputerName,RunspaceId $PingStatus = if ($PingResult.Status.ToString() -eq "Success") {"Available"} else {"Unavailable"} $RHostTableData.Add("PingStatus",$PingStatus) } catch { $RHostTableData.Add("PingStatus","Unavailable") } # Check WSMan Ports try { $WSMan5985Url = "http://$($RHost.IPAddressList[0])`:5985/wsman" $WSMan5986Url = "http://$($RHost.IPAddressList[0])`:5986/wsman" $WSManUrls = @($WSMan5985Url,$WSMan5986Url) foreach ($WSManUrl in $WSManUrls) { $Request = [System.Net.WebRequest]::Create($WSManUrl) $Request.Timeout = 1000 try { [System.Net.WebResponse]$Response = $Request.GetResponse() } catch { if ($_.Exception.Message -match "The remote server returned an error: \(405\) Method Not Allowed") { if ($WSManUrl -match "5985") { $WSMan5985Available = $True } else { $WSMan5986Available = $True } } elseif ($_.Exception.Message -match "The operation has timed out") { if ($WSManUrl -match "5985") { $WSMan5985Available = $False } else { $WSMan5986Available = $False } } else { if ($WSManUrl -match "5985") { $WSMan5985Available = $False } else { $WSMan5986Available = $False } } } } if ($WSMan5985Available -or $WSMan5986Available) { $RHostTableData.Add("WSMan","Available") [System.Collections.ArrayList]$WSManPorts = @() if ($WSMan5985Available) { $null = $WSManPorts.Add("5985") } if ($WSMan5986Available) { $null = $WSManPorts.Add("5986") } $WSManPortsString = $WSManPorts -join ', ' $RHostTableData.Add("WSManPorts",$WSManPortsString) } } catch { $RHostTableData.Add("WSMan","Unavailable") } # Check SSH try { $TestSSHResult = TestPort -HostName $RHost.IPAddressList[0] -Port 22 if ($TestSSHResult.Open) { $RHostTableData.Add("SSH","Available") } else { $RHostTableData.Add("SSH","Unavailable") } } catch { $RHostTableData.Add("SSH","Unavailable") } $RHostTableData.Add("DateTime",$(Get-Date -Format MM-dd-yy_hh:mm:sstt)) if ($RHostTableData.WSMan -eq "Available" -or $RHostTableData.SSH -eq "Available") { # We are within an -Endpoint, so $Session: variables should be available #if ($PUDRSSyncHT."$($RHost.HostName)`Info".CredHT.PSRemotingCreds -ne $null) { if ($Session:CredentialHT.$($RHost.HostName).PSRemotingCreds -ne $null) { $RHostTableData.Add("ManageLink",$(New-UDLink -Text "Manage" -Url "/ToolSelect/$($RHost.HostName)")) } else { $RHostTableData.Add("ManageLink",$(New-UDLink -Text "Manage" -Url "/PSRemotingCreds/$($RHost.HostName)")) } } else { $RHostTableData.Add("ManageLink","Unavailable") } $RHostTableData.Add("NewCreds",$(New-UDLink -Text "NewCreds" -Url "/PSRemotingCreds/$($RHost.HostName)")) if ($PUDRSSyncHT."$($RHost.HostName)Info".Keys -contains "RHostTableData") { $PUDRSSyncHT."$($RHost.HostName)Info".RHostTableData = $RHostTableData } else { $PUDRSSyncHT."$($RHost.HostName)Info".Add("RHostTableData",$RHostTableData) } [pscustomobject]$RHostTableData | Out-UDTableData -Property @("HostName","FQDN","OS_Guess","IPAddress","PingStatus","WSMan","WSManPorts","SSH","DateTime","ManageLink","NewCreds") } $RHostUDTableEndpointAsString = $RHostUDTableEndpoint.ToString() $RHostCounter = 0 #$Session:CredentialHT = @{} foreach ($RHost in $PUDRSSyncHT.RemoteHostList) { $RHostUDTableEndpoint = [scriptblock]::Create( $( "`$RHostName = '$($RHost.HostName)'" + "`n" + $RHostUDTableEndpointAsString ) ) $ResultProperties = @("HostName","FQDN","OS_Guess","IPAddress","PingStatus","WSMan","WSManPorts","SSH","DateTime","ManageLink","NewCreds") $RHostUDTableSplatParams = @{ Title = $RHost.HostName.ToUpper() Headers = $ResultProperties #AutoRefresh = $True #RefreshInterval = 15 Endpoint = $RHostUDTableEndpoint } New-UDTable @RHostUDTableSplatParams $RHostCounter++ if ($RHostCounter -ge $($PUDRSSyncHT.RemoteHostList.Count-1)) { New-UDColumn -Endpoint { $Session:HomePageLoadingTracker = $True $Session:SearchRemoteHosts = $False Sync-UDElement -Id "SearchRemoteHosts" } } } # This hidden column refreshes the RemoteHostList so that when the HomePage is reloaded, it only displays # host/devices that can be resolved. This is so that if PUDAdminCenter is used to shutdown/restart a Remote Host, # the list of hosts on the HomePage is accurate New-UDColumn -AutoRefresh -RefreshInterval 10 -Endpoint { $PUDRSSyncHT = $global:PUDRSSyncHT $Cache:ThisModuleFunctionsStringArray | Where-Object {$_ -ne $null} | foreach {Invoke-Expression $_ -ErrorAction SilentlyContinue} if ($Cache:HomeFinishedLoading -and !$Cache:RHostRefreshAlreadyRan) { $null = Clear-DnsClientCache foreach ($IPAddr in $PUDRSSyncHT.RemoteHostList.IPAddressList) { try { $RemoteHostNetworkInfo = ResolveHost -HostNameOrIP $IPAddr -ErrorAction Stop # ResolveHost will NOT throw an error even if it can't figure out HostName, Domain, or FQDN as long as $IPAddr IS pingable # So, we need to do the below to compensate for code downstream that relies on HostName, Domain, and FQDN if (!$RemoteHostNetworkInfo.HostName) { $LastTwoOctets = $($IPAddr -split '\.')[2..3] -join 'Dot' $UpdatedHostName = NewUniqueString -PossibleNewUniqueString "Unknown$LastTwoOctets" -ArrayOfStrings $PUDRSSyncHT.RemoteHostList.HostName $RemoteHostNetworkInfo.HostName = $UpdatedHostName $RemoteHostNetworkInfo.FQDN = $UpdatedHostName + '.Unknown' $RemoteHostNetworkInfo.Domain = 'Unknown' } $null = $RemoteHostList.Add($RemoteHostNetworkInfo) } catch { continue } } $PUDRSSyncHT.RemoteHostList = $RemoteHostList $Cache:RHostRefreshAlreadyRan = $True } } } } } #endregion >> HomePage Main Content } # IMPORTANT NOTE: Anytime New-UDPage is used with parameter set '-Name -Content', it appears in the hamburger menu # This is REQUIRED for the HomePage, otherwise http://localhost won't load (in otherwords, you can't use the # parameter set '-Url -Endpoint' for the HomePage). # Also, it is important that the HomePage comes first in the $Pages ArrayList $HomePage = New-UDPage -Name "Home" -Icon home -Content $HomePageContent $null = $Pages.Insert(0,$HomePage) |