locked
Windows Powershell : Sort the hashtable RRS feed

  • Question

  • I have a parameter that provides the output like this. When I try to sort for the Name property, it doesn't.  I have tried the following code, but nothing works.

    $writeParams | Sort-Object -Descending
    $writeParams | Sort-Object -Property Name -Descending
    $writeParams | Sort-Object -Property LastReboot -Descending 

    Name                      Value
    Domain                          Phoneix-01                                                                                           
    LastReboot                     4/4/2020 2:03:03 AM                                                                                              
    TaskTrigger                    {Tuesday, --, 02:00:00}                                        
    FileName                       C:\ServerPatchReport.html                                                                    
    KB                                KB4540723                                                                                                
    InstalledOn                    3/14/2020 2:44:12 AM                                                   
    OSType                         Windows Server 2016                                                                                                                                                                                            
    Domain                         Phoneix-02                                                                                           
    LastReboot                     4/6/2020 2:04:06 AM                                                                                              
    TaskTrigger                    {Monday...                                                     
    FileName                       C:\ServerPatchReport.html                                                                    
    KB                                KB4540688                                                                                                        
    InstalledOn                   3/30/2020 2:19:56 AM                                           
    OSType                         Windows Server 2008 R2

    Monday, April 6, 2020 7:40 AM

All replies

  • You cannot sort a collection of hashes.  

    \_(ツ)_/

    Monday, April 6, 2020 8:56 AM
  • You cannot sort a collection of hashes.  

    \_(ツ)_/

    Thanks. I am trying to feed this data into HTML. and then i am trying to sort. I can get the information in the HTML but the sorting the HTML also doesn't work.

    I am really clueless as to how to make this work.

    Monday, April 6, 2020 9:29 AM
  • Sorry but you can only sort objects.  You are trying to sort hash tables.  Also hashes cannot be correctly converted to HTML.  

    Without knowing more about your code there is no way to answer your issue.


    \_(ツ)_/

    Monday, April 6, 2020 10:16 AM
  • $computername = $env:computername
    $USERNAME = $env:username
    $DateStamp = (Get-Date -Format D)
    $FileDateStamp = Get-Date -Format yyyyMMdd
    $ScriptPath = Get-Location
    $ReportFileName = "$ScriptPath\ServerPatchReport.html"
    $ReportFileName2 = "$ScriptPath\ServerPatchReport-Unavailable.html"
    $BGColorTbl = "#EAECEE"
    $BGColorGood = "#4CBB17"
    $BGColorWarn = "#FFFC33"
    $BGColorCrit = "#FF0000"
    $PendingRebootYes = "Yes"
    $PendingRebootNo = "No"
    $ReportTitle = "$ReportTitle"
    $RunFrom = "This report was generated from $computername"
    $EmailSubject = "Millennium Patch Report for $DateStamp"
    $EmailAttachment = "$ScriptPath\Logo.PNG"
    $Global:Servers = Get-Content servers.txt
    
        
    
    <#==================================================
    Begin MAIN
    ==================================================#>
    # Create output file and nullify display output
    New-Item -ItemType file $ReportFileName -Force > $null
    New-Item -ItemType file $ReportFileName2 -Force > $null
    
    <#==================================================
    Write the HTML Header to the report file
    ==================================================#>
    ##HTML code
    
    If ($Servers.count -gt 0) {
        Write-ToLogFile -Info "Starting remote job against $($Servers.count) servers"
        $RemoteJob = Invoke-Command -ScriptBlock ${function:getRemoteServerInfo} -computername $Servers -AsJob
        $JobID = $RemoteJob.Id
        Wait-Job -Id $JobID -Timeout $JobTimeout | Out-Null
        $FailedServers = Get-Job -ID $JobID | select-object -expandproperty ChildJobs | Where-object {$_.State -ne "Completed"} | Select-object -expandproperty Location
        $CompletedJobs = Receive-Job -ID $JobID -ErrorAction SilentlyContinue
        Foreach ($Server in $FailedServers) {
            writeUnavailableData $ReportFileName2 $Server
        }
    
        Foreach ($Server in $CompletedJobs) {
            $Server | Sort-Object -Property LastReboot -Descending
            #Write-Host $Server
            $writeParams = @{
                FileName = $ReportFileName
                Server = $Server.PSComputerName
                InstalledOn = $Server.InstalledOn
                KB = $Server.KB
                TaskTrigger = $Server.taskTrigger
                LastReboot = $Server.LastReboot
            }
            #$writeParams | sort-object @{Expression={$_[2]}; Ascending=$false}
            writeUpdateData @writeParams
        }
    
        Write-Host "Finishing report..." -ForegroundColor "Yellow"
        writeHtmlFooter $ReportFileName
    }
    <#==================================================
    Function to write server update information to the
    HTML report file
    ==================================================#>
    Function writeUpdateData {
        param($FileName,$Server,$InstalledOn,$Domain,$KB,$OSType,$DataCenter,$taskTrigger,$LastReboot)
        Add-Content $FileName "<tr>"
        Add-Content $FileName "<td align='center'>$Server</td>"
        Add-Content $FileName "<td align='center'>$OSType</td>"
        Add-Content $FileName "<td align='center'>$Domain</td>"
        Add-Content $FileName "<td align='center'>$InstalledOn</td>"
        Add-Content $FileName "<td align='center'>$LastReboot</td>"
        Add-Content $FileName "<td align='center'>$KB</td>"
    
    
        # Color BG depending on $Warning and $Critical days set in script
        If ($InstalledOn -eq "Error collecting data")
        {
            $DaySpanDays = "Error"
        }
        Else
        {
            $System = (Get-Date -Format "MM/dd/yyyy hh:mm:ss")
            $DaySpan = New-TimeSpan -Start $InstalledOn -End $System
            $DaySpanDays = $DaySpan.Days
        }
        If ($InstalledOn -eq "Error collecting data" -or $DaySpan.Days -gt $Critical)
        {
            # Red for Critical or Error retrieving data
            Add-Content $FileName "<td bgcolor=$BGColorCrit align='center'>$DaySpanDays</td>"
            #Add-Content $FileName "</tr>"
        }
        ElseIf ($DaySpan.Days -le $Warning)
        {
            # Green for Good
            Add-Content $FileName "<td bgcolor=$BGColorGood align=center>$DaySpanDays</td>"
            #Add-Content $FileName "</tr>"
        }
        Else
        {
            # Yellow for Warning
            Add-Content $FileName "<td bgcolor=$BGColorWarn align=center>$DaySpanDays</td>"
            #Add-Content $FileName "</tr>"
        }
        Add-Content $FileName "<td align='center'>$datacenter</td>"
        Add-Content $FileName "<td align='center'>$taskTrigger</td>"
    }
    
    
    
    <#==================================================
    Function for servers that are unavailable
    ==================================================#>
    Function writeUnavailableData
    {
        param($FileName,$Server)
        Add-Content $FileName "<tr>"
    
    
        Add-Content $FileName "<td bgcolor=$BGColorWarn align='center'>$server</td>"
    }
    
    <#==================================================
    Function to write the HTML footer
    ==================================================#>
    Function writeHtmlFooter
    {
        param($FileName)
        Add-Content $FileName "</table>"
        Add-content $FileName "<table width='75%' align=`"center`">"
        Add-Content $FileName "<tr bgcolor=$BGColorTbl>"
        #Add-Content $FileName "<td width='75%' align='center'><strong>Total Servers: $ServerCount</strong></td>"
        Add-Content $FileName "</tr>"
        Add-Content $FileName "</table>"
        Add-Content $FileName "</body>"
        Add-Content $FileName "</html>"
    }
    
    function getRemoteServerInfo {
        #Create output object
        $OutputObject = New-Object PSObject
    
        #Fetch last reboot date / time
        $LastReboot =gwmi win32_operatingsystem | %{ $_.ConvertToDateTime($_.LastBootUpTime) }
        $OutputObject | Add-Member -MemberType NoteProperty -Name LastReboot -value $LastReboot
    
        #Fetch patch info
        $session = [activator]::CreateInstance([type]::GetTypeFromProgID('Microsoft.Update.Session'))
        $searcher = $session.CreateUpdateSearcher()
        $totalupdates = $searcher.GetTotalHistoryCount()
        $remoteresult = $searcher.QueryHistory(0 , $totalupdates) | select-Object -First 1 | Select-Object Date, Title
        $OutputObject | Add-Member -MemberType NoteProperty -Name remoteresult -value $remoteresult
    
        $Installed = $remoteresult.date
        $InstalledOn = $Installed.tolocaltime()
        $OutputObject | Add-Member -MemberType NoteProperty -Name InstalledOn -value $InstalledOn
        $KB = $remoteresult.Title.Split('(')[1].split(')')[0]
        $OutputObject | Add-Member -MemberType NoteProperty -Name KB -value $KB
    
    
        #Fetch Scheduled Reboot Interval
        $OS = Invoke-Command  { (Get-WMIObject win32_operatingsystem).caption }
        $caption = $OS.substring(25,4)
        if(($caption -eq "2016") -or ($caption -eq "2012")) {
        $task = Invoke-Command  { Get-ScheduledTask | Where-Object {$_.Taskname -match "Weekly Scheduled Reboot"}}
        $tasktime = $task.Triggers[0]
        $tasktime = $tasktime | Select-Object StartBoundary
        $tasktime = $tasktime.StartBoundary.Substring(11)
        $taskday = $task.Triggers[0]
        $taskday = $taskday | Select-Object StartBoundary
        $taskday = $taskday.StartBoundary.substring(0,10)
        $taskday = (get-date $taskday).DayOfWeek
        $taskTrigger = $taskday , "--" , $tasktime
        $OutputObject | Add-Member -MemberType NoteProperty -Name tasktrigger  -value $taskTrigger
        $OutputObject }
        else {
        $Schedule = new-object -com Schedule.Service
        $Schedule.connect() 
        $rootTaskFolder = $Schedule.GetFolder("\")
        $task = $rootTaskFolder.GetTask("Weekly Scheduled Reboot")
        $taskTrigs = $task.NextRunTime
        $taskday = (get-date $taskTrigs).DayOfWeek | ForEach-Object{ $_ } | ft -AutoSize | Out-String
        $tasktime = $taskTrigs | ForEach-Object {([datetime]$_).ToString("HH:mm:ss")}
        $taskTrigger = $taskday , "--" , $tasktime
    
        $OutputObject | Add-Member -MemberType NoteProperty -Name tasktrigger  -value $taskTrigger
    
        $OutputObject
        }    
    }
    Here is teh code, i removed the HTML code. let me know if that would be needed as well?
    Monday, April 6, 2020 10:30 AM
  • I don't think anyone here wants to try to figure out what all of that is.  You need to provide a clear example of the issue.

    As noted above.  You cannot sort hash tables.  Make objects and not hashes.  Objects pave properties that can be sorted.

    This mat\y help you to understand how to ask a technical question in a forum:


    \_(ツ)_/

    Monday, April 6, 2020 1:38 PM
  • Sorry, I will provide the gist of what i am providng.

    • We have around 100 servers from which we pull hte information. I have created a function with the operations and calling that function in the invoke-command for remote execution. I think the issue is that the OutputObject doesn't hold all the values outside the loop as it is getting executed one by one. I am looking for a way in which the outputobjet will have all the values and then i should be able to use sort-object to sort it and add it to the HTML.
        

    function getRemoteServerInfo { #Create output object $OutputObject = New-Object PSObject $OutputObject | Add-Member -MemberType NoteProperty -Name LastReboot -value $LastReboot $OutputObject | Add-Member -MemberType NoteProperty -Name InstalledOn -value $InstalledOn $OutputObject | Add-Member -MemberType NoteProperty -Name tasktrigger -value $taskTrigger $OutputObject } foreach ($S in $servers) { Invoke-Command -ScriptBlock ${function:getRemoteServerInfo} -computername $S -AsJob }




    • Edited by Koz0s Monday, April 6, 2020 2:37 PM
    Monday, April 6, 2020 2:34 PM
  • You need to learn how to format code so that it is readable.  That way you will be able to see your mistakes.

    This would be the way to create objects that are sortable.

    function getRemoteServerInfo {
        [pscustomobject]@{
            LastReboot = $LastReboot
            InstalledOn = $InstalledOn
            TaskTrigger = $taskTrigger
        }
    }

    You don't need a function to do this.  Until you learn basic PowerShell I recommend avoiding functions. They will only confuse your early efforts.


    \_(ツ)_/

    Monday, April 6, 2020 3:46 PM
  • Your function mat create an object but that object will contain no data as it is all null variables.


    \_(ツ)_/

    Monday, April 6, 2020 3:48 PM