Conditional formatting in psobject output

This topic contains 7 replies, has 3 voices, and was last updated by Profile photo of Mapping Everything Mapping Everything 2 years, 2 months ago.

  • Author
    Posts
  • #15982
    Profile photo of Erich Steiger
    Erich Steiger
    Participant

    Is there an elegant way to conditionally format psobject output to the console? I wrote a script to check if a specific service is running or stopped on a number of computers, and used IF logic and write-host -ForegroundColor to make the computers with the service Running stand out. I'm rewriting the script to output a psobject vs. using Write-Host , but am finding that I'm losing the coloring functionality.

  • #15984
    Profile photo of Don Jones
    Don Jones
    Keymaster

    No. The formatting system is more or less color-blind, because it needs to work with more than just the console host. If you're okay with limiting yourself to the console for output, and you've named your script "Show-xxxxx" (the verb "Show" implying "this requires console visuals"), then Write-Host is the correct approach. Also understanding that the output of a "Show-xxxxx" script can only be displayed, and not piped to another command to sort, filter, export etc.

    You could consider an HTML-based report. My EnhancedHTML module lets you conditionally color table cells.

  • #15985
    Profile photo of Erich Steiger
    Erich Steiger
    Participant

    Understood. I'll check out the EnhancedHTML module and look into integrating HTML output into my workflow.

  • #19313
    Profile photo of Mapping Everything
    Mapping Everything
    Participant

    I am working with the EnhancedHTML module and it works really well. Thanks for this Don!

    I have an issue though where I am trying to modify some of the code to customize the results.

    In the demo, there is this line when showing bad services : $svcs = Get-WmiObject -class Win32_Service -ComputerName $ComputerName -Filter "StartMode='Auto' AND State'Running'"
    So I have taken out the filter because I want all of the services to show in the list and then modify them later.

    When I am modifying the values to format the table at the end, I have this in $params :

    'Properties'='Service Name',
    'Display Name',
    'Start Mode',
    @{n='State';e={$_.State};css={if (($_.State -eq "Stopped") -and ($_.StartMode -eq "Auto")) { 'red' } elseif ($_.State -eq "Running") { 'green' }}}}

    Everything works great except for the expression above. So what I am trying to do is if a service is running I change the font to green. If it is stopped and the startmode is auto, I want to make it red. The elseif condition for 'Running' is working and they are green. Nothing seems to happen with the condition for 'red' though. I am not sure what the issue might be either with the logic, syntax, or if this is just not possible.

    Any ideas?

    Todd

  • #19320
    Profile photo of Don Jones
    Don Jones
    Keymaster

    That code will emit the "red" and "green" classes into the HTML. You have to define those classes in your CSS for it to have any visible effect.

  • #19323
    Profile photo of Mapping Everything
    Mapping Everything
    Participant

    Thanks. I do have 'red' and 'green' defined in the CSS and both seem to work (instead of font color I am now using background-color to get the cells changed to make them more visible when scanning the list). There is something about : (b)if (($_.State -eq "Stopped") -and ($_.StartMode -eq "Auto")) { 'red' }(/b) that just never seems to work. It's the same code as what you had as the filter in the original function to get a bad service, but nothing in the cells that are shown that fit that condition is changed. I just remains a white cell with normal black text that says 'Stopped'.

    If I change it just to this (b)@{n='State';e={$_.State};css={if ($_.State -eq "Stopped") { 'red' } elseif ($_.State -eq "Running") { 'green' }}}}(/b) things work fine with all cells either red or green. That second condition though of (b)(-and ($_.StartMode -eq "Auto"))(/b) seems to make it stop working.

    Just not sure why this is. It looks like it should work to me.

    Also, as I was typing I thought about trying this with nesting if statements :

    (b)@{n='State';e={$_.State};css={if ($_.State -eq "Stopped") { if ($_.StartMode -eq "Auto") { 'red' }} elseif ($_.State -eq "Running") { 'green' }}}}(/b)

    That did not work either. Hmm.....

    Todd

  • #19349
    Profile photo of Don Jones
    Don Jones
    Keymaster

    As a note, it's square brackets to do the formatting, not paren. You may need to do a shift+refresh in your browser to unload the old Javascript toolbar.

    It's possible my code isn't evaluating the more complex expression correctly. I'm not sure why; I basically just parse it as a script block. Possibly I'm not interpreting the double us of $_ correctly. Something I'll need to dig into at some point ;).

    As a workaround, the trick would be to add a property to your object.

    $services = get-service | foreach {
     if ($_.state -eq 'Stopped' -and $_.StartMode -eq 'Auto') { $_ | Add-Member NoteProperty Color 'red' } else { $_ | Add-Member NoteProperty Color 'green' }
    }
    

    You could then emit the "Color" property to the CSS:

    @{n='State';e={$_.State};css={$_.color}}

    Something vaguely like that. Point being, you don't want to have to use $_ twice in the CSS expression, since that isn't working, so you do the logic ahead of time and embed it in a custom property. I'd have to fuss a bit to see if the exact example here was the way to do it, but that's the approach I'd take.

  • #19426
    Profile photo of Mapping Everything
    Mapping Everything
    Participant

    Many thanks for sticking with me on this Don. I fully understand what you are saying above and did finally get this to work using the principle. What I have below in the function that gets services now is a little different. Sorry, WAY too many years programming in Delphi with Object-Pascal. The syntax of PS is slightly different. 🙂

    It does do exactly what I need now though and I can move onto bigger fish. It could likely be cleaner and I can look at this once I finish the overall script. Appreciate the guidance!

        $WMISvc = Get-WmiObject -class Win32_Service -ComputerName $ComputerName
        foreach ($svc in $WMISvc) {
            
            #To add a color property to the newly created $props object to for conditional HTML cell formatting
            if ($svc.State -eq 'Stopped' -and $svc.StartMode -eq 'Auto') { $svccolor = 'red' } `
            elseif ($svc.State -eq 'Running') { $svccolor = 'green' } `
            else { $svccolor = '' }
    
            $props = @{'Service Name' = $svc.Name;
                       'Display Name' = $svc.DisplayName;
                       'Start Mode'= $svc.StartMode;
                       'State' = $svc.State;
                       'Color' = $svccolor}
            New-Object -TypeName PSObject -Property $props
        }
    

You must be logged in to reply to this topic.