Duplicate outputs in script cmdlet

Welcome Forums General PowerShell Q&A Duplicate outputs in script cmdlet

This topic contains 5 replies, has 2 voices, and was last updated by

 
Participant
3 months, 3 weeks ago.

  • Author
    Posts
  • #109748

    Participant
    Points: 0
    Rank: Member

    Hello Experts,

    I recently built a function in PS that collections critical/security and application updates from Windows 10/2016 machines. The issue I'm encountering is that my output is generating duplicate KB's. I've tried using Select-Object -Property Name -Unique (Sort-Object as well) to remove the duplicates and the outputs generates a different result.

    If anyone has any feedback on how to get the duplicates removed, it would be greatly appreciated.

    Thank you,

    Vick B.

     (Get-Package).Where({$_.Name -like "*Update*" -or $_.SwidTagText -like "*support.microsoft.com*"}).ForEach({
    # Create hashtable array.
        $psobject = [ordered] @{
            ComputerName = $env:COMPUTERNAME;
            KB = [RegEx]::match($PSItem.Name,'(KB[0-9]{6,7})').Groups[1].Value;
            InstalledOn = ([Xml]($PSItem.SwidTagText)).SoftwareIdentity.Meta.Date;
            Description = $PSItem.Name
        }
        # Create psobject
        $output = New-Object -TypeName psobject -Property $psobject
        # Write output
        Write-Output -InputObject $Output
    })
  • #109763

    Keymaster
    Points: 1,704
    Helping HandTeam Member
    Rank: Community Hero

    So, the reason why Select-Object -Property Name -Unique isn't doing what you want is that you're limiting the output to just the Name property. I'm assuming you still need some of the other properties. If you'd select ALL of the properties you need, you'd probably be okay, assuming that duplicate entries are 100% duplicates across all properties.

    You're taking a very programmer-y approach, rather than a more PowerShell pipeline-oriented approach; is that mandatory? Your current approach is going to kind of limit you in terms of what you can do "inline" since the C#-style methods you're using don't expose all of PowerShell's functionality.

    • #109792

      Participant
      Points: 0
      Rank: Member

      Hi Don,

      The approach is not mandatory at all. I don't have any C# knowledge and didn't realize that was the approach I was taking. Based on what you suggested, I've modified and tested this code and it is now giving the output I'm looking for.

      
      (Get-Package | Sort-Object -Property Name -Unique).Where({$_.Name -like "*Update*" -or $_.SwidTagText -like "*support.microsoft.com*"})
      
      

      Thank you for your feedback!

  • #109793

    Keymaster
    Points: 1,704
    Helping HandTeam Member
    Rank: Community Hero

    Yeah. so I'd probably refactor that entirely into a pipeline.

    Get-Package |
    
    Sort-Object -Property Name -Unique |
    
    Where-Object {$_.Name -like "*Update*" -or $_.SwidTagText -like "*support.microsoft.com*"}

    Those .ForEach() and .Where() methods are cute, but they're really their to entice C# people, in my mind. There are some minor performance differences, but sticking with a fully pipelined approach, for me, provides a lot more flexibility because I can drag in commands for which a cute method doesn't exist. This approach also works in older versions of PowerShell, where the .ForEach() and .Where() method doesn't exist.

     

     

    • #109796

      Participant
      Points: 0
      Rank: Member

      Thanks Don, I made those corrects based on your feedback, I've also attached the snippet since i only shared part of the function.

      
      Get-Command
      
      
      
      
      		
      	
  • #109799

    Participant
    Points: 0
    Rank: Member

    Re-posting snippet.

    Function Get-WUPatches
    
    {
    
    [CmdletBinding()]
    
    Param
    
    (
    
    [Parameter(Position=0,
    
    Mandatory = $false,
    
    ValueFromPipeline = $true,
    
    HelpMessage = "Enter one or more computer names separated by commas.")]
    
    [String[]]
    
    $ComputerName = $env:COMPUTERNAME,
    
    [Parameter(Position=1,
    
    Mandatory = $false,
    
    ValueFromPipeline = $true)]
    
    [System.Management.Automation.Credential()]
    
    $Credential = [System.Management.Automation.PSCredential]::Empty
    
    )
    
    # RECORD BY RECORD PROCESSING BLOCK
    
    PROCESS
    
    {
    
    # PROCESS ALL COLLECTION ITEMS IN AN SEQUENTIAL ORDER
    
    FOREACH ($Computer in $ComputerName)
    
    {
    
    # ERROR HANDLING
    
    TRY
    
    {
    
    # Getting the name of the Operating System
    
    $os = (Get-WmiObject -ComputerName $Computer -ClassName Win32_OperatingSystem -Credential $Credential -ErrorAction Stop).Caption
    
    # Execute cmdlets based on Operating System
    
    If($os -like "*Windows 10*" -or "*Windows Server 2016*")
    
    {
    
    # Local execution
    
    If($env:COMPUTERNAME -match $Computer)
    
    {
    
    # Gather a list of updates
    
    Get-Package |
    
    Sort-Object -Property Name -Unique |
    
    Where-Object {$_.Name -like "*Update*" -or $_.SwidTagText -like "*support.microsoft.com*"} |
    
    ForEach-Object {
    
    # Create hashtable array.
    
    $psobject = [ordered] @{
    
    ComputerName = $Computer;
    
    KB = [RegEx]::match($PSItem.Name,'(KB[0-9]{6,7})').Groups[1].Value;
    
    InstalledOn = ([Xml]($PSItem.SwidTagText)).SoftwareIdentity.Meta.Date;
    
    Description = $PSItem.Name
    
    }
    
    # Create psobject
    
    $output = New-Object -TypeName psobject -Property $psobject
    
    # Write output
    
    Write-Output -InputObject $output
    
    } # End Get-Package, Sort-Object, Where-Object and ForEach-Object cmdlets
    
    } # End If script block
    
    # Remote execution
    
    Else
    
    {
    
    # Start PSRemoting session
    
    Invoke-Command -ComputerName $Computer -Credential $Credential -ScriptBlock {
    
    # Gather a list of updates
    
    Get-Package |
    
    Sort-Object -Property Name -Unique |
    
    Where-Object {$_.Name -like "*Update*" -or $_.SwidTagText -like "*support.microsoft.com*"} |
    
    ForEach-Object {
    
    # Create hashtable array.
    
    $psobject = [ordered] @{
    
    ComputerName = $Computer;
    
    KB = [RegEx]::match($PSItem.Name,'(KB[0-9]{6,7})').Groups[1].Value;
    
    InstalledOn = ([Xml]($PSItem.SwidTagText)).SoftwareIdentity.Meta.Date;
    
    Description = $PSItem.Name
    
    }
    
    # Create psobject
    
    $output = New-Object -TypeName psobject -Property $psobject
    
    # Write output
    
    Write-Output -InputObject $output
    
    }
    
    } -ErrorAction Stop |
    
    Select-Object -ExcludeProperty PSComputerName, RunNameSpaceID
    
    } # End Get-Package, Sort-Object, Where-Object and ForEach-Object cmdlets
    
    } # If Script Block
    
    # Execution for Window 7/2008 R2/2012 R2 or eariler
    
    Else
    
    {
    
    # Query WMI32_QFE for eariler OS's.
    Get-WmiObject -ComputerName $Computer -ClassName Win32_QuickFixEngineering -Credential $Credential -ErrorAction Stop | ForEach-Object {
    # Create hashtable array.
    
    $psobject = [ordered] @{
    
    ComputerName = $Computer;
    
    KB = $PSItem.HotFixID;
    
    InstalledOn = $PSItem.InstalledOn;
    
    Description = $PSItem.Description
    
    }
    
    # Create psobject
    
    $output = New-Object -TypeName psobject -Property $psobject
    
    # Write output
    
    Write-Output -InputObject $output
    
    } # End Get-WmiObject & ForEach-Object cmdlet
    
    } # ELSE SCRIPT BLOCK
    
    } # TRY SCRIPT BLOCK
    
    CATCH
    
    {
    
    # Show error
    
    Write-Error -Message $PSCmdlet.ThrowTerminatingError($_)
    
    } # CATCH SCRIPT BLOCK
    
    } # FOREACH SCRIPT BLOCK
    
    } # PROCESS SCRIPT BLOCK
    
    } # End Function Get-WUPatches
    
    

The topic ‘Duplicate outputs in script cmdlet’ is closed to new replies.