tri_94 Posted December 10, 2019 Posted December 10, 2019 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/"
HPlum78 Posted December 10, 2019 Posted December 10, 2019 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.
tri_94 Posted December 10, 2019 Author Posted December 10, 2019 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
HPlum78 Posted December 10, 2019 Posted December 10, 2019 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.
tri_94 Posted December 11, 2019 Author Posted December 11, 2019 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
fordea Posted December 11, 2019 Posted December 11, 2019 Is it just the latest installed hotfix for each computer that you want to export or all hotfixes for each computer?
tri_94 Posted December 11, 2019 Author Posted December 11, 2019 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
fordea Posted December 11, 2019 Posted December 11, 2019 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
HPlum78 Posted December 11, 2019 Posted December 11, 2019 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.
tri_94 Posted December 11, 2019 Author Posted December 11, 2019 (edited) That's great thank you fordea. How would I go about filtering as you suggested? Thanks Edited December 11, 2019 by tri_94
fordea Posted December 11, 2019 Posted December 11, 2019 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" }
tri_94 Posted December 12, 2019 Author Posted December 12, 2019 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
fordea Posted December 12, 2019 Posted December 12, 2019 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.
tri_94 Posted December 14, 2019 Author Posted December 14, 2019 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.
fordea Posted December 16, 2019 Posted December 16, 2019 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?
tri_94 Posted December 16, 2019 Author Posted December 16, 2019 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
fordea Posted December 17, 2019 Posted December 17, 2019 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. 1
tri_94 Posted December 17, 2019 Author Posted December 17, 2019 thanks again Fordea you really are a powershell god. Works great. Thank you.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now