If you use {...} (a script block) to bind to a [string]-based parameter such as -RecipientFilter, the _verbatim contents_ of the script block (everything between { and }) is passed.
This precludes using variable references and expressions in filters, and this fundamental limitation should be stated in the topics of _all_ cmdlets that accept filters, and examples with variable use need to be given.
Better yet, given that wanting to reference variables in filters is probably the _typical_ use case, do not recommend using { ... } at all, and instead advise users to use _strings_, notably _expandable strings_ ("...") in order to embed variable and expression values.
I see that _some_ docs were improved to at least show the expandable-string technique - e.g., in https://github.com/MicrosoftDocs/office-docs-powershell/pull/3840/files - but it's important to do it comprehensively.
See this StackOverflow answer and this related one in the context of Active Directory.
⚠Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
@officedocsbot assign @yogkumgit
Hi @mklement0, thank you for your feedback.
Let me check what we can do here.
OK. I updated the descriptions and examples wherever there was a Filter or RecipientFilter (or *Filter parameter that was a string).
I'll follow-up by finding and fixing similar examples in the Exchange/Exchange Online repo
Thanks for the feedback.
@dariomws306 Thank you very much for the contribution. @chrisda thanks for updating the documentation with PR. @mklement0 Hope this update is helpful for you. Thanks for taking out some time to open the issue. Appreciate and encourage you to do the same in future also.
I appreciate the quick turnaround and the encouragement.
One thing I noticed - and since I don't have access to the cmdlets in question I can't tell if it's a problem:
The script-block syntax - { RemotePowerShellEnabled -eq $false } - makes the cmdlet see the following verbatim content: RemotePowerShellEnabled -eq $false
By contrast, "RemotePowerShellEnabled -eq $false" results in: RemotePowerShellEnabled -eq False, because the Boolean $false value is string-expanded.
Ditto for $true / True
Just want to make sure that these values are accepted interchangeably.
Yikes. Here's what I've determined after further testing:
As you described, you can't use double quotes around the whole OPath filter if you're searching for $true/$false or $null values (I call those 'system values'); you must use single quotes or braces.
So, either way, some sort of call-out required:
if you say "enclose the whole OPath filter in double quotes" you need to call out system values (which force you enclose the whole OPath filter in single quotes or braces).
If you say "enclose the whole OPath filter in braces" you need to call out variable values (which force you enclose the whole OPath filter in double quotes).
Is one of these choices inherently better than the other? Braces _appear_ to be most compatible across a wide variety of search values, which would be important if you're using multiple criteria in the same filter.
I've summarized the results in the following table. I've also included this information (and more) in an as yet unpublished update to Recipient filters in Exchange PowerShell commands. I'll probably keep the update regardless of which way we decide to go (since it's good to know and helps explain all possible outcomes).
Comments are welcome.
|Search value|OPath filter enclosed
in double quotation marks|OPath filter enclosed
in single quotation marks|OPath filter enclosed
in braces|
|:-----|:-----:|:-----:|:-----:|
|'Text'|X||X|
|"Text"|||X|
|'$Variable'|X|||
|500|X|X|X|
|'500'|X||X|
|"500"|||X|
|$true||X|X|
you must use single quotes or braces.
` (backtick) is PowerShell's escape character, so you can also `-escape individual $ characters inside "..." to prevent their expansion: "foo -eq `$true" yields the following verbatim: foo -eq $true.
So if someone does want to use an expandable string (string interpolation, "..."), they can use ` as shown (an - obviously more cumbersome - alternative is to use an _expression_ that uses _string concatenation_: ("one -eq '$foo' -or two -eq " + '$false')
The _Braces_ construct ({ ... }) creates a [scriptblock] instance - a type that creates a _code block_ for later execution.
It is therefore _unrelated to strings_, and - to avoid confusion - shouldn't be _repurposed_ to act as a string literal of sorts.
That it _looks_ like PowerShell code but _isn't_, created the massive confusion around the ActiveDirectory module's -Filter parameter previously linked to.
P.S.: The AD provider actually can _itself_ resolve simple variable references such as 'sAMAccountName -eq $name', but not _expressions_ such as 'sAMAccountName -eq $obj.Name'
I don't know if that's the case here too - it's certainly worth clarifying, along with what _system values_ refers to - but either way it is important to make it obvious that a _string_ is being passed _whose interpretation is up to the cmdlet_ - and, conversely, to avoid the misconception that just any _piece of PowerShell code_ can be passed, which is the misconception that use of { ... } promotes.
So, the definitive information around this will be at Additional OPATH syntax information. I've updated it to include the fact that an escaped dollar sign in a system value allows the entire OPath filter to be enclosed in single quotes, double quotes, or braces. And I always define 'system values' with the examples of "$true, $false, or $null". Those are the parameter/property values that are most used in Exchange, I don't know exactly what else to call them, and I'm not sure if there are any others.
Fundamentally, braces work in filters as long as you aren't trying to expand variables. Exchange topics have been using braces around OPath filters since the Exchange 2007 days, and for alphanumeric values or 'system values', they work just fine.
The Braces construct ({ ... }) creates a [scriptblock] instance - a type that creates a code block for later execution.
It is therefore unrelated to strings, and - to avoid confusion - shouldn't be repurposed to act as a string literal of sorts.
I get it, and I'm trying to do the technically accurate thing here, but what exactly is the harm in using OPath filters surrounded by braces if the filters work as written now that we call out the fact that you can't use braces with variables? I'm 'asking' mostly rhetorically, because I'm convinced the answer is mostly 'there is no harm'.
Because of 12+ years of using braces in our content, I feel we must somehow pay homage to the fact that they actually work, and I think this sentence that's now used everywhere captures the correct and most reasonable sentiment:
You need to enclose the whole OPath filter in double quotation marks " or " single quotation marks ' '. Although any OPath filter object is technically a string and not a script block, you can still use braces { }, but only if the filter doesn't contain variables that require expansion.
This serves as a necessary safety blanket to everyone with working filters who might be confused/upset with our sudden global change in syntax after 12 years.
If any other call-out is required when using braces, we should definitely add it.
OK. No further response, so closing.
Sorry, dropped the ball.
what exactly is the harm in using OPath filters surrounded by braces if the filters work as written now that we call out the fact that you can't use braces with variables?
The harm is that you're promoting a misconception about what the syntax { ... } does, which may bite you in other scenarios.
{...}, as script-block literals meant to contain _PowerShell code_ can be _situationally repurposed_ to act as _verbatim strings_, but (a) that's not what they're for, (b) it doesn't always work, and (c) you may form a misconception about the _true_ purpose of the syntax.
Try the following for instance (a contrived example, but it illustrates the point that {...} doesn't contain a _string_):
Write-Host { 1 + } # BOOM!
Conversely, if you _do_ know that { ... } are script blocks, you will expect the contents of the block to _work the same way it does in PowerShell_ - since it doesn't, confusion is likely to ensue - your clarification notwithstanding.
All good, but I know from experience that using braces for the _Filter_ and _RecipientFilter_ parameters in Exchange cmdlets works in all cases EXCEPT when you're trying to expand variables.
I think I've done enough by modifying the parameter descriptions any general mention of OPath filter syntax EVERYWHERE in Exchange PowerShell/cmdlet reference content to include this statment (or a version of it):
You need to enclose the whole OPath filter in double quotation marks " " or single quotation marks ' '. Although any OPath filter object is technically a string and not a script block, you can still use braces { }, but only if the filter doesn't contain variables that require expansion.
That's about as far as I'm willing to go, unless you can suggest alternative or additional text that I need to add.
works in all cases EXCEPT when you're trying to expand variables.
Yes, it _works_, but my point is that use of {...} ultimately does more harm than good, for the reasons stated.
What you've done and the wording you propose are great improvements, but the problem is that your explanation won't always be present when users look at examples and real-world code that uses {...}
And even users who've read the explanation may forget over time, and scratch their heads over why their perfectly well-formed (ostensible) script block doesn't work (doesn't recognize their variables).
Another problematic aspect: users may expect that _all regular PowerShell operators_ work inside { ... }, which is clearly not the case.
None of the OPath examples anywhere in Exchange PowerShell/cmdlet reference topics use {...} anymore, in particular in the original object of this issue, which was New-AddressList.
We're in the process of updating Exchange conceptual/procedural content for similar _Filter_/_RecipientFilter_ OPath examples to replace {...} with '...' or "...". It will happen; it's just a question of when. From an issue tracking perspective, that work is far outside the scope of this specific issue.
So is there any reason I shouldn't close this issue?
I'm glad to hear it all.
Feel free to close.
Thank you. Closing.
Most helpful comment
OK. I updated the descriptions and examples wherever there was a Filter or RecipientFilter (or *Filter parameter that was a string).
I'll follow-up by finding and fixing similar examples in the Exchange/Exchange Online repo
Thanks for the feedback.