Extracting several Multi-Value Properties from Array of Objects

This topic contains 11 replies, has 4 voices, and was last updated by  js 3 months, 3 weeks ago.

  • Author
    Posts
  • #77811

    DJ
    Participant

    I have an array that is full of firewall rules objects. These objects all have properties associated with firewall rules. Many of these properties are multi-value, for example the Source IP field could have multiple IP Addresses and Groups. The source zone the same. The same with Destination Zone, Destination IP, Service, Application, etc...

    With that said, how can I show all the expanded results of all my multi-value properties on the console? How about when exporting to a text file? How about when exporting to a CSV?

    Thanks in Advance,

    DJ

  • #77812

    Kevyn
    Participant

    Try setting $FormatEnumerationLimit to -1 before dumping out your objects.

    For example:

    $FormatEnumerationLimit = -1
    $FirewallObjects | fl
    
    • #77869

      DJ
      Participant

      This was very helpful for when displaying the results in the console, or just dumping to a text file. Thanks!

  • #77817

    Michael Halpin
    Participant

    How exactly do you need the output ? For example, do you want to have a single source IP field, and then if an object has multiple Source IPs each one gets listed on it's own line? Or would fields like SourceIP1, SourceIP2.. SourceIPN format work?

    Since you can't do an -expandproperty on multiple properties, the only thing I can think of is to iterate the object (essentially an array of arrays), and "flatten" it out, storing that in a new object, and exporting that.

    Here's a simple example, the dependentservices property could contain 0 or more services, I just took 3 as an example.

    $Services = get-service CryptSvc, BITS
    
    
    function Expand-DependentServices
    {    
        [OutputType([PSObject])]
        Param
        (
            # Param1 help description
            [Parameter(Mandatory=$true,
                       ValueFromPipeline = $true,
                       Position=0)]
            $service
        )
    
        Process
        {
          $name = $service.name
          $depends = $service | select -ExpandProperty dependentservices
          if ($depends){
          
          
            $props =[ordered] @{'Name' =$name
                    'Depends on 1' =$depends[0]
                    'Depends on 2' =$depends[1]
                    'Depends on 3' =$depends[2]
                    }
          }else {
            $props =[ordered] @{'Name' =$name
                    'Depends on 1' =$null
                    'Depends on 2' =$null
                    'Depends on 3' =$null
                    }
          }
          $obj = New-Object -TypeName PSObject -Property $props
        Write-Output $obj
        }
        
    }
    $Services | Expand-DependentServices
    
    Name     Depends on 1 Depends on 2  Depends on 3
    ----     ------------ ------------  ------------
    BITS                                            
    CryptSvc AppIDSvc     applockerfltr 
    • #77866

      DJ
      Participant

      .

  • #77871

    DJ
    Participant

    Let me provide some more context with sample data:

    Get-UnusedAddresses | Select Name
    Name
    —-
    CFG-LOP-IP-1
    CFG-LOP-IP-2
    CFG-LOP-IP-3
    CFG-LOP-IP-4
    CFG-LOP-IP-5

    Get-UnusedAddressGroups | Select Name
    Name
    —-
    CFG-OFC
    OPP-OFC
    JAK-LOTS

    Get-UnusedServices | Select Name
    Name
    —-
    TCP-555
    TCP-139
    TCP-445
    TCP-389
    TCP-88
    UDP-88
    TCP-636
    TCP-123
    TCP-53
    UDP-53

    All 3 of these functions return Objects of: System.Management.Automation.PSCustomObject

    I've also saved out these results to variables of Type: System.String as shown below

    $unusedAddresses
    CFG-LOP-IP-1
    CFG-LOP-IP-2
    CFG-LOP-IP-3
    CFG-LOP-IP-4
    CFG-LOP-IP-5

    $unusedAddressGroups
    CFG-OFC
    OPP-OFC
    JAK-LOTS

    $unusedServices
    TCP-555
    TCP-139
    TCP-445
    TCP-389
    TCP-88
    UDP-88
    TCP-636
    TCP-123
    TCP-53
    UDP-53

    I want to export these Strings or PSCustomObjects into a CSV similar to below. There's no telling how many items will be in each array.

    Column1 = UnusedAddresses
    Column2 = UnusedAddressGroups
    Column3 = UnusedServices

    Essentially these arrays have NOTHING to do with each other. Its just for presentation. Aside from CSV, I would like this as HTML too.

  • #77898

    Kevyn
    Participant

    See if this helps. I tested with an array of strings, but you can work with your PSCustomObjects as well.

    $UnusedAddresses = 'CFG-LOP-IP-1','CFG-LOP-IP-2','CFG-LOP-IP-3','CFG-LOP-IP-4','CFG-LOP-IP-5'
    $UnusedAddressGroups = 'CFG-OFC','OPP-OFC','JFK-LOTS'
    $UnusedServices = 'TCP-555','TCP-139','TCP-445','TCP-389','TCP-88','UDP-88','TCP-636','TCP-123','TCP-53','UDP-53'
    $LargestListCount = ($UnusedAddresses.Count,$UnusedAddressGroups.Count,$UnusedServices.Count | Measure-Object -Maximum).Maximum
    
    For($i = 0;$i -lt $LargestListCount;$i++)
    {
      If($i -lt $UnusedAddresses.Count)
      {
        $UA = $UnusedAddresses[$i]
      }
      Else
      {
        $UA = ''
      }
    
      If($i -lt $UnusedAddressGroups.Count)
      {
        $UAG = $UnusedAddressGroups[$i]
      }
      Else
      {
        $UAG = ''
      }
    
      If($i -lt $UnusedServices.Count)
      {
        $US = $UnusedServices[$i]
      }
      Else
      {
        $US = ''
      }
    
      $Props = [ordered] @{
                            UnusedAddresses = $UA;
                            UnusedAddressGroups = $UAG;
                            UnusedServices = $US
                          }
    
      $Obj = New-Object -TypeName PSObject -Property $Props
      $ObjArray += $Obj
    }
    
    Write-Output $ObjArray
    $ObjArray | Export-Csv -Path 'C:\data\obj.csv' -NoTypeInformation
    $ObjArray | ConvertTo-Html | Out-File -FilePath 'C:\data\obj.html'
    

    I get the following output, for the Write-Output command at the bottom of the code. When I do the export to CSV or conversion/export of the html, it looks good to me. I'm sure there's a better solution, but this worked for me.

    UnusedAddresses UnusedAddressGroups UnusedServices
    ————— ——————- ————–
    CFG-LOP-IP-1 CFG-OFC TCP-555
    CFG-LOP-IP-2 OPP-OFC TCP-139
    CFG-LOP-IP-3 JFK-LOTS TCP-445
    CFG-LOP-IP-4 TCP-389
    CFG-LOP-IP-5 TCP-88
    UDP-88
    TCP-636
    TCP-123
    TCP-53
    UDP-53

  • #77907

    Kevyn
    Participant

    I edited my above code to put the columns in the order you were mentioning.

  • #77913

    Kevyn
    Participant

    BTW, don't trust how the Write-Output looks in my above post. It's just the way this forum formats it. It looks the way it should, on the screen and in the files, when you run the code.

    • #78132

      DJ
      Participant

      Kevyn, thanks for the help. I hadn't had time to respond back to you. This worked better than what I was able to come up with. As an FYI, I essentially created an empty CSV file, and then using Add-Content I manually added entries to the CSV file with comma delimiters, using a for loop with index. Its similar, but the code you showed looked cleaner, and lets me remain standardized on export-csv. Thanks!

  • #78135

    Kevyn
    Participant

    You're welcome. Glad it worked for you. 🙂

  • #78138

    js
    Participant

    My nuclear option to expand everything is

    $object | fc *
    

    You can also export it to xml or json (-depth 100)...

You must be logged in to reply to this topic.