TL:DR - How do I get Select-Object to recognize/parse/interpret custom properties when they come from an imported text file?
Hi
I’m trying to import a list of properties to display for Select-Object from a text file including custom properties, but the custom properties wont appear.
Now, regular properties can be imported easily enough using Get-Content and then pumping that into Select-Object
Date
Computer
ID
@{L=‘MemberName’;E={([System.Security.Principal.SecurityIdentifier]$_.MemberSid).Translate([System.Security.Principal.NTAccount])}}
@{L=“TagetUserName”;E={$_.TargetUsername.ToUpper()}}
Where it breaks down is the custom properties. The regular properties appear, but the custom properties do not.
Currently I’m doing some gymnastics to pump the entire command into a [scriptblock]::Create, and it works (this involves putting all the text of myProperty.txt on one line).. but seems quite inelegant and I feel it’s potentially open to abuse.
Date Computer ID MemberName TagetUserName
---- -------- -- ---------- -------------
26/08/2025 2:40:48 PM *REDACTED* 4733 *REDACTED* POWER USERS
Why am I doing this? I’m trying to process many different Windows Event Log ID’s and turning them around for CSV storage and for email notifications and daily/weekly/monthly reports. I’d like to be able to have external dictionaries for each event ID rather than defining all the conversions and filters within the script.
I agree with Olaf, there are probably better ways to handle this. However, to make your example functional, you can use invoke-expression on the calculated properties
If I’m, understanding your objective you want to keep a list of property names and calculated properties in a file. There’d be one such file that corresponds to each log file type. You’ll store the list of properties in a simple text file with each property or calculated property occupying its own line.
If that’s correct, wouldn’t something as simple as this work?
From their original post it looks to me like they are.
Date
Computer
ID
@{L=‘MemberName’;E={([System.Security.Principal.SecurityIdentifier]$_.MemberSid).Translate([System.Security.Principal.NTAccount])}}
@{L=“TagetUserName”;E={$_.TargetUsername.ToUpper()}}
I think the issue here is that the -Property parameter of Select-Object is expecting an array of “objects”. A calculated property is technically a hashtable, but when you import it as text from a file it’s just a string, rather than a hashtable, so it doesn’t get treated like a calculated property.
@krzydoug example works because it invokes the string text representing a hashtable and converts it in to a hashtable. I would either do Krzydoug’s method, or look for a different way to store the data.
My inclination would be to write a function for processing these Event Viewer logs you’re interested in, then you can bake the Select-Object work into the function.
But I’m guessing you didn’t test that for yourself because it does not work. If props.txt contains a list of properties, and calculated property definitions, like the OP proposed then $p becomes an array of strings. Trying to call an array with @p throws a syntax error, and even if you call it as $ instead it still doesn’t work for the reasons i said in my previous reply.
I think for my use case @krzydoug solution wins, iterating over each line with the regex match with Invoke-Expression. I had almost got there in a similar foreach loop with ConvertFrom-StringData but it just never occurred to me to use Invoke-Expression.
Honorable mention to @Olaf for the dot sourcing solution. I PoC’d that as well and works just the same. After head-slamming for 30 minutes with Import-PowerShellDataFile I figured I’m either too silly or impatient to make that work so no feedback on that one.
@RichMath s solution I couldn’t get it working, closest I got was the same result I had when I started.
Looks like we’re done here. Thanks for all the replies, much appreciated
I probably wouldn’t use dot-sourcing. There’s no way to get rid of it unless you start a new PS session, and there’s no way to limit any variable names from being exposed outside the contents of the file you’re dot-sourcing.
Regex’s are useful, but they aren’t always easy to understand (especially true for anyone that may need to maintain/modify the code). They can also be complex to write/debug.
I’d go with the creation of a module. You can load it when you need it, and unload it when you no longer need it. You can also control what names (variables and functions) are visible outside the code in the module.
Here’s an example of a simple module that you can take the contents of your “property” files (the ones you’re having problems with) and paste their contents into individual arrays.
$p1 = @(
"Date",
"Computer",
"ID",
@{L='MemberName';E={([System.Security.Principal.SecurityIdentifier]$_.MemberSid).Translate([System.Security.Principal.NTAccount])}},
@{L="TargetUserName";E={$_.TargetUsername.ToUpper()}}
)
$p2 = @(
"some other property names or calculated properties go here (see $p1 for example)"
)
function FunctionOne{
param(
[PSCustomObject]$record
)
Select-Object -InputObject $record -Property $p1
}
function FunctionTwo{
param(
[PSCustomObject]$record
)
Select-Object -InputObject $record -Property $p2
}