Get AD user properties from Active Directory - powershell

I am trying to filter the values of a property in Active Directory.
I tried:
Get-ADUser -filter * -Properties physicalDeliveryOfficeName | Where-Object (($_.physicalDeliveryOfficeName -like "NICE")) | Select-Object physicalDeliveryOfficeName, name
Get-ADUser -filter * -Properties physicalDeliveryOfficeName | Select-Object physicalDeliveryOfficeName, name | Where-Object (($_.physicalDeliveryOfficeName -like "NICE"))
I did not get any errors, but no results either.
I searched all users with physicaldeliverofficename is (myvalue). I would like to display name and office.

You have a syntax problem:
The Where-Object's (positionally implied) -FilterScript parameter expects a script block argument - { ... } - not a parenthesized expression ((...)).[1]
Therefore:
# Note the { ... } around the expression passed to Where-Object
Get-ADUser -Filter * -Properties physicalDeliveryOfficeName |
Where-Object { $_.physicalDeliveryOfficeName -eq "NICE" } # | ...
Note: Since "NICE" is a literal string rather than a wildcard pattern, I've used the -eq instead of the -like operator. If you doe need to find "NICE" as a substring, use something like -like "*NICE*" or, for case-sensitive matching, -clike "*NICE*", as Mathias R. Jessen suggests.
Note that you may alternatively use simplified syntax, which obviates the need for a script block and allows use of individual parameters (also note the absence of $_., which is implied):
Get-ADUser -Filter * -Properties physicalDeliveryOfficeName |
Where-Object physicalDeliveryOfficeName -eq "NICE" # | ...
Taking a step back:
Santiago Squarzon suggests performing the filtering at the source, by using Get-ADUser's -Filter or -LDAPFilter parameter, which is much more efficient; e.g.:
Get-ADUser -Filter 'physicalDeliveryOfficeName -eq "NICE"'
As an aside: There are many examples out there that use script-block syntax with -Filter (-Filter { ... }), but the -Filter parameter accepts a string and that string, even though it supports PowerShell-like syntax, is interpreted by the AD provider, so it's better to pass a string to begin with - see this answer for more information.
[1] If you use (...), the expression's value gets bound to the -Property parameter instead, and is therefore interpreted as a property name whose value - assuming such a property even exists - is interpreted as a Boolean that determines whether the input object at hand should be filtered in or not. If the expression doesn't evaluate to the name of a property that exists on an input object, $false is implied, and the input object is filtered out. In your case, this predictably resulted in no objects being filtered in and therefore no output.

The Select-Object cmdlet is used to select only the columns you want from a larger object or list.
For instance:
C:\git\Core> gsv Spooler | fl
Name : Spooler
DisplayName : Print Spooler
Status : Running
DependentServices : {Fax}
ServicesDependedOn : {RPCSS, http}
CanPauseAndContinue : False
CanShutdown : False
CanStop : True
ServiceType : Win32OwnProcess, InteractiveProcess
Get-Service returns Service Objects which have a lot of properties. If I only want certain ones, I'd use it like so:
C:\git\Core> gsv Spooler | Select Name,Status
Name : Spooler
Status : Running
You're using the cmdlet and probably discarding the columns which have the values you need. Run your one-liner again and remove the Select-Object cmdlet to see all of the columns availble, till you find the one that pertains to the Office.

Related

Powershell - add extra column called RoleUserA* inside CSV file

I want to add an extra, empty column called RoleUserA* to a CSV file, as attempted below. RoleUserA* is not an AD Attribute.
Get-ADUser -Filter {Enabled -eq $true} -Properties * |
Select givenName, sn, displayname, samaccountname, RoleUserA*, title |
Export-Csv -Path "c:\tmp\users.csv" -NoTypeInformation -Encoding UTF8
Desired output:
givenName,sn,displayname,samaccountname,RoleUserA*,title
user01,sn1,User01 SN,user01,,specialist
user02,sn2,User02 SN,user02,,specialist
However, RoleUserA* isn't being added as a column.
The (positionally implied) -Property parameter of the Select-Object cmdlet (whose built-in alias is select) interprets its arguments as wildcard expressions.
Therefore, RoleUserA* looks for existing properties on the input objects whose name starts with RoleUserA - and simply adds none to the output objects if no existing property matches; a simple example:
# Because 'bar*' matches no existing properties, it is *ignored*.
PS> [pscustomobject] #{ foo = 1 } | Select-Object foo, bar*
foo
---
1
While escaping * as `* ('RoleUserA`*') so that it is used literally, as a (non-existent, in this case) property name, should be possible, it unfortunately isn't as of PowerShell 7.2.2,[1] as Mathias R. Jessen points out, and he also points to the solution that does work:
Use a calculated property, whose Name entry (shortened to n below) is always treated literally; using { $null } as the Expression (e) entry script block creates the property with value $null:[1]
Get-ADUser -Filter 'Enabled -eq $true' -Properties * |
Select givenName, sn, displayname, samaccountname, #{n='RoleUserA*';e={ $null }}, title |
Export-Csv -Path "c:\tmp\users.csv" -NoTypeInformation -Encoding UTF8
Note: In scripts used long-term, it's better to spell out the entry keys of the calculated property in full, i.e.:
#{ Name = 'RoleUserA*'; Expression = { $null } }
[1] This should be considered a bug, or at least needs documenting - see GitHub issue #17068
[2] Using just { } - i.e. an empty script block, almost works the same, but it actually creates an "Automation Null" value (the System.Management.Automation.Internal.AutomationNull.Value singleton), which in expressions behaves the same as $null, but acts differently in a pipeline - see this answer for more information.
That said, in the context of converting to CSV this difference won't matter.

Powershell - Issues to test a value in a list

I have a 2 variables:
$Listgivenname = Get-aduser -filter * | Select-Object givenname
$Usergivenname = "Tom"
Actually, Tom is in $Listgivenname, so I decided to make an if statement like:
If($Listgivenname -contains $Usergivenname) {Write-Host "blablabla"}
But actually this command outputs false, even with $Usergivenname -in $Listgivenname.
The weird part comes when I choose to manually write the list in $Listgivenname, like "Tom", "Jerry". It actually works, so I believe there's a thing that I need to understand about a command witch I assign to a variable.
Get-aduser -filter * | Select-Object givenname doesn't seem to work like a manually written list.
I don't ask for solutions but I'm trying to understand why this happens.
try add -ExpandProperty (to get the values of each object):
$Listgivenname = Get-aduser -filter * | Select-Object -ExpandProperty givenname

Filter Records on Multiple Values

I have to get a list of AD users where the Manager field contains one of the following names on my FilterValues list.
I see that someone recommended to use the Where-Object, however when I run the script nothing gets returned. I have tried many approaches but I can't seem to find where I am going wrong. Any advise will be greatly appreciated, TIA.
$FilterValues = $('Williams', 'Smith', 'Johnson')
Get-ADUser -Filter "extensionAttribute10 -like '1.0'" -Properties *
| Where-Object {$_.Manager -contains $FilterValues}
| Format-Table Name, Manager
*** If I run the script without the Where-Object portion I get values back.
The -contains is for testing values in an array, unlike the string .Contains() method. In this case, I'd suggest using the regex -match operator like this:
# an array of manager names
$FilterValues = 'Williams', 'Smith', 'Johnson'
# create a regular expression of the manager names by combining them with the regex OR symbol '|'
# the [regex]::Escape() is there to make sure characters that have special meaning in regex are escaped.
$regexManagers = ($FilterValues | ForEach-Object { [regex]::Escape($_) }) -join '|'
Get-ADUser -Filter "extensionAttribute10 -like '1.0'" -Properties Name, Manager |
Where-Object {$_.Manager -match $regexManagers } |
Format-Table
P.S. It is always a bad idea to search for ALL properties with -Properties * when you only want two properties returned.

PowerShell to check for missing ShadowCopies

Trying to check for SMB shares (DC's and member servers) that are missing Shadow Copies. Can't get the PowerShell filter just right.
This is the closest I've come. I'm still getting SYSVOL shares in the list.
Get-SmbShare -Special $False | FL Name,Path,ShadowCopy | Where-Object {(-not $_.ShadowCopy) -and ($_.Path -notlike "*sysvol*")}
I'm expecting the output to be like:
Name : test
Path : C:\test
ShadowCopy : False
You should never use the formatting cmdlet's in the middle of a pipeline. By using fl, you are essentially creating an array of strings. The where clause operates on those strings.
Most likely, you meant to do something like this
Get-SmbShare |
Where-Object {!($_.ShadowCopy) -and ($_.Path -notlike '*sysvol*')} |
fl name, path, shadowcopy

Passing string in Get-ADUser filter parameter causes error - property not found in pscustomobject

I'm trying to create a new Active Directory user, but first I verify that the user doesn't exist already with Get-ADUser.
I import the user data from our HR department and build custom properties:
$newUsers = Import-Csv $csvFile |
Select-Object -Property #{n='EmpNum';e={$_.'Employee Number'}},
#{n='UPN';e={$_.'Email Address'}},
#{n='Alias';e={$_.'Email Address'.Split("#")[0]}} #### etc
When I loop through the objects from the CSV file, I use the UPN property to search for the user in Active Directory:
foreach ($newUser in $newUsers) {
$exists = Get-ADUser -Filter {UserPrincipalName -eq $newUser.UPN} -Properties * -Server $adServer -Credential $adCred
...
}
The filter causes an error:
Get-ADUser : Property: 'UPN' not found in object of type:
'System.Management.Automation.PSCustomObject'. At
C:\Users\bphillips.NEWHOPEOFIN\Dropbox\Powershell\NewHire\AddNewDSP.ps1:50
char:15
+ $exists = Get-ADUser -Filter {UserPrincipalName -eq $newUser.UPN} -Propertie ...
I've tried doing this: -Filter {UserPrincipalName -eq $("$newUser.UPN") but that doesn't help; I get another error
Get-ADUser : Cannot process argument because the value of argument
"path" is not valid. Change the value of the "path" argument and run
the operation again. At
C:\Users\bphillips.NEWHOPEOFIN\Dropbox\Powershell\NewHire\AddNewDSP.ps1:50
char:15
+ $exists = Get-ADUser -Filter {UserPrincipalName -eq $("$newUser.UPN")} -Prop ...
$newUser is a string, so I don't understand why it causes a problem. Hard-coding a UserPrincipalName like, "test#ourcompany.com" works, but the $newUser.UPN won't work.**
PS C:\> $newUser.UPN.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
and
PS C:\> $newUser.UPN | gm
TypeName: System.String
$newUser.UPN contains a valid string value
PS C:\> $newUser.UPN
ypope#ourcompany.net
What do I have to do to get $newUser.UPN to be recognized as a string for the filter parameter? What's going on that I don't understand?
The BNF for filter query strings does not allow expressions as the second operand in a comparison, only values (emphasis mine):
Syntax:
The following syntax uses Backus-Naur form to show how to use the PowerShell Expression Language for this parameter.
<filter> ::= "{" <FilterComponentList> "}"
<FilterComponentList> ::= <FilterComponent> | <FilterComponent> <JoinOperator> <FilterComponent> | <NotOperator> <FilterComponent>
<FilterComponent> ::= <attr> <FilterOperator> <value> | "(" <FilterComponent> ")"
<FilterOperator> ::= "-eq" | "-le" | "-ge" | "-ne" | "-lt" | "-gt"| "-approx" | "-bor" | "-band" | "-recursivematch" | "-like" | "-notlike"
<JoinOperator> ::= "-and" | "-or"
<NotOperator> ::= "-not"
<attr> ::= <PropertyName> | <LDAPDisplayName of the attribute>
<value>::= <compare this value with an <attr> by using the specified <FilterOperator>>
Put the value of the property you want to compare against in a variable and use that variable in the comparison. You may also want to define the filter as an actual string, if only for clarity (despite what it looks like the filter is not a scriptblock).
$upn = $newUser.UPN
$exists = Get-ADUser -Filter "UserPrincipalName -eq '$upn'" ...
Expressions can inside the filter block of a Get-ADUser but they need to be properly wrapped with quotes.
Get-ADUser -Filter "UserPrincipalName -eq '$($newUser.UPN)'"
Never use a script block ({ ... }) as the -Filter argument - the -Filter parameter's type is [string] - construct your filter as a string.
BenH's answer shows how to do that.
While seemingly convenient, using a script block only works in very limited scenarios and causes confusion when it doesn't work - such as when involving property access, as in this case.
For more information, see this answer of mine.