Strings aren’t automatically processed as code - they’re strings. Even in double quotes with a subexpression, the result is a string, not the $True/$False that Where-Object needs to see.
$query = {$_.passwordneverexpires -eq $true}
Defines a script block, which is code. You could probably pass that directly to the Where-Object parameter…
Where-Object $query
You could possibly also use the call operator (Call operator - Run - PowerShell - SS64.com) to “invoke” the string, although that’s kind of a poor practice in terms of readability.
I’ll also note that in this specific example, you’re probably not in an efficient model. You’ve already got a Where-Object clause; just do all your filtering there. And, it might be worth writing an -LdapFilter instead, for Get-ADUser, so you could do all your filtering right on the DC.
I was able to get it after using an ‘invoke-expression’ cmdlet and minor altering, but you make good points on using the ldapfilter, i’ll look into it.