Home > database >  Where-Object Error When Passing Get-Content as Variable
Where-Object Error When Passing Get-Content as Variable

Time:02-04

First, my PS knowledge is very basic, so know that up front.

I'm working on a basic script to search EventIDs in archived .evtx files and kick out "reports". The Where-Object queries are in .txt files stored in .\AuditEvents\ folder. I'm trying to do a ForEach on the .txt files and pass each query to Get-WinEvent.

Here's an example of how the queries appear in the .txt files: {($_.ID -eq "11")}

The script is:

$ae = Get-ChildItem .\AuditEvents\

ForEach ($f in $ae) {
    $qs = Get-Content -Path .\AuditEvents\$f
    Get-WinEvent -Path .\AuditReview\*.evtx -MaxEvents 500 | Select-Object TimeCreated, ID, LogName, MachineName, ProviderName, LevelDisplayName, Message | Where-Object $qs | Out-GridView -Title $f.Name
    }

This is the error:

Where-Object : Cannot bind argument to parameter 'FilterScript' because it is null.
At C:\Users\######\Desktop\PSAuditReduction\PSAuditReduction.ps1:6 char:177
  ... e, ProviderName, LevelDisplayName, Message | Where-Object $qs | Out-G ...
                                                                ~~~
      CategoryInfo          : InvalidData: (:) [Where-Object], ParameterBindingValidationException
      FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.WhereObjectCommand

CodePudding user response:

Your symptom implies that $qs is $null, which in turn implies that file .\AuditEvents\$f is empty.

However, even if it had content, you couldn't pass the resulting string as-is to the (positionally implied) -FilterScript parameter of Where-Object requires a script block ({ ... }).

You must create a script block from the string explicitly, using [scriptblock]::Create().

A simplified example:

# Simulated input using a literal string instead of file input via Get-Content
$qs = '{ 0 -eq $_ % 2 }'  # Sample filter: return $true for even numbers.

# Remove the enclosing { and }, as they are NOT part of the code itself 
# (they are only needed to define script-block *literals* in source code).
# NOTE: If you control the query files, you can simplify them
#       by omitting { and } to begin with, which makes this 
#       -replace operation unnecessary. 
$qs = $qs.Trim() -replace '^\{(. )\}$', '$1'

# Construct a script block from the string and pass it to Where-Object
1..4 | Where-Object ([scriptblock]::Create($qs)) # -> 2, 4

Note:

  • Your code assumes that each .\AuditEvents\$f file contains just one line, and that that line contains valid PowerShell source code suitable for use a Where-Object filter.

  • Generally, be sure to only load strings that you'll execute as code from sources you trust.


Taking a step back:

As Abraham Zinala points out, a much faster way to filter event-log entries is by using Get-WinEvent's -FilterHashtable parameter.

This allows you to save hastable literals in your query files, which you can read directly into a hashtable with Import-PowerShellDataFile:

# Create a file with a sample filter.
'@{Path=".\AuditEvents\.*evtx";ID=11}' > sample.txt

# Read the file into a hashtable...
$hash = Import-PowerShellDataFile sample.txt

# ... and pass it to Get-WinEvent
Get-WinEvent -MaxEvents 500 -FilterHashtable $hash | ...
  • Related