How to parse out Failure Reason on Security Event Viewer Log

I have the following script and i having hard time to accommodate the following field: Failure Reason

Get-WinEvent -FilterHashtable @{LogName="Security"; Id="4625"} | ForEach-Object {
	$xml = [xml]($_.ToXml() -replace 'xmlns=', 'foo=')
	$_ | Select-Object -Property `
		@{n='Message';					e={$_.Message.Split("`r")[0]}},
		@{n='Failure Reason';				e={$xml.SelectSingleNode("Event/EventData/Data[@Name='FailureReason']").InnerText}}
		@{n='Account Name';				e={$xml.SelectSingleNode("Event/EventData/Data[@Name='TargetUserName']").InnerText}},
		@{n='Account Domain';			e={$xml.SelectSingleNode("Event/EventData/Data[@Name='TargetDomainName']").InnerText}},
		@{n='Workstation Name';			e={$xml.SelectSingleNode("Event/EventData/Data[@Name='WorkstationName']").InnerText.Trim("`r`n")}},
		@{n='Source Network Address';	e={$xml.SelectSingleNode("Event/EventData/Data[@Name='IpAddress']").InnerText}},
		@{n='Source Port';				e={$xml.SelectSingleNode("Event/EventData/Data[@Name='IpPort']").InnerText}}
} | Where-Object {$_.'Account Name'  -eq "imorgan"} | Format-List

Open in new window

This is the event viewer log (General Tab):
An account failed to log on.

	Security ID:		SYSTEM
	Account Name:		LLC1CCVCORPDC01$
	Account Domain:		CORP
	Logon ID:		0x3e7

Logon Type:			3

Account For Which Logon Failed:
	Security ID:		NULL SID
	Account Name:		imorgan
	Account Domain:		CORP

Failure Information:
	Failure Reason:		Account locked out.
	Status:			0xc0000234
	Sub Status:		0x0

Process Information:
	Caller Process ID:	0x1e0
	Caller Process Name:	C:\Windows\System32\lsass.exe

Network Information:
	Workstation Name:	LLC1CCVCORPDC01
	Source Network Address:
	Source Port:		57890

Detailed Authentication Information:
	Logon Process:		Advapi  
	Transited Services:	-
	Package Name (NTLM only):	-
	Key Length:		0

Open in new window

And the (Details Tab) XML:
- <Event xmlns="">
- <System>
  <Provider Name="Microsoft-Windows-Security-Auditing" Guid="{54849625-5478-4994-A5BA-3E3B0328C30D}" /> 
  <TimeCreated SystemTime="2018-11-09T13:04:38.547717600Z" /> 
  <Correlation /> 
  <Execution ProcessID="480" ThreadID="1252" /> 
  <Security /> 
- <EventData>
  <Data Name="SubjectUserSid">S-1-5-18</Data> 
  <Data Name="SubjectUserName">LLC1CCVCORPDC01$</Data> 
  <Data Name="SubjectDomainName">CORP</Data> 
  <Data Name="SubjectLogonId">0x3e7</Data> 
  <Data Name="TargetUserSid">S-1-0-0</Data> 
  <Data Name="TargetUserName">imorgan</Data> 
  <Data Name="TargetDomainName">CORP</Data> 
  <Data Name="Status">0xc0000234</Data> 
  <Data Name="FailureReason">%%2307</Data> 
  <Data Name="SubStatus">0x0</Data> 
  <Data Name="LogonType">3</Data> 
  <Data Name="LogonProcessName">Advapi</Data> 
  <Data Name="AuthenticationPackageName">MICROSOFT_AUTHENTICATION_PACKAGE_V1_0</Data> 
  <Data Name="WorkstationName">LLC1CCVCORPDC01</Data> 
  <Data Name="TransmittedServices">-</Data> 
  <Data Name="LmPackageName">-</Data> 
  <Data Name="KeyLength">0</Data> 
  <Data Name="ProcessId">0x1e0</Data> 
  <Data Name="ProcessName">C:\Windows\System32\lsass.exe</Data> 
  <Data Name="IpAddress"></Data> 
  <Data Name="IpPort">57890</Data> 

Open in new window

When i run the script, i do not get nothing. But if comment the "Failure Reason" line i get something.

Thanks for your help,
namergSystems AdministratorAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Where-Object {$_.'Account Name'  -eq "imorgan"}

Open in new window

Where-Object {$_.'TargetUserName'  -eq "imorgan"}

Open in new window

I think what you're after is a way to avoid the "failure reason" appearing as "%%2307" (or something similar).
You can use the following replacement for line 7.
@{n='Failure Reason';			e={$_.Message | Where-Object { $_ -match "(?m)Failure Reason:\s+(?<reason>.+)$" } | ForEach-Object { $Matches['reason'] }}},

Open in new window

I've looked for something that would be more universal than trying to parse the entire message property (and be subject to changes in language settings), similar to how you've done the retrieval from XML for other properties, but so far have not come up with a method.  Values like "%%2307" are insertion string placeholders.  Messages are formed from message text files, which typically are compiled as .DLLs but can also be included in .EXEs (and maybe other) resources.  The location of these message text files is stored in the registry under subkeys of HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog, that corresponds with the specific logname and source.  So essentially you have have something like HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\<eventlogname>\<eventsource>.  Once you locate the correct key, the location data is stored in a value named EventMessageFile, which points to the path of the .DLL (or other type of file).  There can also be a value for CategoryMessageFile, and ParameterMessageFile (these could all point to the same file, or different ones).  As I understand it, the ParameterMessageFile is where the insertion strings are defined for the placeholders which begin with a double percent sign (%%xxxx).

So far I haven't found any way to parse a message text file for insertion strings which correspond to their numbers.

The only bright side is that the message property of an event has already gone through the process of formatting (probably through the use of the FormatMessage function -, substituting all the placeholders with their insertion strings corresponding with the proper language.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
namergSystems AdministratorAuthor Commented:
Sweet, getting the right field and value. I am trying to export into a csv...but i get non-readable info.

.\Get-LogonHistory.ps1 | Export-Csv C:\Scripts\imorgan_logonhistory.csv -nti

Open in new window


Open in new window

If your script includes Format-* commands at the end, you can't then pipe that to Export-CSV.  Remove any Format-* commands from your script.  Then if you need to pipe the results to Format-List, Export-CSV, or whatever you can do so.
.\Get-LogonHistory.ps1 | Export-Csv C:\Scripts\imorgan_logonhistory.csv -nti
.\Get-LogonHistory.ps1 | Format-List
# etc.

Open in new window

namergSystems AdministratorAuthor Commented:
Thank You.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.