Powershell variable without automatic double quotes - powershell

Searched but can't find an answer to this (though there are very similar threads).
I have a variable $var = 'string','string2,'string3'
There is a cmdlet I'd like to pass this $var to
Set-DistributionGroup -ManagedBy $var
However, it ends up looking like this:
Set-DistributionGroup -ManagedBy "'string','string2','string3'"
And, I want:
Set-DistributionGroup -ManagedBy 'string','string2','string3'
Is this possible? I wonder if it is, and likely is, a limitation with the Exchange PowerShell cmdlet Set-DistributionGroup. ManagedBy is a MultiValuedProperty per docs.

The basic logic of passing a array of "users" to the -ManagedBy parameter works without any special intervention required. The only thing I can question is the line in you post
Set-DistributionGroup -param $var
Was that an attempt at generalizing the parameter switch name? Makes for some odd output regardless
[PS] >$list = "jim","tim"
[PS] >Set-DistributionGroup -param $list
A positional parameter cannot be found that accepts argument 'jim tim'.
You should have just been able to do this:
[PS] >$list = "jim","tim"
[PS] >Set-DistributionGroup -ManagedBy $list
Assuming that jim and tim resolve to mailboxes. If they were aliases for example.

Related

Searching partial names with Powershell [duplicate]

I'd like to check if a user account already exists in the system.
$SamAc = Read-Host 'What is your username?'
$User = Get-ADUser -Filter {sAMAccountName -eq "$SamAc"}
I'm not sure why, but $User will always return null even if {sAMAccountName -eq "$SamAc"} is supposed to be true.
What am I missing here?
Edit:
This is what was missing:
$User = Get-ADUser -Filter "sAMAccountName -eq '$SamAc'"
Editor's note: The script block ({ ... }) was replaced with a string.
There is valuable information in the existing answers, but I think a more focused summary is helpful. Note that the original form of this answer advocated strict avoidance of script blocks ({...}) and AD-provider variable evaluation, but this has been replaced with more nuanced recommendations.
Option A: Letting the AD provider resolve - stand-alone only - variable references:
Get-ADUser -Filter 'sAMAccountName -eq $SamAc' # note the '...' quoting
Note the use of '...', i.e. a verbatim (single-quoted) string, because the string's value is to be passed as-is to the AD provider (cmdlet).
While use of a script block ({ ... }), Get-ADUser -Filter { sAMAccountName -eq $SamAc }, technically works too (its verbatim content, sans { and }, is converted to a string), it is conceptually problematic - see bottom section.
Do not quote the variable reference ("$SamAc").
Use only stand-alone variable references (e.g, $SamAc); expressions are not supported (e.g., $user.SamAccountName or "$name*" or $("admin_" + $SamAc)); if necessary, use an intermediate, auxiliary variable; e.g.:
$name = "admin_" + $SamAc; Get-ADUser -Filter 'sAMAccountName -eq $name'
Generally, only a subset of PowerShell's operators are supported, and even those that are do not always behave the same way - see bottom section.
Caveat: If you use Get-ADUser via an implicitly remoting module - whether self-created via Import-PSSession or, in PowerShell v7+, via the Windows Compatibility feature - neither '...' nor { ... } works, because the variable references are then evaluated on the remote machine, looking for the variables there (in vain); if (Get-Command Get-ADUser).CommandType returns Function, you're using an implicitly remoting module.
If implicit remoting is involved, you MUST use string interpolation, as shown next.
Option B: Using PowerShell's string interpolation (expandable strings), up front:
Get-ADUser -Filter "sAMAccountName -eq `"$SamAc`"" # note the "..." quoting
Using "...", i.e. an expandable (double-quoted) string makes PowerShell interpolate (expand) all variable references and subexpression up front, in which case the AD provider sees only the (variable-free) result.
As shown above, for string operands embedded quoting then is necessary.
For embedded quoting, '...' is a simpler alternative to `"...`" (`" is an _escaped "), but note that this assumes that an expanded value doesn't itself contain ', which is a distinct possibility with last names, for instance.
Also, be sure to `-escape constants such as $true, $false, and $null inside the "..." string, which are always recognized by the AD provider; i.e., use `$true, `$false and `$null, so that PowerShell doesn't expand them up front.
Caveat: Using an expandable string does not work with all data types, at least not directly: for instance, the default stringification of a [datetime] instance (e.g., 01/15/2018 16:00:00 is not recognized by the AD provider; in this case, embedding the result of a call to the instance's .ToFileTime() (or .ToFileTimeUtc()?) method into the string may be necessary (as suggested in the comments on this post); I'm unclear on whether there are other data types that require similar workarounds.
On the plus side, string interpolation allows you to embed entire expressions and even commands in a "..." string, using $(...), the subexpression operator; e.g.:
# Property access.
Get-ADUser -Filter "sAMAccountName -eq `"$($user.SamAccountName)`""
# String concatenation
Get-ADUser -Filter "sAMAccountName -eq `"$('admin_' + $SamAc)`""
Background
Any argument you pass to -Filter is coerced to a string first, before it is passed to the Get-ADUser cmdlet, because the -Filter parameter is of type [string] - as it is for all provider cmdlets that support this parameter; verify with Get-ADUser -?
With -Filter in general, it is up to the cmdlet (the underlying PowerShell provider) to interpret that string, using a domain-specific (query) language that often has little in common with PowerShell.
In the case of Get-ADUser, that domain-specific language (query language) is documented in Get-Help about_ActiveDirectory_Filter.
Note: As of this writing, no newer version of this legacy topic exists; this GitHub issue requests one.
With Get-AdUser, the language supported by -Filter is certainly modeled on PowerShell, but it has many limitations and some behavioral differences that one must be aware of, notably:
As Santiago Squarzon points out, these limitations and difference stem from the fact that the language is translated into an LDAP filter behind the scenes, it is therefore constrained by its features and behaviors. (Note that you can use the -LDAPFilter parameter in lieu of -Filter to directly pass an LDAP filter).
Only a limited subset of PowerShell operators are supported, and some exhibit different behavior; here's a non-exhaustive list:
-like / -notlike only support * in wildcard expressions (not also ? and character sets/ranges ([...])
'*' by itself represents any nonempty value (unlike in PowerShell's wildcard expressions, where it also matches an empty one).
Instead of -eq "" or -eq $null to test fields for being empty, use
-notlike '*'.
Certain AD fields, e.g., DistinguishedName, only support '*' by itself, not as part of a larger pattern; that is, they only support an emptiness test.
There is no support for regex matching.
-lt / -le and -gt / -ge only perform lexical comparison.
Referencing a nonexistent / misspelled property name causes the Get-ADUser command to quietly return $null.
As stated, only stand-alone variable references are supported (e.g, $SamAc), not also expressions (e.g., $SamAc.Name or $("admin_" + $SamAc))
While you can use a script block ({ ... }) to pass what becomes a string to -Filter, and while this syntax can be convenient for embedding quotes, it is problematic for two reasons:
It may mislead you to think that you're passing a piece of PowerShell code; notably, you may be tempted to use unsupported operators and expressions rather than simple variable references.
It creates unnecessary work (though that is unlikely to matter in practice), because you're forcing PowerShell to parse the filter as PowerShell code first, only to have the result converted back to a string when the argument is bound to -Filter.
This one bit me when I first started to work with the ActiveDirectory module, and it was a pain to figure out.
The -Filter parameter for the ActiveDirectory module cmdlets is actually looking for a string. When you do {sAMAccountName -eq "$SamAc"} as the value, it is actually looking for "sAMAccountName -eq ""`$SamAc"""
Basically, Powershell parses the parameter and turns its value into a string, and will not interpolate the variable. Try building the string before hand, and it should work.
Something like this:
$SamAc = Read-Host 'What is your username?'
$filter = "sAmAccountname -eq ""$SamAc"""
$User = Get-ADUser -Filter $filter
I have to comment on this because it really aggravated me to sort this out.
Joseph Alcorn has the right idea. The filter parameter takes a string and then evaluates that in order to process the filter. What trips people up with this is that you are given the option to use curly brackets instead {}, and this doesn't work as you'd expect if you were using Where... it still has to be treated like a string.
$SamAc = Read-Host 'What is your username?'
$User = Get-ADUser -Filter "sAMAccountName -eq '$SamAc'"
I recommend sticking to quotes to make it more clear/readable for yourself and others and to avoid potential syntax errors, or stick to Where{} in the pipeline. When doing so, I find it best to use double-quotes on the outside & single-quotes on the inside so you still get intellisense detection on the variable.
Simply remove the quotes around your variable:
$SamAc = Read-Host 'What is your username?'
$User = Get-ADUser -Filter {sAMAccountName -eq $SamAc}
This should work just fine.
if (($ADUser = Get-ADUser -filter "SamAccountName -eq '$(Read-Host Username)'") -ne $null) {$ADUser.SamAccountName} else {"Not Found"}
Little addendum if anyone like me got here and was still tearing their hair out:
-properties *
Would be quite a common this to have in this query. Doesn't work, I'm sure someone smarter than me can figure it out
-properties mail,cn,wtf
etc does work as expected
It took me quite a bit to just use
Do not quote the variable reference ("$SamAc").
TXH so much
Okay, I got mine to finally work using the following syntax and using the following example from up above:
Previously:
$User = Get-ADUser -Filter "sAMAccountName -eq '$SamAc'"
Working Version:
$user = Get-aduser -Filter "sAMAccountName -eq '$($SamAc)'"
I had to add $($ ) to $SamAc before PowerShell could access the variable string value.

Again with Powershell: I know how to create a file but I don't know how to write in it [duplicate]

I am creating a script and want to both use Write-Host and Write-Output
As I work I want a backup of information I pull from AD to also become attached to a .txt file.
This is more of a backup in case I miss a piece of information and need to go back and recreate a ticket. Anyways I have a sample of my script, form what I can tell it should be working. If someone with a bit more experience can take a look or point me in the right direction I would appreciate it. If I need to add any more of the script I can provide this. Thanks in Advance.
Import-Module activedirectory
$object = Get-ADUser $sid -Properties * | Select-Object EmailAddress
Write-Host Email: $object.EmailAddress
Write-Output ("Email: $object.EmailAddress") >> C:\psoutput\psoutput.txt -Append
This will create the .txt file of course but is also add other information such as:
Email: #{GivenName=myfirstname; Surname=mylastname; SamAccountName=myid; DisplayName=lastname, firstname - Contingent Worker; City=; EmailAddress=myemailaddress#mywork.com; EmployeeID=; Enabled=True; OfficePhone=; MobilePhone=(555) 555-5555; LockedOut=False; LockOutTime=0; AccountExpirationDate=05/09/2020 00:00:00; PasswordExpired=False; PasswordLastSet=12/03/2019 12:16:37}.EmailAddress
-Append
I am looking to have the output like the following...
name: username
email: user email address
phone: user phone number
etc...
All general information from Active Directory
Thanks again for the suggestions
Don't use write-output. Use (Get-ADUser $sid -properties mail).mail.
Like this:
Add-Content -Path "FilePath" -Value "Email: $((Get-ADUser $sid -properties mail).mail)"
Write-Output ("Email: $object.EmailAddress")
As an aside: No need for (...) here.
This doesn't do what you expect it to: it stringifies $object as a whole and then appends .EmailAddress verbatim; in order to embed an expression, such as accessing a property inside "..." (an expandable string), you need $(), the subexpression operator.
Write-Output "Email: $($object.EmailAddress)" >> C:\psoutput\psoutput.txt
See this answer for an overview of the syntax in PowerShell expandable strings.
Or, more simply, using PowerShell's implicit output behavior (use of Write-Output is rarely necessary):
"Email: $($object.EmailAddress)" >> C:\psoutput\psoutput.txt
>> C:\psoutput\psoutput.txt -Append
>> is effectively an alias for Out-File -Append (just like > is for just Out-File), so not only is there no need for -Append, it isn't interpreted by >>, which accepts only the filename operand.
Instead, -Append was interpreted by Write-Output, which is why it ended up literally in your output file.
Perhaps surprisingly, while a redirection such as >> C:\psoutput\psoutput.txt is typically placed last on the command line, that is not a syntactic requirement: other arguments may follow.
I am looking to have the output like the following..
It sounds like you want formatting as provided by the Format-List cmdlet:
$object | Format-List >> C:\psoutput\psoutput.txt
Note that > / >> / Out-File apply the default string formatting, i.e. the same representation that would by default display in the console.
By using an explicit Format-* cmdlet, you can control that formatting, but note two things about Out-File in general:
As you're outputting for-display formats, the resulting file may not be suitable for further programmatic processing.
To prevent truncation of values, you may have to pass a -Width argument to Out-File, control the enumeration length of nested properties with $FormatEnumerationLimit, and, in the case of Format-Table, specify -AutoSize.
You don't really need to use Write-Output at all. Try this to just get your string to your file:
("Email: " + $object.EmailAddress) >> C:\psoutput\psoutput.txt
You don't need to specify append because '>>' already does that for you

Using comparison operators on parameters of Powershell cmdlets

I'm trying to use the Get-BrokerDesktop cmdlet,
Like any other Powershell cmdlets I can pass it parameters to filter out the results to my needs. So, I could do something like,
Get-brokerdesktop -RegistrationState Unregistered
Which would return an object that only has Unregistered as its RegistrationState.
How would I go about having the ones that are not Unregistered?
I tried,
Get-brokerdesktop -RegistrationState -ne Unregistered
Which is invalid syntax.
Actually, I just noticed an example at the bottom of the linked documentation...
The trick here is to use -Filter like so,
Get-BrokerDesktop -Filter { RegistrationState -ne 'Unregistered' }
Or even better in this case, as proposed by #TheIncorrigible1,
-Filter 'RegistrationState -ne "Unregistered"'

Why doesn't $PSItem behave as expected when using a bracket-based -Filter argument?

I was assisting a user with this question, linked to my answer here: Powershell script to add users to A/D group from .csv using email address only?
Initially I wrote the script as follows, using a bracket-based filter for Get-AdUser like follows:
Import-CSV "C:\users\Balbahagw\desktop\test1.csv" |
Foreach-Object {
# Here, $_.EmailAddress refused to resolve
$aduser = Get-ADUser -Filter { EmailAddress -eq $_.EmailAddress }
if( $aduser ) {
Write-Output "Adding user $($aduser.SamAccountName) to groupname"
Add-ADGroupMember -Identity groupname -Members $aduser
} else {
Write-Warning "Could not find user in AD with email address $($_.EmailAddress)"
}
}
However, $_.EmailAddress failed to populate a value. However, changing the Get-ADUser filter to a string-based filter worked as intended:
$aduser = Get-ADUser -Filter "EmailAddress -eq '$($_.EmailAddress)'"
What is the strangeness I'm experiencing, and why? Is it because when I'm using brackets, it's treated as a new scope and the $PSItem won't follow?
-Filter parameters are generally string parameters (verify with
Get-Help Get-AdUser -Parameter Filter)
They generally do not accept PowerShell code - filters are provider-specific and often have their own syntax, although it happens to be PowerShell-like in the case of the AD cmdlets.
Also, they generally have no knowledge of PowerShell variables (see below).
Thus, when a script block ({ ... }) is passed, it is converted to a string, which evaluates to its literal contents (everything between the opening { and the closing }):
{ EmailAddress -eq $_.EmailAddress }.ToString() yields the literal string EmailAddress -eq $_.EmailAddress - without any evaluation - and that's what Get-AdUser sees - no evaluation takes place.
In a presumably well-meaning but misguided effort to support the widespread, but ill-advised practice of passing script blocks to the -Filter parameter of AD cmdlets, it seems that these cmdlets actually explicitly expand simple variable references such as $_ in the string literal they receive, but that doesn't work with expressions, such as accessing a property of a variable ($_.EmailAddress)
Therefore, -Filter arguments should generally be passed as expandable strings ("..."); in the case at hand:
-Filter "EmailAddress -eq '$($_.EmailAddress)'"
That is, the only robust solution is to use strings with the variable parts baked in, up front, via string expansion, as shown above.
For values that are neither numbers nor strings, such as dates, you may have to use a literal string ('...') and rely on the AD provider's ability to evaluate simple references to PowerShell variables (e.g., $date) - see this answer of mine for details.
As stated, the syntax of AD filters is only PowerShell-like: it supports only a subset of the operators that PowerShell supports and those that are supported differ subtly in behavior - see Get-Help about_ActiveDirectory_Filter.
It is tempting to use script blocks, because the code inside requires no escaping of embedded quotes / no alternating of quote chars and no use of subexpression operator $(...). However, aside from using script blocks as strings being inefficient in general, the problem here is that the script block is making a promise that it cannot keep: it looks like you're passing a piece of PowerShell code, but you're not - and it works only in simple cases (and then only due to the misguided accommodation mentioned above); generally, it's hard to remember under what circumstances it doesn't work and how to make it work if it fails.
It is therefore really unfortunate that the official documentation uses script blocks in its examples.
For a more comprehensive discussion, see this answer of mine.
You're not wrong, it's the module's fault
The type of payload you have to use with the -Filter parameter differs depending on which provider you're working with, a design decision which can be pretty confusing!
The output of Get-Help Get-ADUser -Parameter Filter gives you some pretty detailed examples of the different syntax options you can use with the Active Directory Provider's implementation of Filter syntax.
Here's an example:
#To get all user objects that have an e-mail message attribute, use one of the following commands:
Get-ADUser -Filter {EmailAddress -like "*"}
It looks like the ActiveDirectory provider places the specific restriction that you must wrap the input in quotes. Here's what happens when I look for my account without putting quotes around my e-mail.
Get-ADUser -Filter {EmailAddress -eq stephen#foxdeploy.com}
Get-ADUser : Error parsing query: 'EmailAddress -eq stephen#foxdeploy.com'
Error Message: 'syntax error' at position: '18'.
But adding quotes? It works!
Get-ADUser -Filter {EmailAddress -eq "stephen#foxdeploy.com"}
DistinguishedName : CN=Stephen,CN=Users,DC=FoxDeploy,DC=local
Enabled : True
GivenName : Stephen
Name : Stephen
ObjectClass : user
ObjectGUID : 6428ac3f-8d17-45d6-b615-9965acd9675b
SamAccountName : Stephen
SID : S-1-5-21-3818945699-900446794-3716848007-1103
Surname :
UserPrincipalName : Stephen#FoxDeploy.local
How to make yours work
Now, because of this confusing filter implementation, you will need to change your user lookup on line 5 to the following:
$aduser = Get-ADUser -Filter "EmailAddress -eq `"$($_.EmailAddress)`""
We are providing the -Filter payload as a String. Next we want to use String Expansion to pull out the .EmailAddress property, so we wrap the string in $( ) to signal string expansion. Finally, the provider wants our filter comparison wrapped in quotes, so we put double quotes around it, and then escape the quotes using the backtick character.
And now it should work.
TLDR - blame the provider and blame the module, there are so many inconsistencies with the Active Directory module.

Differences between | and $

Can anyone explain to me the differences I'm seeing in either using a | to pipe one command to another or using $ to 'pipe' it a different way (sorry not sure if the use $ is actually considering piping).
So… this works:
Get-Mailbox -ResultSize Unlimited |
where { $_.RecipientTypeDetails.tostring() -eq "SharedMailbox" } |
Get-MailboxPermission
Which is great, however because I want to place another where command after the Get-MailboxPermission which doesn't work above I then tried to use this:
$Mailbox = Get-Mailbox -ResultSize Unlimited |
where { $_.RecipientTypeDetails.tostring() -eq "SharedMailbox" }
Get-MailboxStatistics -Identity $Mailbox |
where { $_.IsInherited.tostring() -eq "False" }
It causes me to get this error:
Cannot process argument transformation on parameter 'Identity'. Cannot convert the "System.Collections.ArrayList" value of type "System.Collections.ArrayList" to type "Microsoft.Exchange.Configuration.Tasks.GeneralMailboxOrMailUserIdParameter".
Surely using | or $ is the same in the sense that it pipes through the results to the next command or am I completely wrong?
I don't have an exchange shell here to test but I guess I can still explain the basics and point you in the right direction.
The pipe | is used to redirect output from one command to another command. $ in Powershell is the character which defines that the character sequence right behind it is either a variable (e.g. $Mailbox as an example for a normal variable or $_ as an example for a variable that holds data that has been piped through from a previous command) or an expression. An example for an expression one is $(4+5).
Or in a more frequently used example:
PS C:\Users\Administrator> $file = (get-childitem)[0]
PS C:\Users\Administrator> write-output "The fullname of $file is $($file.fullname)"
The fullname of .ssh is C:\Users\Administrator\.ssh
In that example it is actually necessary to use an expression, because variable detection inside a string doesn't recognize dots as separator between variable and a variable member (fullname is a member of $file).
If it's not clear to you why there is a point and what members are, you should probably look into object oriented programming a bit because Powershell is object oriented through and through.
In your 2nd example you just save everything that's returned by your Get-Mailbox command in the $Mailbox variable. The $Mailbox variable is available as long as you don't delete it or leave its scope (in this case, the powershell session). You can actually use the variable as input for multiple commands without losing its data.
When using the pipe, the data returned by your first command is only accessible for the command behind the pipe and after that it's gone.
That's probably the difference you're interested in.
As for your actual problem: Powershell tells you that it's not expecting to be handed a variable of type System.Collections.ArrayList, which is what Get-Mailbox returns. The technet help is unclear as to what Get-Mailbox specificly returns, but I strongly guess it's an ArrayList of Mailbox-Objects. You can check it like this:
$Mailbox.GetType()
$Mailbox[0].GetType() # gets the type of the first object in $Mailbox
To fix your code, you need to loop over what's been returned by Get-Mailbox. Try this:
$Mailboxes = Get-Mailbox -ResultSize Unlimited | where { $_.RecipientTypeDetails.tostring() -eq "SharedMailbox" }
$Mailboxes | ForEach-Object { Get-MailboxStatistics -Identity $_ }
The ForEach-Object cmdlet loops over an array or a list and works on each item individually.
Your first example works so far because Powershell has been made smarter about piped data a few versions ago (See paragraph about 'Member Enumeration'). It's actually ForEach-ing over the passed in data.
Follow up links:
The $_ variable in powershell
Powershell is an object oriented language
Sorry to have to say this, but you're completely wrong. Pipelines and variables are two entirely different things.
The pipe (|) connects the output of one cmdlet to the input of another cmdlet. List output is processed one item at a time, i.e. the second cmdlet receives each list item separately.
If you collect a list of items in a variable ($mailbox) and call a cmdlet with that variable you're passing the list object instead of individual list items. That only works if the cmdlet parameter accepts array input.
The pipe operator | i used to flow the output of one command into the input of another command.
The dollar symbolc, $ is used to denote that the name following it is a variable, and has nothing to do with piping data between cmdlets. The where cmdlet create a $_ variable for use within its expression.