Selecting specific lines/data from Get-Winevent message in powershell - 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

Related

Is there a way to find which user run what application on a server using Powershell

I am trying to find a way to find out who has ran an application (for example SQL) on a server, just to get some idea.
I tried Get-Process but this doesn't give me historic information, I want to get historical information
Get-Process -IncludeUserName *
what I want the return resule is "name of application", "user who ran it" and the last datetime it was ran by that user'
As for ...
I am trying to find a way to find out who has ran an application (for
example SQL) on a server, just to get some idea.
What you are asking for here is software metering.
SQL is a service that is always running once it is installed, so, no individual user is ever going to be running it. So, that is a bad example. MS Word for example would be a better example.
Yet there is nothing native in PowerShell that does this, software metering, but of course PowerShell can look at event logs. Yet if your auditing is not setup correctly then it's moot. This is better for a software metering tool, and there are several out there. So, why try and reinvent the wheel.
As for ...
I tried Get-Process but this doesn't give me historic information, I
want to get historical information
That is not what a process is nor what Get-Process is for. It, Get-Process only checks for and lists whatever process is currently running, regardless of what/who launched it.
As for...
what I want the return resule is "name of application", "user who ran
it" and the last datetime it was ran by that user'
As long as the process is running, you can get this, with that cmdlet.
However, what are you trying to accomplish by this?
Again, there are purpose built tools to meter software use.
https://learn.microsoft.com/en-us/sccm/apps/deploy-use/monitor-app-usage-with-software-metering
If you must go down this reinvent the wheel road, using scripting, then you need a task watcher on the target machines, which watches for the WinWord process to appear.
Get-Process -IncludeUserName |
Where ProcessName -EQ 'Winword'
... you then write those results to a file or database or your own event log each time you see that process.
Use PowerShell to Create and to Use a New Event Log
New-EventLog -LogName ScriptingGuys -Source scripts
When the command runs, no output appears to the Windows PowerShell console. To ensure the command actually created a new event log, I use
the Get-EventLog cmdlet with the –List parameter. Here is the command
and the associated output.
Write-EventLog -LogName ScriptingGuys -Source scripts -Message “Dude, it works … COOL!” -EventId 0 -EntryType information
Or just to a file
Get-Process -IncludeUserName |
Where ProcessName -EQ 'Winword' |
Select-Object -Property Name, StartTime, Username |
Export-Csv -Path 'F:\Temp\AppLaunchLog.csv' -Append
Import-Csv -Path 'F:\Temp\AppLaunchLog.csv'
# Results
Name StartTime UserName
---- --------- --------
WINWORD 5/23/2019 9:02:53 PM WS01\LabUser001

Need a Way to Test a user against remote server using Powershell

My only objective is to validate the user account against bunch servers. I am using below commands to do it.
$creds2= Get-Credential
$servers = Get-Content ('C:\Users\vishnuvardhan.chapal\Documents\Widnows Servers success in 139 and 445.txt')
$servers | ForEach-Object {Get-WmiObject Win32_ComputerSystem -ComputerName $_ -Credential $creds2} | Out-GridView
Here, I am encountering two problems.
1) In the Grid view, I am just getting the hostname but without FQDN like shown in below screenshot.
2) Above screen is only for succeeded servers and for failed ones (for the servers, where authentication is failing) I am getting the output in Powershell window like below screen.
Now, my goal is to combine both the output's in at single place. Is it possible? If yes, How to do it? Please shed some light to it.
Apart from above is there any way to test it more easily, i mean a direct command to test the user authentication against a remote server??
FYI...My only goal for this exercise is to validate user authentication not to get some details from a remote computer.
Out-GridView is not a good way to handle these things. Recommended to convert that into JSON or some kind of a format and then parse it in files or however you wish to.
There are multiple ways to check that but error handling will solve your issue:
try
{
$creds2= Get-Credential
$servers = Get-Content ('C:\Users\vishnuvardhan.chapal\Documents\Widnows Servers success in 139 and 445.txt')
$servers
foreach($server in $servers)
{
try
{
Get-WmiObject Win32_ComputerSystem -ComputerName $Server -Credential $creds2
}
catch
{
"Error in accessing the server - $Server with the given credential. Kindly validate."
}
}
}
catch
{
$_.Exception.Message
}
So within the loop also I have added a try catch because if one server is failing, it will proceed with the next server from the list and that will capture the error with server name along with the message.
Hope it helps.

O365 PShell similar recipient search does not return correct results using Where {$_.RecipientAddress -like

Good morning!
Have a curious quirk with Microsoft's Office365 EOL powershell cmdlets where the "-like" filter does not seem to report accurate results.
Here's an example:
get-messagetrace -startdate 08/20/2018 -enddate 08/30/2018 -senderaddress bob#diversifiedstaffing.com
I get four results which are expected.
However, if I use the -like filter:
get-messagetrace -startdate 08/20/2018 -enddate 08/30/2018 | Where {$_.SenderAddress -like '*diversified*'}
I get zero results. The expectation would be anything with "diversified" in the sender domain address would be returned. I should see at least four results.
Any thoughts on this? I believe I'm missing something very simple but just can't quite figure it out.
Thank you in advance; have a wonderful week!
Update 09/26/2018: A ticket is active with MS as I was able to replicate the issue on several domains including large ISP's. MS acknowledges the behaviour is odd and should not be producing the results I'm seeing. Will update once the fix is found.
Curiously, the Search and Compliance GUI does return the expected results. We have ruled out different Powershell and .NET versions (For reference, PS 5.0 / .NET 4.7)
Update 10/03/2018: Continued progress with MS. As an example using a major local ISP:
get-messagetrace -startdate 09/03/2018 -enddate 10/03/2018 | Where {$_.SenderAddress -like '*telus*'}
Result here is 10 when I exported to CSV and counted manually. MS suggested declaring this as a variable and counting it:
PS U:\> $messages = get-messagetrace -startdate 09/03/2018 -enddate 10/03/2018 | Where {$_.SenderAddress -like '*telus*'}
PS U:\> $messages.Count
10
I later found that a month's worth of email is limited to 1000 results by default; I can expand the results up to 5000:
PS U:\> $messages = get-messagetrace -startdate 09/03/2018 -enddate 10/03/2018 -PageSize 5000 | Where {$_.SenderAddress -like '*telus*'}
PS U:\> $messages.Count
46
Going to keep poking. If I'm on the right track, the "discrepancy" may not be a discrepancy at all but a forced limit by MS skewing my results in PS versus eDiscovery.
Are you sure you are signed into MSOL and remoted into EXO?
If you are just on your internal Exchange server doing this, then you are using the local Exchange cmdlets and those will not bring anything back from O365.
If you are on your internal Exchange server and imported the EXO cmdlets via remoting and did not uniquely identify them, and / or did not use -clobber, then you can have other issues.
I just did a quick hit of my tenant and get the expected results...
Connect-MsolService -Credential $MSOLCreds
Connect-AzureAD -Credential $MSOLCreds
$ExchangeOnlineSession = New-PSSession -ConfigurationName 'Microsoft.Exchange' `
-ConnectionUri 'https://outlook.office365.com/PowerShell-liveid/' `
-Authentication Basic -Credential $MSOLCreds -AllowRedirection
Import-PSSession $ExchangeOnlineSession -Prefix 'EXO'
Get-EXOMessageTrace -startdate 08/01/2018 -enddate 08/30/2018 |
Where {$_.SenderAddress -like '*MyO365VanityDomain*'} |
Format-Table -AutoSize
# Results
Received Sender Address Recipient Address Subject
-------- -------------- ----------------- -------
8/29/2018 8:48:10 AM SomeEXOUsername#MyO365VanityDomain SomeRecipientAddress Undeliverable: Your Off...
8/28/2018 3:00:30 PM SomeEXOUsername#MyO365VanityDomain bounce-108_html... Undeliverable: Your Off...
8/22/2018 8:55:10 AM SomeEXOUsername#MyO365VanityDomain SomeRecipientAddress Undeliverable: Weekly d...
If you are doing this from a remote workstation and testing this for Exchange on-prem and O365, you need to setup to different implicit remote sessions and when using the commands, use the right cmdlet associated with the right session.
I use prefixes (all on-prem uses EXP and online as EXO) regularly to avoid confusion as shown above, whether I am on the Exchange server directly or via remoting.
So, with prefix, the cmdlets noun part gets renamed:
Get-EXPMessageTrace
Get-EXOMessageTrace
I have a final answer for what was an exercise in frustration.
What I've discovered is everything before the pipe character | is direct to the server(s), whereas everything else is manipulating the gathered data local to PowerShell.
When running this:
get-messagetrace -startdate 09/03/2018 -enddate 10/03/2018
The results can be misleading as there may be multiple events for each message. A really good example would be where the recipient is a distribution list. Rather than the cmdlet counting every result, only the original item is counted. This has the net effect of appearing to have "fewer results" than what is expected.
The get-messagetrace cmdlet can be combined with historical message tracing to fully expand gathered results.
In short:
On-premises will see the expected results without issue
Hybrid solutions are a mixed bag depending on which server is
connected (prem vs cloud)
Fully cloud based solutions will have a "watered down" version of
the results and admins will need to take this into account.

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

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.

Powershell Write-EventLog / Get-WinEvent Message issues

The first command creates an entry in the event log, it seems to be working because I can see the message data in event viewer. The issue is when reading it back from powershell the message field is empty.
write-eventlog System -source 'Microsoft-Windows-Kernel-General' -eventid 999 -message 'Kernel something or other'
get-winevent -filterHashTable #{Logname = 'System'; ID = '999'}| select-object -first 10
Maybe this picture explains it better. Notice the message column is blank.
The event is being written correctly, to read it back use this:
get-winevent -filterHashTable #{Logname = 'System'; ID = '999'}|
select-object -first 10 | select timecreated,providername,
#{n="Message";e={$_.properties.Value}}
The reason you can't see it in the message column is evident when launching eventvwr:
The description for Event ID 999 from source Microsoft-Windows-Kernel-General cannot be found. Either the component that raises this event is not installed on your local computer or the installation is corrupted. You can install or repair the component on the local computer.
If you want to write custom messages from custom sources use New-EventLog cmdlet, here is the Scripting Guy's tutorial: http://blogs.technet.com/b/heyscriptingguy/archive/2013/06/20/how-to-use-powershell-to-write-to-event-logs.aspx
Here is the snip that ended up making it work. Credit to Raf for the link where I found this answer.
$source = "Some Name"
If ([System.Diagnostics.EventLog]::SourceExists("$source") -eq $false)
{New-EventLog -LogName $log -Source "$source"}