Jump to content

Export Windows Update HotFix List from PC's


Recommended Posts

Posted

Hi

I've found the following script which I've amended to fit my needs.

But having a problem getting it to output the HotFixId's. I've added it in the code to select HotFixID but not sure how to output it.

 

Please help

Many thanks

 

 

 

 

 

$timeoutSeconds = 10

$results = @()

 

$serverlist = Get-ADComputer -Filter * -SearchBase "OU=" -Properties *

 

foreach ($server in $serverlist) {

#re-initialise $hash

$hash = @{}

$connex = Test-Connection $server.Name -Count 1 -TTL 100 -Quiet

if ($connex) {

$code = {

(Get-HotFix -computername $using:server.Name | Select HotFixId,description, @{l="InstalledOn";e={[DateTime]$_.psbase.properties["installedon"].value}}| Sort InstalledOn)[-1] | Select -ExpandProperty InstalledOn

}

$j = Start-Job -ScriptBlock $code

if (Wait-Job $j -Timeout $timeoutSeconds) {

$patch = Receive-Job $j

}

Remove-Job -force $j

 

}

else {

#server can't be contacted

$patch = "offline"

}

$hash=[ordered]@{

Computername=$server.Name

HotfixID=

PatchDate=$patch

OperatingSystem=$Server.OperatingSystem

}

[pscustomobject]$hash

$results += [pscustomobject]$hash

}

 

#output just the computer names, sorted alphabetically

$results | sort computername | select computername | Out-file "d:\test\names.txt"

 

#output the computer names, patch date and operating system, sorted by computer name

$results | sort computername | Export-Csv "d:\test\patches.csv"

 

 

 

Script from "https://tmacstips.wordpress.com/2015/03/16/the-poor-persons-way-to-finding-the-last-patch-date-on-multiple-systems/"

Posted

Before I look any further can we sort out the - Filter * -Properties *

 

That is not the best way to get the details you are after and do that in a large environment you will not be popular either.

Posted
Before I look any further can we sort out the - Filter * -Properties *

 

That is not the best way to get the details you are after and do that in a large environment you will not be popular either.

 

Okay what way would you recommend?

 

 

Thanks

Posted

Right so one approach is to set the filter to return computers based on an OS filter so -Filter 'OperatingSystem - like "Windows Server *"' -properties Name

 

I have covered on more than one occasion on here the -Filter * -properties * when doing searches against AD you should absolutely avoid doing this. You can be smarter with your searches by firstly doing a Get-ADComputer / Get-Aduser -Identity -properties * and then digging out the best way to filter and then returning only the properties that are needed for your next operation/ export.

Posted

Thank you for that

 

So now i'm using $Computers = Get-ADComputer -Filter {OperatingSystem -like '*Windows 10*'}

 

Can anyone help with the exporting the HotfixID field?

 

Thanks

Posted

Basically I'm updating pcs with 4 updates and wanted to just confirm which pcs have all 4 updates. Rather then logging in to each and checking that way. As we have about 3000 pcs. So I was going to generate a log for each OU and check which machines need patching.

 

Thanks

Nick

Posted

Hmm so using your example something like this will give you all the hotfixes installed on your computers:

$timeoutSeconds = 10
$computerList = Get-ADComputer -Filter "OperatingSystem -like '*Windows 10*'"

$results = foreach ($computer in $computerList) {

   $connex = Test-Connection $computer.Name -Count 1 -TTL 100 -Quiet
   if ($connex) {
       $code = { Get-HotFix -computername $using:computer.Name |
           Select HotFixId, InstalledOn | Sort InstalledOn -Desc
       }
       $j = Start-Job -ScriptBlock $code
       if (Wait-Job $j -Timeout $timeoutSeconds) {
           $hotFixInfo = Receive-Job $j
       }
       Remove-Job -force $j

       foreach ($hotfix in $hotFixInfo) {
           [PSCustomObject]@{
               Computername    = $computer.Name
               HotfixID        = $hotfix.HotFixID
               PatchDate       = $hotfix.InstalledOn.ToString('dd/MM/yyyy')
               OperatingSystem = $computer.OperatingSystem
           }
       }
   }
   else {
       Write-Warning "Computer $($computer.name) can't be contacted"
       #computer can't be contacted
   }
}

$results | Export-Csv D:\test\patches.csv -NoTypeInformation

 

You could even just filter it so that you only export the names of the computers that are missing all four hotfixes to a file to save you having to manually filter the results in Excel

Posted

The issue is what you have done to the hash splatting here (Get-HotFix -computername $using:server.Name | Select HotFixId,description, @{l="InstalledOn";e={[DateTime]$_.psbase.properties["installedon"].value}}| Sort InstalledOn)[-1] | Select -ExpandProperty InstalledOn

 

You cannot just change that line to select the HotFixID and description and @fordea approach is one way to solve this issue.

Posted (edited)

That's great thank you fordea.

 

How would I go about filtering as you suggested?

 

Thanks

Edited by tri_94
Posted

You can add an array to the top of your code containing a list of the patches to want to search for:

 

$patchesToSearch = @(
   'KB4519565'
   'KB4486156'
)

 

And then change this bit:

 

foreach ($hotfix in $hotFixInfo) {
   [PSCustomObject]@{
       Computername    = $computer.Name
       HotfixID        = $hotfix.HotFixID
       PatchDate       = $hotfix.InstalledOn.ToString('dd/MM/yyyy')
       OperatingSystem = $computer.OperatingSystem
   }
}

 

to:

$patchesNeeded = $patchesToSearch.Where{$hotFixInfo.HotFixID -notcontains $_} 
       if ($patchesNeeded.count -gt 0) {
           [PSCustomObject]@{
               Name = $Computer.Name
               PatchesNeeded = $patchesNeeded
           }
       } else {
           Write-Verbose "Computer $($computer.Name) is already patched"
       }

Posted

Hi again

 

Right I've changed the script to the following below. however on the export I get "System.Collections.ObjectModel.Collection`1[system.Management.Automation.PSObject]" for Patches needed.

 

Thanks for your all help

 

 

 

$patchesToSearch = @(

'kb4514358'

'kb4514366'

'kb4516115'

'kb4523204'

'kb4523205'

)

 

 

 

$timeoutSeconds = 10

$computerList = Get-ADComputer -Filter "OperatingSystem -like '*Windows 10*'"

$results = foreach ($computer in $computerList) {

 

$connex = Test-Connection $computer.Name -Count 1 -TTL 100 -Quiet

if ($connex) {

$code = { Get-HotFix -computername $using:computer.Name |

Select HotFixId, InstalledOn | Sort InstalledOn -Desc

}

$j = Start-Job -ScriptBlock $code

if (Wait-Job $j -Timeout $timeoutSeconds) {

$hotFixInfo = Receive-Job $j

}

Remove-Job -force $j

 

$patchesNeeded = $patchesToSearch.Where{$hotFixInfo.HotFixID -notcontains $_}

if ($patchesNeeded.count -gt 0) {

[PSCustomObject]@{

Name = $Computer.Name

PatchesNeeded = $patchesNeeded

}

} else {

Write-Verbose "Computer $($computer.Name) is already patched"

}

 

}

}

$results | Export-Csv D:\test\name.csv -NoTypeInformation

Posted

if you change the following:

 

[color=#333333][PSCustomObject]@{[/color]
[color=#333333]    Name = $Computer.Name[/color]
[color=#333333]    PatchesNeeded = $patchesNeeded[/color]
[color=#333333]}[/color]

 

to

 

[color=#333333][PSCustomObject]@{[/color]
[color=#333333]    Name = $Computer.Name[/color]
[color=#333333]    PatchesNeeded = $patchesNeeded -join ', '[/color]
[color=#333333]}[/color]

 

it should output the patches needed as strings to the csv.

Posted

Hi again

 

Worked great however there are too small issues now. If the pc is off or not responding there is no way of knowing in the export. And also a couple of the updates have been replaced so i really need to tell the script check if this one or that one hope this makes sense.

 

Thanks again for all your help.

Posted

So if the PC is off or already fully patched you also want those output to the same CSV? Also with regards for checking for updates that have been replaced how do you want to handle these cases:

  • None of the group of patches are installed: I'm guessing you want to output the latest one to the CSV instead of all of them?
  • One of the patches in a group is installed but not the latest one: Do you want the latest one output to the CSV or no output for that group of patches?

Posted

Hi again

 

Yes I was after being able to run the script against an ou and then have a csv telling me which machines need updating. I need to know if the machine hasn’t been checked so I can get it checked and no just think that every machine is fine so again it would be great to show fully patched too

 

As for the patch group it’s a case of at least two on my list have been replaced with new ones. I’m not worried if the pcs have the old update. But when I run the script currently any machine with new update would be missing the old one and so that shows on the list. The way is exporting currently I’m not to know that the new update has been installed as it just tells me that the old one is missing. (Hope that makes sense)

 

So ideally not only do I need to be able to put a list of updates but for some I need to be able to put two or more update kb’s with some kind of if like statement so it will check for update1 or update2 and if it finds either one then not to show in the required update csv.

 

Thanks

Nick

Posted

Ok so I think I get what you're after. There's a couple of amendments I'd suggest to make based on your requirements. It's probably easier if I just post the full code instead of the changed bits as there's quite a few:

 

$patchesToSearch = [ordered]@{
   'group1' = 'KB453300', 'KB453071', 'KB451956'
   'security patch 01' = 'KB4465065'
   'KB4480979' = 'KB4480979'
   'whatever name you like' = 'KB1234567'
}
$timeoutSeconds = 10
$computerList = Get-ADComputer -Filter "OperatingSystem -like '*Windows 10*'"
$results = foreach ($computer in $computerList) {

   $connex = Test-Connection $computer.Name -Count 1 -TTL 100 -Quiet
   if ($connex) {
       $code = { Get-HotFix -computername $using:computer.Name |
           Select HotFixId, InstalledOn | Sort InstalledOn -Desc
       }
       $j = Start-Job -ScriptBlock $code
       if (Wait-Job $j -Timeout $timeoutSeconds) {
           $hotFixInfo = Receive-Job $j
       }
       Remove-Job -force $j

       $patchesNeeded = $patchesToSearch.Values | Foreach-Object {
           if ($_ -is [array]) {
               if ($null -eq (Compare-Object $_ $hotFixInfo.HotFixID -IncludeEqual -ExcludeDifferent)) {
                   $_[-1]
               }
           } elseif ($hotFixInfo.HotFixID -notcontains $_) {
               $_
           }
       }
       switch ($patchesNeeded.count) {
           0 {$patchesNeededStr = 'Fully patched'}
           {$_ -gt 0} {$patchesNeededStr = $patchesNeeded -join ', '}
       }
   } else {
       $patchesNeededStr = 'Offline'
   }
   [PSCustomObject]@{
       Name          = $Computer.Name
       PatchesNeeded = $patchesNeededStr
   }
}
$results | Export-Csv D:\test\name.csv -NoTypeInformation

 

The main difference is switching from using an array to a hashtable for listing the patches you want to search for, this makes it easier to create 'groups' of patches to search for as 2D arrays can be a pain in Powershell. The keys of the hashtable don't matter so use whatever names for the keys that you like. If you create a group of patches (like 'group1' = 'KB453300', 'KB453071', 'KB451956' in my example) and the computer doesn't have any of those patches then it will only output the last one (KB451956) to the CSV for that group as it assumes that it is the most recent one. Also it should now output any computers that are fully patched or offline to the CSV too.

  • Thanks 1

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now



×
×
  • Create New...