arrayspowershellhashtablenessus

Is a hash table the way forward?


I have a variable that looks like this:

Plugin ID     : 66334
Host          : GCAB-L7-449090L
Plugin Output : . Microsoft Operating System Patches :
                + To patch the remote system, you need to install the following Microsoft patches :
                - KB3167679 (MS16-101) (2 vulnerabilities)The following CVEs would be covered: 
                CVE-2016-3300, CVE-2016-3237
                - KB3114340 (MS16-099) (133 vulnerabilities)The following CVEs would be covered: 
                CVE-2016-3313, CVE-2016-3315, CVE-2016-3316, CVE-2016-3317, CVE-2016-3318, 
                - KB3115427 (MS16-099) (133 vulnerabilities)The following CVEs would be covered: 
                CVE-2016-3313, CVE-2016-3315, CVE-2016-3316, CVE-2016-3317, CVE-2016-3318

Plugin ID     : 66334
Host          : GCAB-L7-449096R
Plugin Output : . Microsoft Operating System Patches :
                + To patch the remote system, you need to install the following Microsoft patches :
                - KB3167679 (MS16-101) (2 vulnerabilities)The following CVEs would be covered: 
                CVE-2016-3300, CVE-2016-3237
                - KB3177725 (MS16-099) (58 vulnerabilities)The following CVEs would be covered: 
                CVE-2016-3313, CVE-2016-3315, CVE-2016-3316, CVE-2016-3317, CVE-2016-3318

What I am trying to accomplish is an array of KBs containing hosts. I thought a hashtable was the way to go but if it is I'm missing a key piece of understanding about them. Here's my code:

$filtered = $data | Where-Object {[string]$_."Plugin ID" -eq "66334"}

foreach ($item in $filtered)
{
    $machine = $item.Host
    $kbs = Select-String -InputObject $item.'Plugin Output' -Pattern $regex -AllMatches |
           ForEach-Object { $_.Matches }

    foreach ($k in $kbs)
    {
        if ($hash.ContainsKey($k))
        {
            #The KB is already a part of the hash table.  Edit the key value to include the new host.
        }
        else
        {
            $hash[$k] = $machine
        }
    }
}

If the key doesn't exist then add it to the hash, otherwise I would modify the value of the existing key to include the new host. Unfortunately my if statement continues to execute only the else clause.

What I want to get to is this:

KB                 Host

KB3167679           GCAB-L7-449090L, GCAB-L7-449096R
KB3114340           GCAB-L7-449090L
KB3115427           GCAB-L7-449090L
KB3177725           GCAB-L7-449096R

So, a couple of questions:

  1. Why isn't $hash.ContainsKey() working for me here?
  2. Is a hashtable the way I want to go?

Solution

  • Yes, a hashtable is the way to go. $hash.ContainsKey() doesn't work for you, because you made $kbs a list of MatchInfo objects instead of expanding the matched values to strings.

    As others have already suggested you can add another ForEach-Object to your pipeline

    $kbs = Select-String -InputObject $item.'Plugin Output' -Pattern $regex -AllMatches |
           ForEach-Object { $_.Matches } | ForEach-Object { $_.Value }
    

    or (if you have PowerShell v3 or newer) use member enumeration

    $kbs = Select-String -InputObject $item.'Plugin Output' -Pattern $regex -AllMatches |
           ForEach-Object { $_.Matches.Value }
    

    to get the actual string values.