Querying Windows Update Errors using PowerShell Get-WinEvent or Get-WMIObject - powershell

Trying to create a simple Windows Update error query using Get-WinEvent (although I would prefer querying a WMI Object for use with SCUP):
get-winevent -logname System| Where-Object {$_.ProviderName -eq "Microsoft-Windows-WindowsUpdateClient"}
This seems to work for the most part. However, it only returns informational events and not errors. Are these located somewhere else and, if so, how would I query them? For some background, there is a specific update failure occurring on approximately 10% of Windows 10 machines in my environment (missing assembly file) and I want to target it so that I can deploy a solution.
A solution using Get-WinEvent is fine, though I would prefer using Get-WMIObject if possible.

You can use the Win32_NTLogEvent like this:
Get-WmiObject Win32_NTLogEvent |?{($_.LogFile -eq 'System') -and ($_.SourceName -eq 'Microsoft-Windows-WindowsUpdateClient') }
Note: You can further filter with Type which will tell you about information or error or warning.
Hope it helps.

I cannot find anything that actually states this but it looks like Get-WinEvent by default only returns information messages. If you want to see the other then you need to tell it to return those. One way to do it is with -FilterHashtable.
Get-WinEvent -FilterHashtable #{LogName='System';Level=1,2}
That would return only warnings and error.
1 - Error
2 - Warning
4 - Information
You can look at the enum [System.Diagnostics.EventLogEntryType] to see where I got the numbers from.
Looking at MS you can see what the hashtable filter supports..
LogName=<String[]>
ProviderName=<String[]>
Path=<String[]>
Keywords=<Long[]>
ID=<Int32[]>
Level=<Int32[]>
StartTime=<DateTime>
EndTime=<DataTime>
UserID=<SID>
Data=<String[]>
*=<String[]>
If your WMI queries are having similar issues then you can do something like this
Get-WmiObject -class Win32_NTLogEvent -filter "(logfile='Application') AND (type='error')"
You can find some tangential examples here

Write a WMI query (this overrides weird event type filters):
Get-WmiObject -Query "Select * from Win32_NTLogEvent" |?{(($_.LogFile -eq 'System') -and ($_.Type -in ("Error", "Warning"))) -and ($_.SourceName -eq 'Microsoft-Windows-WindowsUpdateClient') }

Okay, so after doing some additional research, I stumbled upon this website that sheds some light on the issue I'm running into. Essentially, while most, if not all Windows Events are logged in the C:\Windows\System32\Winevt\logs folder, not all Windows Events are replicated in WMI by default.
In PowerShell, Get-WinEvent appears to use the above folder when querying its event data, whereas Get-EventLog uses the Win32_WinNTLogEvent WMI class.
In my original question, I mentioned that I was unable to query Windows Update error events using Get-WinEvent. This is because I was pointing to the System log file, which does not contain the information. The Microsoft-Windows-WindowsUpdateClient/Operational log file (literal path being C:\Windows\System32\Winevt\logs\Microsoft-Windows-UpdateClient%4Operational.evtx) does contain this information, so my query can simply be changed up using something similar to the following:
Get-WinEvent -logname "Microsoft-Windows-WindowsUpdateClient/Operational" | Where-Object {$_.LevelDisplayName -eq "Error"}
In order to query the same data returned by Get-WinEvent using the Win32_NTLogEvent WMI class, the registry must first be modified. Again, the link I posted in this answer describes the process in greater detail, but essentially I performed the following registry mod:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Microsoft-Windows-WindowsUpdateClient/Operational]
"File"="%SystemRoot%\\System32\\Winevt\\Logs\\Microsoft-Windows-WindowsUpdateClient%4Operational.evtx"
"Primary Module"="Microsoft-Windows-WindowsUpdateClient/Operational"
"Microsoft-Windows-WindowsUpdateClient/Operational"=hex(2):25,00,53,00,79,00,73,00,74,\
00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,00,5c,00,73,00,79,00,73,00,74,00,\
65,00,6d,00,33,00,32,00,5c,00,77,00,65,00,76,00,74,00,61,00,70,00,69,00,2e,\
00,64,00,6c,00,6c,00,00,00
Note: The "Microsoft-Windows-WindowsUpdateClient/Operational" Expanded String (REG_EXPAND_SZ) at the end there is pointing to %SystemRoot%\system32\wevtapi.dll
Once the registry was modified, I was able to query the error events as follows:
Get-WmiObject -query "SELECT * FROM Win32_NTLogEvent WHERE LogFile='Microsoft-Windows-WindowsUpdateClient/Operational' AND Type='Error'"
Somewhat of a pain considering that Windows Update errors should probably be present in the Win32_NTLogEvent WMI class by default (ah, Microsoft). Still, this essentially resolves my question.
One additional point to mention. The website above states that, upon editing the registry, you'd be able to query the new events immediately. I had to reboot my machine first.

Related

Get-Hotfix throws Get-HotFix : Provider load failure

I have huge list of servers and workstations where I need to review patches for a specific date range. I have my command working, but there is one machine that apparently has a WMI issue, and its throwing an error, effectively aborting the entire operation. I attempted to add some command to ignore the error and keep going but nothing seems to catch and continue, it always aborts and I never see the output.
Get-HotFix -ComputerName (Get-Content .\TARGETS.txt) | Where { $_.InstalledOn -gt '7/29/2017' -AND $_.InstalledOn -lt '8/25/2017' } | sort InstalledOn
I've tried adding $erroractionpreference = 'SilentlyContinue' but that just doesn't throw the error to the screen, the hotfix list still doesn't get displayed.
You may need to trace down the process tree.
Get-Hotfix uses the Win32_QuickFixEngineering WMI class. You can usually find out provider information in PS help files or you can use wbemtest.
Once you know that information, you can check the Windows Event Viewer > View >Show Analytic and Debug logs option. Navigate to Microsoft > Windows > WMI Activity, and right click Operational log then select Enable Log from the pane on the right. Then right click the Trace log, and similarly enable.
The next time you try and run the command (or maybe in a new PS session, not sure) there should be more detailed information to help you determine the root problem.
MS Scripting Guy Ed Wilson details the process with a how-to here as well:
https://blogs.technet.microsoft.com/heyscriptingguy/2012/09/12/use-powershell-to-troubleshoot-provider-load-failure/

Selecting specific lines/data from Get-Winevent message in powershell

I am trying to extract a specific line from the message output of a get-winevent cmdlet and haven't been able to find a way to do this (I could be searching incorrectly but am still learning more advanced scripting methods). What I am running is this:
Get-WinEvent -ComputerName $DC -FilterHashtable #{Logname='Security';Keywords='9007199254740992';Data=$userid} -MaxEvents 1 | Select Message | Format-List
Which will return with a message similiar to this (Changed some info to generic info):
Message : The computer attempted to validate the credentials for an account.
Authentication Package: MICROSOFT_AUTHENTICATION_PACKAGE_V1_0
Logon Account: jdoe
Source Workstation: Generic-Computername
Error Code: 0x0
I am attempting to create an easy way to find a computer someone last logged into for faster troubleshooting but I am unable to filter out only the Source Workstation line, I could just not have the correct syntax for a good search to find the results I am looking for but I have been searching for about a week now and haven't found anything close to what I am looking for, any help would be great!
Im not sure what information you want to retrieve but im pretty sure there is a better way then using Get-WinEvent to obtain that information. However, if you just want to get the value of Source Workstation you can do that with a regex:
$event = Get-WinEvent `
-ComputerName $DC `
-FilterHashtable #{Logname='Security';Keywords='9007199254740992';Data=$userid} `
-MaxEvents 1 `
| Select -expand Message
[regex]::Match($event, 'Source Workstation:\s*(.*)\s*').Groups[1].Value

Early filtering within the root\ccm\cliensdk namespace

I recently wrote a script that updates registry values on remote desktops after checking, for instance, that a certain application, MyApp, is properly installed.
The aforementioned application is installed/deployed by SCCM (2012, not R2 for the moment).
In the process of optimizing the script, I wanted to change the test of the install state of MyApp (from late to early filtering).
So far, no luck and so far, no explanation either.
I can't properly understand why it seems not possible to do some early filtering with the following command :
gwmi -ComputerName myserver -Namespace root\ccm\clientsdk -query "select * from ccm_application where Fullname='MyApp'"
Of course, nor can we use :
gwmi -ComputerName myserver -Namespace root\ccm\clientsdk -class ccm_application -filter "Fullname='MyApp'"
Late filtering, of course, works but I wanted (and expected) early filtering to work, especially since I am checking the Install state of an app for quite a lot of remote desktops.
Of course, I do know that I could (can) use SCCM for that purpose (executing a script only if ...) but that still does not explain why I can't do early filtering.
Whenever I try to query that class with my installation while specifying either properties or a filter, I get the error "Provider is not capable of the attempted operation". It doesn't matter if I use Get-WmiObject or Get-CimInstance.
I get the same error when I run this:
PS C:\> WMIC.EXE /NAMESPACE:\\root\ccm\clientsdk PATH ccm_application GET FullName
Node - <SERVERNAME>
ERROR:
Description = Provider is not capable of the attempted operation
PS C:\> wmic /NAMESPACE:\\root\ccm\clientsdk PATH ccm_application WHERE "FullName='Java 32-bit'"
Node - <SERVERNAME>
ERROR:
Description = Provider is not capable of the attempted operation
Although this works just fine:
WMIC.EXE /NAMESPACE:\\root\ccm\clientsdk PATH ccm_application
Seems like a limitation of the provider then, not a problem with your code. -Filter and -Property don't work by design.
Note that I am using 2012 R2 SP1 (5.00.8239.1000), so this may not perfectly apply. However, it seems unlikely that they would remove the functionality from the provider moving from 2012 to 2012 R2.

Get user's last logon from sccm with powershell

I'm trying to create wmi query to sccm to get PC, where was user's last logon.
Something like:
Get-WmiObject -namespace $SCCMNameSpace -computer $SCCMServer -query "select lastlogon, PCname from sms_? where LastLogonUserName='$SamAccountName'" | select lastlogon, PCname.
I can see this information in sccm report, but I don't know what class I have to use for a query. I'm using sms_r_system for getting IP and computer name.
Is anyone knew sccm class with this information or sql queries will be better for me?
Which report are you viewing the data with? You might want to open up the report's SQL code, figure out which ConfigMgr SQL views it's referencing, and then translate that to the SCCM WMI class names. The WMI class names closely correlate to the SQL view names.
Use a WMI browser like SAPIEN's free WMI Explorer GUI tool to help explore the root\sms\site_xyz WMI namespace and discover which class you are looking for. You can also use Windows PowerShell to help discover which class contains this property:
gwmi -name root\cimv2 -list | ? { ($_.Properties.Name -join ',') -match 'lastlogon' }
Note: Make sure you're using PowerShell version 3.0 Release Preview for the above command, otherwise it won't work right.

WMI Generic Failure when attempting to remove software updates from a SCCM deployment package

I'm using SCCM 2007 and Powershell to automate some mundane tasks in my environment. One of these tasks is to remove all expired or superseded updates in all of my deployment packages. I've went over the Microsoft documentation for the SMS_SoftwareUpdatesPackage WMI class and the RemoveContent method you're supposed to use for this function but keep hitting a wall.
Here's the code I'm currently using.
$x = gwmi SMS_SoftwareUpdatesPackage -computer sccm -namespace root\sms\site_unh -filter "PackageID = 'UNH00277'"
$array = #()
$array += 34827
$x.RemoveContent($array,$true)
Pretty simple, right? I've tried so many different combinations of syntax I'm going crazy. The only lead I've been tracking is that maybe my array datatype isn't right. According to the documentation, it's supposed to be a UInt32 Array but that's just a hunch.
The "solution" for this was to simply set $ErrorActionPreference to SilentlyContinue to skip over this failure. After I added that, it successfully removed all of the updates I wanted.